核心原則:三層編碼必須一致
| 層級 | 系統變量 | 作用 | 推薦值 |
| 客戶端發送 | character_set_client | 客戶端→Server 編碼 | utf8mb4 |
| 連接層轉換 | character_set_connection | 內部處理編碼 | utf8mb4 |
| 結果返回 | character_set_results | Server→客戶端 編碼 | utf8mb4 |
一鍵設定:SET NAMES utf8mb4; 等同於同時設定上述三個變量。
亂碼兩大根因場景
場景 A:寫入未設編碼,讀取時設了 → 雙重編碼
-- 寫入:client=latin1, connection=latin1 → UTF-8 數據被當 latin1 存 → 每中文 3 字節變 6 字節
-- 讀取:client=utf8, connection=utf8 → 6 字節原樣返回 → 客戶端按 UTF-8 解碼 → 亂碼
場景 B:寫入設 UTF-8 連接,但表為 latin1 → 數據截斷/丟失
-- 寫入:client=utf8, connection=utf8 → 內部轉 latin1 存儲 → 超出 latin1 範圍字符變 ? (0x3F) → 永久損壞
各語言/框架配置模板
JDBC (Java)
# URL 參數
jdbc:mysql://host:3306/db?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai
# 連接池屬性
dataSource.setConnectionProperties("useUnicode=true;characterEncoding=utf8mb4;");
PHP (mysqli/PDO)
// mysqli
mysqli_set_charset($link, 'utf8mb4');
// PDO
$pdo = new PDO("mysql:host=localhost;dbname=test;charset=utf8mb4", $user, $pass,
[PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"]);
Python (PyMySQL/SQLAlchemy)
# PyMySQL
conn = pymysql.connect(host='localhost', user='root', password='pass', db='test', charset='utf8mb4')
# SQLAlchemy
engine = create_engine('mysql+pymysql://user:pass@host/db?charset=utf8mb4')
JSP / Servlet / Filter
<%@ page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8" %>
public class EncodingFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
req.setCharacterEncoding("UTF-8");
res.setCharacterEncoding("UTF-8");
chain.doFilter(req, res);
}
}
Tomcat (server.xml)
MySQL 服務端 (my.cnf)
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
init_connect = 'SET NAMES utf8mb4'
skip-character-set-client-handshake -- 強制忽略客戶端請求編碼,統一用 server 設定
Java 編碼 ↔ MySQL 編碼對照表
| Java | MySQL |
| UTF-8 | utf8mb4 |
| GBK | gbk |
| GB2312 | gb2312 |
| ISO-8859-1 | latin1 |
utf8 → utf8mb4 遷移檢查清單
my.cnf:character-set-server=utf8mb4、collation-server=utf8mb4_unicode_ci
ALTER DATABASE db CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
- 檢查索引長度:
VARCHAR(255) utf8mb4 = 767 字節,需 innodb_large_prefix=ON + ROW_FORMAT=DYNAMIC。
- 應用層連接參數同步修改為
utf8mb4。
- 驗證:
INSERT INTO t(col) VALUES ('😀'); SELECT HEX(col) FROM t; → F09F9880 (4 字節)。
常見誤區
| 誤區 | 真相 |
my.cnf 的 default-character-set 對所有客戶端生效 | **僅影響 mysql 命令列客戶端**,不影響 JDBC/PHP/ODBC 等 |
設了 SET NAMES utf8 就萬事大吉 | **庫/表/欄位編碼必須也為 utf8/utf8mb4**,否則仍會轉碼丟失 |
| utf8 支援 Emoji | MySQL utf8 = utf8mb3,**不支援**補充平面,**必須用 utf8mb4** |