數據存儲 (表 CHARSET) → 連線傳輸 (SET NAMES) → 頁面輸出 (HTTP Header/Meta) → 瀏覽器渲染
↑ ↑ ↑ ↑
utf8mb4 utf8mb4 utf-8 utf-8
任一環節為 latin1/gbk 即產生亂碼 (、???、鍵盤字符)。
CREATE DATABASE my_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE articles (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(200) NOT NULL,
content TEXT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- PHP (mysqli/PDO)
$mysqli->set_charset('utf8mb4');
$pdo->exec("SET NAMES utf8mb4");
-- Java (JDBC URL)
jdbc:mysql://host:3306/db?characterEncoding=utf8&useUnicode=true&connectionCollation=utf8mb4_unicode_ci
-- Python (PyMySQL)
conn = pymysql.connect(charset='utf8mb4')
-- Go (go-sql-driver/mysql)
dsn := "user:pass@tcp(host:3306)/db?charset=utf8mb4&parseTime=True&loc=Local"
-- PHP (輸出任何內容前)
header('Content-Type: text/html; charset=utf-8');
-- Nginx (全站生效)
add_header Content-Type "text/html; charset=utf-8";
# 或
charset utf-8;
-- HTML Meta (兜底)
pt-online-schema-change \
--alter "CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci" \
D=my_db,t=big_table \
--execute --max-load Threads_running=50 --critical-load Threads_running=100
| 現象 | 原因 | 修正 |
|---|---|---|
| Emoji 存成 ???? | 表/列為 utf8 (僅 3 字節) | 改 utf8mb4 (4 字節) |
| 中文變 | 連線未 SET NAMES,驅動默認 latin1 | 連線建立即執行 SET NAMES utf8mb4 |
| 頁面顯示 鍵盤字符 | HTTP Header 無 charset,瀏覽器猜測錯誤 | header('Content-Type: ... charset=utf-8') |
| JDBC 存取正常但查詢亂碼 | connectionCollation 未指定 | JDBC URL 加 connectionCollation=utf8mb4_unicode_ci |
**「庫表 utf8mb4、連線 SET NAMES、輸出 Header utf-8」** 三句口訣貫穿全鏈路。新專案開局即鎖定,舊專案用 pt-osc 遷移,徹底告別中文亂碼,同時擁抱 Emoji 與多語言國際化。