C# → Connector/NET → TCP → MySQL Server → 儲存引擎 → 磁碟,任何一環編碼不一致即產生亂碼。網上流傳「UTF-8 萬能論」實為害,實測 UTF-8 在舊版 Connector/NET、MySQL 5.6-、特定 sql_mode 下頻發問題。本文提供全鏈路 GBK/utf8mb4 雙方案,含驗證腳本與避坑清單。
| 環節 | 配置點 | GBK 方案 | utf8mb4 方案(推薦新專案) |
|---|---|---|---|
| 1. C# 字串 | .NET 內部 Unicode | 無需配置 | 無需配置 |
| 2. Connector/NET | 連接字串 CharSet | CharSet=gbk | CharSet=utf8mb4 |
| 3. MySQL 連線層 | session 變數 | 自動執行 SET NAMES gbk | 自動執行 SET NAMES utf8mb4 |
| 4. 資料庫/表/欄位 | CHARSET/COLLATE | CHARSET=gbk COLLATE=gbk_chinese_ci | CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci |
| 5. 作業系統/終端 | 控制台編碼 | chcp 936 | chcp 65001 |
[mysqld]
character_set_server=gbk
collation_server=gbk_chinese_ci
skip_character_set_client_handshake=1 # 強制忽略客戶端請求,統一 Server 端
CREATE DATABASE `legacy_app` DEFAULT CHARACTER SET gbk COLLATE gbk_chinese_ci;
CREATE TABLE `users` (
`name` VARCHAR(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=gbk COLLATE=gbk_chinese_ci;
Server=127.0.0.1;Port=3306;Database=legacy_app;Uid=app;Pwd=pass;
CharSet=gbk; // 關鍵
ConnectionReset=true;
[mysqld]
character_set_server=utf8mb4
collation_server=utf8mb4_general_ci
# 不開 skip_character_set_client_handshake,允許客戶端協商
CREATE DATABASE `modern_app` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE TABLE `users` (
`name` VARCHAR(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
Server=127.0.0.1;Port=3306;Database=modern_app;Uid=app;Pwd=pass;
CharSet=utf8mb4; // 關鍵
ConnectionReset=true;
using (var conn = new MySqlConnection(connStr))
{
conn.Open();
// 1. 連線層協商結果
using (var cmd = new MySqlCommand("SHOW VARIABLES LIKE 'character_set_%';", conn))
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
Console.WriteLine($"{reader.GetString(0)} = {reader.GetString(1)}");
}
// 2. 實測寫入讀取
conn.Execute("DROP TABLE IF EXISTS encoding_test");
conn.Execute("CREATE TABLE encoding_test (txt VARCHAR(100)) CHARSET=utf8mb4");
conn.Execute("INSERT INTO encoding_test VALUES ('中文測試😀Emoji')");
var result = conn.QuerySingle("SELECT txt FROM encoding_test");
Console.WriteLine($"寫入讀取: {result}");
Console.WriteLine($"十六進制: {BitConverter.ToString(Encoding.UTF8.GetBytes(result))}");
}
預期輸出(utf8mb4 方案):
character_set_client = utf8mb4
character_set_connection = utf8mb4
character_set_database = utf8mb4
character_set_results = utf8mb4
character_set_server = utf8mb4
寫入讀取: 中文測試😀Emoji
十六進制: E4-B8-AD-E6-96-87-E6-B8-AC-E8-A9-A6-F0-9F-98-80-45-6D-6F-6A-69
| 誤區 | 真相 | 正確做法 |
|---|---|---|
| 「資料庫設 UTF-8 就夠了」 | 連線層未協商、Connector/NET CharSet 未設 → 亂碼 | 五環全鎖定 |
| 「UTF-8 不支援 Emoji」 | MySQL utf8 實為 utf8mb3,需用 utf8mb4 | 全鏈路 utf8mb4 |
| 「GBK 較省空間」 | 中文 2 bytes vs utf8mb4 3-4 bytes,但 GBK 不支援 Emoji/生僻字/多語言 | 新專案 utf8mb4,舊系統 GBK 相容 |
| 「Connector/NET 自動偵測」 | 不會,必須顯式 CharSet | 連接字串強制指定 |
| 「SET NAMES 放在第一條 SQL」 | Connector/NET 連線建立時自動執行,手動執行會被覆蓋 | 用 CharSet 參數 |
ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;(鎖表,維護窗口執行)CharSet=utf8mb4,灰度發布驗證C# MySQL 中文亂碼不是玄學,是工程。建立「編碼鏈路五環鎖定」標準,將 CharSet、my.cnf、建表 DDL、驗證腳本納入專案模板、Code Review 清單、CI/CD 檢查。新專案直上 utf8mb4 全鏈路,舊系統 GBK 相容到退役。編碼治理,一勞永逸。