許多開發者遇過:MySQL 設定檔 my.ini 已將 default-character-set=utf8,PHP 程式讀取資料卻顯示亂碼(?????),加上 mysql_query("SET NAMES UTF8") 即正常。為何設定檔不生效?SET NAMES 到底做了什麼?
MySQL 擁有多層字符集設定,執行 SHOW VARIABLES LIKE 'character_set_%' 可見:
| 變數 | 說明 | 預設值 |
|---|---|---|
| character_set_client | 客戶端發送 SQL 的編碼 | latin1 |
| character_set_connection | 連線層轉換目標編碼 | latin1 |
| character_set_database | 預設資料庫編碼 | utf8 |
| character_set_results | 回傳給客戶端的編碼 | latin1 |
| character_set_server | 伺服器預設編碼 | utf8 |
| character_set_system | 系統識別符編碼 (固定 utf8) | utf8 |
設定檔僅影響 database、server、system 三個變數,連線三要素 預設仍為 latin1。
SET NAMES 'utf8' 等同於一次執行三條指令:
SET character_set_client = utf8;
SET character_set_connection = utf8;
SET character_set_results = utf8;
這確保了資料流向編碼一致:
若中間任一環節為 latin1,UTF-8 資料經雙向轉碼會損毀,產生不可逆亂碼。
MySQL 官方文件未提供參數直接設定 character_set_client/connection/results 的啟動預設值。這三個變數屬於「連線級別」,每個 Session 建立時才初始化,故必須在每次連線成功後、執行查詢前 手動發送 SET NAMES。
mysqli::set_charset("utf8")、PDO DSN 加 charset=utf8、Java JDBC URL 加 characterEncoding=utf8。initSql 或 connectionInitSql 設定 SET NAMES utf8mb4(支援 Emoji)。my.cnf 設定 character-set-server=utf8mb4、collation-server=utf8mb4_unicode_ci,確保新建資料表預設正確。SET NAMES 非修改伺服器全域設定,而是調整當前 Session 的三個連線級字符集變數。只要「連線三要素」與資料實際編碼一致,亂碼問題即可根治。請養成連線後第一時間執行的習慣,或在連線池/驅動層自動化處理。