MySQL 亂碼的根源只有一個:編碼與解碼使用的字元集不一致。資料在「寫入 → 儲存 → 讀取 → 傳輸 → 顯示」每個環節都有字元集設定,任一環節錯位即產生亂碼。本文建立「全鏈路 UTF-8」體系,兼顧舊版 GBK 遷移場景。
| 層級 | 系統變數 | 說明 | 建議值 |
|---|---|---|---|
| 伺服器 | character_set_server | 預設資料庫/資料表字元集 | utf8mb4 |
| 資料庫 | character_set_database | 資料庫預設字元集 (建表時繼承) | utf8mb4 |
| 資料表 | DEFAULT CHARSET | 表層級預設,欄位可覆蓋 | utf8mb4 |
| 欄位 | CHARACTER SET | 實際儲存編碼,支援 utf8mb4/gbk 等 | utf8mb4 |
| 連線 | character_set_client/connection/results | 客戶端↔伺服器傳輸編碼「三兄弟」 | utf8mb4 |
| 排序規則 | collation_* | 比較/排序規則,如 utf8mb4_general_ci (不分大小寫) | utf8mb4_0900_ai_ci (8.0+ 推薦) |
關鍵區別: utf8 (MySQL 別名) 僅支援 3 位元組字符 (BMP 平面),無法儲存 Emoji、生僻字;utf8mb4 完整支援 4 位元組 Unicode,**新專案務必使用 utf8mb4**。
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_0900_ai_ci
init_connect = 'SET NAMES utf8mb4' -- 非 super 權限連線自動執行
skip-character-set-client-handshake -- 強制忽略客戶端請求的字元集
修改後 重啟 MySQL 服務 生效。查看驗證:SHOW VARIABLES LIKE 'character_set%'; SHOW VARIABLES LIKE 'collation%';
-- 建庫
CREATE DATABASE mydb DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_0900_ai_ci;
-- 建表 (欄位級別顯式指定最保險)
CREATE TABLE users (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
bio TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ENGINE=InnoDB;
-- 既有表轉換 (需鎖表,大表請用 pt-online-schema-change)
ALTER TABLE old_table CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
| 語言/驅動 | 正確配置方式 |
|---|---|
| PHP (PDO) | new PDO("mysql:host=...;dbname=...;charset=utf8mb4", $user, $pass) (PHP 5.3.6+) |
| PHP (mysqli) | $mysqli->set_charset('utf8mb4'); |
| Python (PyMySQL/mysqlclient) | connect(..., charset='utf8mb4') |
| Java (JDBC) | 連線字串 ?characterEncoding=utf8mb4&connectionCollation=utf8mb4_0900_ai_ci (Connector/J 5.1.47+) |
| C# (MySql.Data) | 連線字串 Charset=utf8mb4; |
| Node.js (mysql2) | createConnection({..., charset: 'utf8mb4'}) |
| Go (go-sql-driver/mysql) | DSN charset=utf8mb4,utf8 (雙寫相容) |
原理: 驅動程式在建立連線時會發送 SET NAMES utf8mb4,同步設定 character_set_client、connection、results 三大變數,確保傳輸編碼一致。
<meta charset="utf-8"> (HTML5 標準寫法,瀏覽器視為 utf-8 即涵蓋 utf8mb4)。Content-Type: text/html; charset=utf-8。<form accept-charset="utf-8">。encodeURIComponent(),後端確保 URI 解碼使用 UTF-8。若專案已大量使用 GBK (如 CHARSET=gbk、COLLATE=gbk_chinese_ci),遷移步驟:
SELECT TABLE_NAME, TABLE_COLLATION FROM information_schema.TABLES WHERE TABLE_SCHEMA='mydb';mysqldump --default-character-set=gbk --single-transaction mydb > backup_gbk.sql。iconv -f gbk -t utf-8 backup_gbk.sql > backup_utf8.sql,並將 SQL 內 CHARSET=gbk 批次替換為 CHARSET=utf8mb4,COLLATE=gbk_chinese_ci 替換為 COLLATE=utf8mb4_0900_ai_ci。SET NAMES utf8 就夠了。utf8mb4,且伺服端、資料表、欄位全鏈路一致。
utf8mb4_general_ci 就以為搞定。connection 變數,斷線即失效,必須配置驅動程式連線字串。
VARCHAR(100) 能存 100 個中文。VARCHAR(N) 的 N 是字符數 (非位元組),但 utf8mb4 單字符最多 4 位元組,InnoDB 單列索引長度限制 767 字節 (或 3072 字節開啟 innodb_large_prefix),VARCHAR(255) 索引可能超限。
-- 1. 檢查實際儲存的十六進位
SELECT HEX(column_name) FROM table WHERE id=1;
-- 2. 檢查連線當前字元集
SHOW VARIABLES LIKE 'character_set_%';
SHOW VARIABLES LIKE 'collation_%';
-- 3. 檢查欄位定義
SHOW FULL COLUMNS FROM table_name;
-- 4. 強制指定查詢編碼 (應急)
SELECT CONVERT(CAST(CONVERT(column_name USING latin1) AS BINARY) USING utf8mb4) FROM table;
解決 MySQL 亂碼不靠「試錯」,而靠建立全鏈路 utf8mb4 體系:伺服器預設 → 資料庫/表/欄位顯式指定 → 驅動程式連線字串強制指定 → 前端頁面宣告。舊 GBK 專案遷移需謹慎規劃,備份優先、分階段驗證。記住:**字元集不一致是唯一禍根,統一才是唯一出路。**