**傳輸不轉碼、存儲不轉碼、只有「輸入→連接→存儲」與「存儲→結果集→顯示」兩次轉碼會產生亂碼。** 只要全鏈路編碼一致,亂碼必不存在。
【寫入流程】 Client(character_set_client) → 轉換 → connection(character_set_connection) -- 第1次轉換 → 傳輸(不轉碼) → Server: 按列字符集再轉換 -- 第2次轉換 → 存儲(不轉碼) 【讀取流程】 存儲 → Server: 按 character_set_results 轉換 -- 逆向第2次 → 傳輸(不轉碼) → Client: 按 character_set_client 轉換 -- 逆向第1次 → 顯示(這裡看到亂碼)
| 變量 | 含義 | 建議值 |
|---|---|---|
character_set_client | 客戶端發送編碼 | utf8mb4 |
character_set_connection | 連接層處理編碼 | utf8mb4 |
character_set_results | 結果集返回編碼 | utf8mb4 |
character_set_database | 庫默認字符集 | utf8mb4 |
character_set_server | 服務器默認字符集 | utf8mb4 |
character_set_system | 系統標識符編碼(固定 utf8) | 不可改 |
SET NAMES utf8mb4; -- 等價於同時設置: SET character_set_client = utf8mb4; SET character_set_connection = utf8mb4; SET character_set_results = utf8mb4;
最佳實踐: 連接建立後**第一條語句**執行此命令,或在連接池/驅動配置中指定 characterEncoding=utf8mb4。
環境:Client/Connection/Results = latin1,Database/Server = utf8。插入「你」(UTF-8: E4 BD A0)。
原因:客戶端編輯器是 UTF-8,latin1 單字節直通,字節內容不變(E4 BD A0),MySQL 以為是 3 個 latin1 字符。
原因:Server 將列內容(實際存儲的 C3 A4 C2 BD C2 A0,即 UTF-8 字節被 latin1 解讀後的字符)按 latin1 輸出,恰好還原為 E4 BD A0。
原因:終端 是 UTF-8,收到 E4 BD A0 直接渲染「你」。**這是「雙重錯誤抵消」的偽正常!** 數據庫實際存儲的是亂碼字符(6 字節),非真正 UTF-8(3 字節)。
原因:連接/庫編碼一致,無轉換,直接輸出存儲的 C3 A4 C2 BD C2 A0,終端按 UTF-8 解析為 2 個雜字符。**這才是真實存儲狀態的忠實反映。**
SET NAMES utf8mb4characterEncoding=utf8mb4、PHP PDO charset=utf8mb4、Python charset='utf8mb4'CREATE TABLE ... CHARSET=utf8mb4 COLLATE=utf8mb4_general_cipt-online-schema-change 無鎖轉碼SHOW VARIABLES LIKE 'character_set_%'; 確認 6 大變量SELECT HEX(col), col FROM tbl LIMIT 1; 看實際存儲字節SET NAMES 或驅動配置MySQL 亂碼本質是**編碼鏈路不一致導致的錯誤轉換**。**統一 utf8mb4 + 連接初始化 SET NAMES + 驅動顯式配置 + 顯式建表字符集**,從架構層面根治,不再靠運氣。