中文亂碼本質是「編碼鏈路不一致」:客戶端編碼 ≠ 連接編碼 ≠ 庫表編碼 ≠ 存儲編碼。本文從連接建立、庫表創建、寫入讀取三個階段,給出標準化修復動作與驗證清單,徹底終結「問號菱形、亂碼、截斷」三大癥狀。
[應用代碼字符集] → [JDBC/驅動 connectionEncoding] → [MySQL 連接 character_set_client/connection/results]
↓
[庫默認字符集] → [表默認字符集] → [列字符集]
↓
[存儲引擎頁面編碼] → [磁盤字節序列]
-- 方式 1:連接後立即執行(所有客戶端通用)
SET NAMES utf8mb4; -- 等價於 SET character_set_client/connection/results = utf8mb4
-- 方式 2:JDBC URL 參數(Java 應用推薦)
jdbc:mysql://host:3306/db?useUnicode=true&characterEncoding=utf8mb4&zeroDateTimeBehavior=convertToNull
-- 方式 3:my.cnf 全局默認(需重啟)
[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' -- 非 super 用戶自動執行
驗證: SHOW VARIABLES LIKE 'character_set_%'; 客戶端/連接/結果均為 utf8mb4。
-- 庫級
CREATE DATABASE mydb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- 或修改現有庫
ALTER DATABASE mydb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- 表級
CREATE TABLE t1 (
id INT PRIMARY KEY,
name VARCHAR(50)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- 或修改現有表(僅影響新增列)
ALTER TABLE t1 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- 列級(徹底修復現有列)
ALTER TABLE t1 MODIFY name VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- 或批量生成
SELECT CONCAT('ALTER TABLE ', TABLE_NAME, ' MODIFY ', COLUMN_NAME, ' ', COLUMN_TYPE, ' CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;')
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'mydb' AND CHARACTER_SET_NAME != 'utf8mb4';
-- 單引號轉義規則:' → \'
-- 應用層處理(Java 示例)
String safeVal = val.replace("\\", "\\\\").replace("'", "\\'");
-- 或使用 PreparedStatement 完全避免轉義問題
PreparedStatement ps = conn.prepareStatement("INSERT INTO t1(name) VALUES (?)");
ps.setString(1, userInput); // 驅動自動處理編碼與轉義
ps.executeUpdate();
| 癥狀 | 典型根因 | 修復動作 |
|---|---|---|
| 插入「???」/「」 | 連接編碼為 latin1,無法表示中文 | SET NAMES utf8mb4 / JDBC characterEncoding=utf8mb4 |
| 插入「中文」讀出「涓浣」 | 寫入 utf8mb4,讀取按 latin1 解碼 | 統一讀寫連接編碼為 utf8mb4 |
| Emoji 存成「????」/報錯 | 庫表字符集為 utf8 (3 字節),不支持 4 字節 Emoji | 全鏈路升級 utf8mb4,含列級 MODIFY |
| 截斷「中文...」 | 列長度按字節計算,utf8mb4 中文佔 3-4 字節超過 VARCHAR(n) | 擴大列長度或改用 TEXT |
| 單引號導致 SQL 語法錯誤 | 拼接 SQL 未轉義 | 強制使用 PreparedStatement |
-- 1. 連接編碼
SHOW VARIABLES LIKE 'character_set_%';
-- client/connection/results/database/server 均為 utf8mb4
-- 2. 庫表字符集
SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_COLLATION
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'mydb';
-- 3. 列字符集
SELECT TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'mydb' AND CHARACTER_SET_NAME != 'utf8mb4';
-- 4. 實測寫讀
INSERT INTO t1(name) VALUES ('測試Emoji😀');
SELECT name, HEX(name), CHAR_LENGTH(name), LENGTH(name) FROM t1 WHERE name LIKE '%測試%';
-- HEX 應顯示 E6B58BE8AF95F09F9880 (UTF-8 字節序列)
utf8 實為 utf8mb3,不支持 Emoji,新項目強制 utf8mb4。SET NAMES 僅對當前連接生效,連接池復用時必須在借出前執行(HikariCP connectionInitSql)。ALTER TABLE ... DEFAULT CHARSET 不修改現有列,必須逐列 MODIFY。--default-character-set=utf8mb4,導入時目標庫必須同字符集。HEX(column) 是唯一真理,肉眼不可信。編碼一致性是系統工程,而非單點配置。建立「連接層統一、庫表層顯式、列層徹底、驗證層自動化」四層防線,中文亂碼將成為歷史。