中文亂碼是 MySQL 開發者的「成人禮」。根因在於 **Client → Connection → Server → Database → Table → Column** 六層字符集不一致。MySQL 預設 latin1,不支援中文。本文從配置檔、連接參數、建表語句、資料修正四個層面,給出徹底解決方案。
Client (應用/終端)
→ character_set_client
Connection (JDBC/ODBC/CLI)
→ character_set_connection
Server (mysqld)
→ character_set_server / character_set_database
Database
→ DEFAULT CHARACTER SET
Table
→ DEFAULT CHARACTER SET
Column
→ CHARACTER SET
**核心原則**:六層**全部統一為 utf8mb4**(支援 Emoji、完整 Unicode),而非舊版 utf8(僅 3 字節,缺補充平面字符)。
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
init_connect = 'SET NAMES utf8mb4'
skip-character-set-client-handshake # 強制忽略客戶端請求的字符集
修改後 **重啟 mysqld** 生效。適用於全新部署或可控制所有客戶端的環境。
不依賴 Server 配置,由應用層強制指定,最穩健。
# 5.1 版本
jdbc:mysql://host:3306/db?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
# 8.0+ 版本(自動識別 utf8mb4)
jdbc:mysql://host:3306/db?characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai
conn = pymysql.connect(host='...', user='...', password='...', db='...', charset='utf8mb4')
# 或
conn = MySQLdb.connect(..., charset='utf8mb4')
dsn := "user:pass@tcp(host:3306)/db?charset=utf8mb4&parseTime=True&loc=Local"
// PDO
$pdo = new PDO("mysql:host=...;dbname=...;charset=utf8mb4", $user, $pass);
// mysqli
$mysqli->set_charset("utf8mb4");
-- 資料庫層級
CREATE DATABASE mydb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- 表層級
CREATE TABLE t_user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL COMMENT '姓名',
bio TEXT COMMENT '簡介'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- 欄位層級(覆蓋表預設)
ALTER TABLE t_user MODIFY COLUMN bio TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
SHOW CREATE TABLE t_user、SELECT HEX(col) FROM t_user LIMIT 1 分析位元組。
ALTER TABLE t_user
CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CONVERT TO 會**轉換資料位元組**,而 DEFAULT CHARSET 僅改元資料。
-- 1. 連線層面
SHOW VARIABLES LIKE 'character_set_%';
-- 期望:client/connection/results/database/server 皆為 utf8mb4
-- 2. 表/欄位層面
SHOW CREATE TABLE t_user;
-- 期望:DEFAULT CHARSET=utf8mb4,欄位無額外 CHARSET 或同為 utf8mb4
-- 3. 實際寫讀測試
INSERT INTO t_user (name) VALUES ('測試😀');
SELECT name, HEX(name) FROM t_user WHERE id=LAST_INSERT_ID();
-- HEX 應為 E6B58BE8AF95F09F9880 (UTF-8MB4)
| 誤區 | 正確認知 |
|---|---|
| 用 utf8 而非 utf8mb4 | utf8 = utf8mb3,不支援 Emoji、生僻字,必須用 utf8mb4 |
| 只改 my.cnf 不重啟 | 需重啟 mysqld 才生效 |
| 只改表 DEFAULT CHARSET | 不轉換既有資料,必須用 CONVERT TO |
| JDBC 用 characterEncoding=utf8 | 驅動會映射為 utf8mb3,建議 utf8mb4 或依賴 8.0+ 自動協商 |
**「全鏈路 utf8mb4」** 是解決中文亂碼的唯一正解。新專案:my.cnf + 連接參數 + 建表語句三重保險。存量專案:CONVERT TO CHARACTER SET utf8mb4 一鍵轉換(備份先行)。將字符集治理納入上線檢查清單,杜絕亂碼根源。