MySQL 字符集體系龐雜:四級層級(服務/庫/表/列)、六個連接變量、校對規則、文件系統編碼。本文從官方文檔與實測出發,梳理完整知識圖譜,給出亂碼根因分析與生產級配置範本。
| 層級 | 設置方式 | 繼承規則 | 查看命令 |
|---|---|---|---|
| 服務級 | character-set-server=utf8mb4 (my.cnf) | 默認根源 | SHOW VARIABLES LIKE 'character_set_server'; |
| 庫級 | CREATE DATABASE db CHARACTER SET utf8mb4; | 無指定則繼承服務級 | SHOW CREATE DATABASE db; |
| 表級 | CREATE TABLE t (...) CHARACTER SET utf8mb4; | 無指定則繼承庫級 | SHOW CREATE TABLE t; |
| 列級 | col VARCHAR(20) CHARACTER SET utf8mb4 | 無指定則繼承表級 | SHOW FULL COLUMNS FROM t; |
原則:**顯式指定 > 隱式繼承**。生產環境建議:服務級 utf8mb4,庫/表/列**不再單獨指定**,統一繼承,消除「套娃」亂碼源頭。
客戶端發送 SQL (編碼 A: character_set_client)
↓ 服務端按 A 解碼,轉換為編碼 B
character_set_connection (編碼 B)
↓ 執行前轉換為列編碼 C 存儲
列編碼 (character_set_database/表/列定義)
↓ 讀取時從列編碼 C 轉換為編碼 D
character_set_results (編碼 D)
↓ 客戶端按 D 解碼顯示
| 變量 | 角色 | 建議值 | 典型坑 |
|---|---|---|---|
| character_set_client | 客戶端發送 SQL 的編碼 | 與應用編碼一致 (utf8mb4) | 應用實為 utf8 卻設 latin1 → 存入亂碼 |
| character_set_connection | 連接層統一工作編碼 | 同 client | 與 client 不一致 → 隱式轉換損失精度 |
| character_set_results | 結果集返回編碼 | 同 client | 不一致 → 終端顯示亂碼 |
| character_set_database | 當前庫默認編碼 (只讀) | 建庫時指定 | 跨庫查詢隱式轉換導致索引失效 |
| character_set_server | 服務端默認編碼 | my.cnf 設 utf8mb4 | 未改動保持 latin1 → 全鏈路預設 latin1 |
| character_set_filesystem | 文件名解析編碼 (LOAD/INTO OUTFILE) | binary (不轉換) | 改為 utf8 導致中文文件名雙重編碼亂碼 |
SET NAMES utf8mb4; -- 等價於: SET character_set_client = utf8mb4; SET character_set_connection = utf8mb4; SET character_set_results = utf8mb4;
characterEncoding=utf8mb4)。utf8mb4_0900_ai_ci,SET NAMES utf8mb4 自動帶上校對規則。CREATE TABLE test (
gbk_col VARCHAR(2) CHARACTER SET gbk,
utf8_col VARCHAR(2) CHARACTER SET utf8,
latin_col VARCHAR(6) CHARACTER SET latin1
) CHARSET=latin1;
SET NAMES utf8; -- 客戶端聲明 utf8
INSERT INTO test VALUES ('中文', '中文', '中文');
-- Warning: Incorrect string value: '\xE4\xB8\xAD\xE6\x96\x87' for column 'latin_col'
gbk_col:UTF-8 → gbk 轉換成功,存 D6D0 CEC4,讀出正常。utf8_col:編碼一致,存 E4B8 ADE6 9687,讀出正常。latin_col:UTF-8 字符超出 latin1 範圍,轉換失敗存 3F3F (??),讀出永久亂碼。_latin1'中文' Introducer 告知服務端字面值編碼為 latin1,避免 client→connection 轉換。
INSERT INTO test VALUES ('中文', '中文', _latin1'中文');
BINARY 強制跳過 results 轉換,直接取原始字節。
SELECT gbk_col, utf8_col, BINARY latin_col FROM test;
[client] default-character-set = utf8mb4 [mysql] default-character-set = utf8mb4 [mysqld] character-set-server = utf8mb4 collation-server = utf8mb4_0900_ai_ci init_connect = 'SET NAMES utf8mb4' # 非 SUPER 用戶自動執行 skip-character-set-client-handshake # 強制忽略客戶端聲明,以服務端為準 # 可選:禁用 latin1 相關編碼,防止誤用 # character-set-filesystem = binary
utf8 實為 utf8mb3 (最多 3 字節),**無法存 Emoji/生僻字**,**必須用 utf8mb4**。?characterEncoding=utf8mb4&useUnicode=true,Python charset='utf8mb4',Go charset=utf8mb4。SHOW CREATE TABLE 確認每列編碼,SELECT HEX(col) 確認存儲字節。_ci 不區分大小寫/重音,_cs 區分,_bin 按字節碼,業務需區分大小寫的列必須指定 COLLATE utf8mb4_bin。MySQL 字符集亂碼本質是**編碼鏈路不一致導致的多次有損轉換**。解決之道只有四字:**統一 utf8mb4**——從服務端、連接端、庫表列、應用驅動、遷移腳本,端到端同一編碼。掌握六大連接變量流向、Introducer/BINARY 急救技巧、生產級 my.cnf 範本,字符集問題將不再是噩夢。