亂碼本質是編碼/解碼不一致。掌握全鏈路轉換流程,對症下藥,徹底根治。
用戶輸入 → Terminal編碼(UTF-8) → 二進制流
→ MySQL Client → Server解碼(character_set_client)
→ 判斷client_charset vs table_charset
→ 不一致則轉換(client_charset → table_charset)
→ 存入文件(table_charset)
關鍵點:
character_set_clientcharacter_set_table / character_set_column文件讀取(table_charset) → 解碼
→ 轉換為 character_set_client 編碼
→ 二進制流傳輸
→ Client 按 Terminal 編碼展示
-- my.cnf
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
init_connect = 'SET NAMES utf8mb4'
skip-character-set-client-handshake -- 強制忽略客戶端請求編碼
-- 建表顯式指定
CREATE TABLE t (c VARCHAR(100)) CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
useUnicode=true&characterEncoding=utf8mb4&connectionCollation=utf8mb4_unicode_cicharset='utf8mb4' (PyMySQL) / connect(charset='utf8mb4') (mysqlclient)mysqli_set_charset($link, 'utf8mb4') / PDO DSN charset=utf8mb4dsn "user:pass@tcp(127.0.0.1:3306)/db?charset=utf8mb4,utf8"| 現象 | 原因 | 修復 |
|---|---|---|
| 中文顯示 ??? 或 | 客戶端編碼 ≠ 服務端編碼 | 統一 SET NAMES utf8mb4 / 修正連接參數 |
| Emoji 存入變 ???? | 表/列為 utf8(3字節),需 utf8mb4(4字節) | ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4 |
| 歷史 latin1 表顯示亂碼 | 雙重編碼:latin1 存實際 utf8 字節 | ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4 |
| 備份恢復後亂碼 | mysqldump 無 --default-character-set | mysqldump --default-character-set=utf8mb4 |
| 終端顯示正常,Web 亂碼 | HTML meta charset / HTTP Header 缺失 | <meta charset="utf-8"> + Response Header |
-- 查看當前會話編碼
SHOW VARIABLES LIKE 'character_set_%';
SHOW VARIABLES LIKE 'collation_%';
-- 查看表/列編碼
SHOW CREATE TABLE t\G
SELECT CHARSET(c), COLLATION(c) FROM t LIMIT 1;
-- 測試寫讀
INSERT INTO t VALUES ('測試😀');
SELECT c, HEX(c), CHAR_LENGTH(c), LENGTH(c) FROM t;
一句話總結:應用、連接、庫、表、列、終端六端統一為
utf8mb4,啟用skip-character-set-client-handshake強制服務端編碼,徹底消滅亂碼。