中文亂碼是 MySQL 開發中最經典、最讓人頭痛的問題。其本質是**編碼鏈路任一環節不一致**導致的解碼錯誤。本文提供從資料庫建立、表結構設計、連線配置、應用層設定到頁面輸出的全鏈路統一標準化方案,徹底根治亂碼。
| 環節 | 關鍵設定 | 常見錯誤 |
| 1. 頁面/HTTP | <meta charset="utf-8"> / HTTP Header Content-Type: text/html; charset=utf-8 | 頁面聲明 GBK 但實為 UTF-8,或反之 |
| 2. 應用連線 | SET NAMES utf8mb4 / 連線參數 characterEncoding=utf8 | 未設定、或設為 latin1/GBK |
| 3. Server 系統變數 | character_set_server=utf8mb4 collation_server=utf8mb4_unicode_ci | 預設 latin1,導致隱式轉換 |
| 4. 表/欄位定義 | DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci | 建表未指定,繼承庫/Server 預設 |
| 5. 存儲引擎 | InnoDB 內部無編碼,依賴上層傳遞 | 無直接設定 |
鐵律:五個環節必須 100% 一致,缺一不可。
二、資料庫與表建立:源頭治理
建庫標準語句
CREATE DATABASE `dbname`
DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
建表標準語句(中文欄位顯式指定)
CREATE TABLE `test` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='測試表';
關鍵點:
- 必須使用
utf8mb4 而非 utf8:MySQL 的 utf8 實為 utf8mb3,**不支援 Emoji 及生僻字**(4 字節字符),會導致截斷或報錯
COLLATE utf8mb4_unicode_ci:基於 Unicode 標準排序,支援多語言;utf8mb4_general_ci 速度略快但排序準確度較低
- 表級
DEFAULT CHARSET 為新增欄位提供預設,欄位級可覆蓋
三、應用層連線配置:關鍵橋樑
PHP (mysqli/PDO)
// mysqli 面向對象
$mysqli = new mysqli($host, $user, $pass, $db, $port);
$mysqli->set_charset('utf8mb4'); // 單一調用替代下面兩行
// PDO (推薦)
$dsn = "mysql:host=$host;port=$port;dbname=$db;charset=utf8mb4";
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false
]);
Java (JDBC / Spring Boot)
# JDBC URL (8.0+ 無需 characterEncoding)
jdbc:mysql://host:3306/db?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai
# Spring Boot application.yml
spring:
datasource:
url: jdbc:mysql://host:3306/db?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai
hikari:
connection-init-sql: "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"
Python (PyMySQL / SQLAlchemy)
# PyMySQL
conn = pymysql.connect(host=host, user=user, password=pass, db=db, charset='utf8mb4')
# SQLAlchemy
engine = create_engine('mysql+pymysql://user:pass@host/db?charset=utf8mb4')
Node.js (mysql2)
const pool = mysql.createPool({
host, user, password, database,
charset: 'utf8mb4',
// 或連線後執行
// connection.query('SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci')
});
四、歷史專案遷移:從 GBK/UTF8 升級至 UTF8MB4
- 修改 Server 配置 (
my.cnf):
[mysqld]
character_set_server = utf8mb4
collation_server = utf8mb4_unicode_ci
# 客戶端預設也建議設定
[client]
default-character-set = utf8mb4
- 轉換現有庫表:
-- 轉換資料庫預設
ALTER DATABASE `dbname` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
-- 轉換表及所有欄位(會重建表,大表謹慎)
ALTER TABLE `tbl` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 僅修改表預設(不轉換現有欄位)
ALTER TABLE `tbl` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
- 檢查最大長度限制:
utf8mb4 單字符最多 4 字節,VARCHAR(255) 索引長度可能超過 767 字節限制(5.7 預設),需開啟 innodb_large_prefix=ON + innodb_file_format=Barracuda + ROW_FORMAT=DYNAMIC(8.0 預設已解決)
- 應用層同步修改連線字符集:同上節配置
五、驗證與排查清單
-- 1. 檢查當前連線編碼
SHOW VARIABLES LIKE 'character_set_%';
SHOW VARIABLES LIKE 'collation_%';
-- 2. 檢查表/欄位實際編碼
SHOW CREATE TABLE `tbl`\G
SELECT COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA='db' AND TABLE_NAME='tbl';
-- 3. 實測寫入/讀取
INSERT INTO `tbl`(`name`) VALUES('測試😀');
SELECT `name`, HEX(`name`) FROM `tbl` WHERE `name`='測試😀';
-- HEX 應顯示:E6B58BE8AF95F09F9880 (UTF-8 編碼)
六、常見亂碼現象與成因對照表
| 現象 | 典型成因 | 解決方向 |
| (菱形問號) | UTF-8 資料被 Latin1 解碼 | 連線層 SET NAMES utf8mb4 |
| 鍛沖涓 (亂碼中文) | UTF-8 資料被 GBK 解碼 | 頁面/應用層編碼統一 UTF-8 |
| ??? (問號) | 字符集不支援該字符(如 utf8 存 Emoji) | 升級 utf8mb4 |
| 截斷/報錯 Data too long | 4 字節字符導致字節長度超限 | 調整 VARCHAR 長度/啟用 large_prefix |
徹底解決中文亂碼沒有捷徑,唯有**全鏈路統一 utf8mb4**、**新專案起步即標準化**、**舊專案計劃遷移驗證**。將本方案落地為團隊「編碼規範文檔」,納入代碼審查清單,才能從根源杜絕亂碼困擾。
- 上一篇文章: MySQL DDL 語句完整手冊:庫表結構管理的核心語法與細節陷阱
- 下一篇文章: MySQL DATE_ADD 函數實戰指南:時間日期運算的完整場景覆蓋
Copyright ©
程式碼之家 All Rights Reserved