首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

MySQL 4.1+ 中文亂碼根因分析與 SET NAMES 徹底解決方案

     編輯:MySQL 資料庫相關知識

MySQL 4.1 引入多語系支援,字元集設定層層疊加(編譯預設 → 配置檔 → 啟動參數 → 資料庫 → 表 → 欄位),加上連線層的 character_set_client/connection/results 三重轉換,導致中文亂碼頻發。本文深入剖析機制,給出 SET NAMES 統一設定法與 PHP 應用層最佳實踐。

MySQL 字元集繼承鏈

編譯預設 latin1
  → my.cnf default-character-set (如 utf8)
    → 啟動參數 --default-character-set
      → character_set_server
        → CREATE DATABASE 時預設 character_set_database
          → CREATE TABLE 時預設表字元集
            → 定義欄位時預設欄位字元集
              → 實際儲存編碼

連線層三重轉換機制

客戶端送資料 → 1. 視為 character_set_client 編碼 → 2. 轉為 character_set_connection 編碼 → 3. 轉為資料庫/表/欄位儲存編碼 → 寫入磁碟。

讀取時:儲存編碼 → 4. 轉為 character_set_results 編碼 → 送給客戶端。

若第 1 步假設錯誤(如客戶端送 UTF-8,伺服器以 latin1 解讀),後續轉換皆會產生錯誤碼位,造成「??」或亂碼。

典型錯誤場景:WordPress + MySQL 4.1/5.x

  • 資料庫/表/欄位:UTF-8(my.cnf 設定)。
  • PHP/瀏覽器:UTF-8(HTML meta charset=utf-8)。
  • PHP 送資料:UTF-8 位元組流。
  • MySQL 預設 character_set_client = latin1,誤以為資料是 latin1 → 轉為 character_set_connection = latin1 → 再轉為 UTF-8 存入 → 部分字元損壞成 ??。
  • 讀出時 character_set_results = latin1 → 再次錯誤轉換 → 前端顯示亂碼。

解決方案:統一設定連線字元集

方法一:SET NAMES 'charset'(最簡單、效果等同三個 SET)

-- 客戶端送 UTF-8,資料庫存 UTF-8
SET NAMES 'utf8';  -- MySQL 5.5+ 建議用 utf8mb4
-- 等同於:
SET character_set_client = 'utf8';
SET character_set_connection = 'utf8';
SET character_set_results = 'utf8';

方法二:客戶端送 BIG5/GBK,資料庫存 UTF-8

SET character_set_client = 'gbk';
SET character_set_connection = 'utf8';  -- 或 gbk 皆可,轉換路徑不同
SET character_set_results = 'gbk';

方法三:my.cnf 固定預設(需重啟)

[mysqld]
default-character-set = utf8mb4
character_set_server = utf8mb4
collation_server = utf8mb4_unicode_ci

[client]
default-character-set = utf8mb4

PHP 應用層最佳實踐

1. 連線建立後立即執行 SET NAMES(僅一次)

<?php
$mysqli = new mysqli('localhost', 'user', 'pwd', 'db');
if ($mysqli->connect_errno) die($mysqli->connect_error);
// 根據頁面 charset 決定
$charset = 'utf8'; // 或從設定讀取
$mysqli->set_charset($charset); // 內部會送 SET NAMES
?>

2. PDO 方式

$pdo = new PDO('mysql:host=localhost;dbname=db;charset=utf8mb4', 'user', 'pwd');
// DSN 中 charset 參數會自動送 SET NAMES(PHP 5.3.6+)

3. 舊版 mysql 擴充套件(已棄用)

mysql_connect(...);
mysql_query("SET NAMES 'utf8'"); // 每次連線後執行

MySQL 8.0 重要變更

  • 預設字元集從 latin1 改為 utf8mb4,預設校對 utf8mb4_0900_ai_ci。
  • utf8 別名指向 utf8mb3(最多 3 位元組),新專案請直接使用 utf8mb4(支援 Emoji、補充字元)。
  • 連線字元集協商更智慧,但仍建議明確在 DSN 或連線後指定 charset=utf8mb4。

驗證指令

SHOW VARIABLES LIKE 'character_set_%';
SHOW VARIABLES LIKE 'collation_%';
/* 關鍵變數應皆為 utf8mb4:
character_set_client
character_set_connection
character_set_database
character_set_results
character_set_server
collation_connection
collation_database
collation_server */

常見誤區

誤區正確觀念
資料庫建立時指定 UTF-8 就萬事大吉連線層字元集未設定,資料寫入/讀出仍會雙重轉換錯誤
SET NAMES 每次查詢前都執行連線建立後執行一次即可,重複執行浪費效能
utf8 就夠了utf8 實為 utf8mb3,無法儲存 Emoji 與部份生僻字,請用 utf8mb4
PHP header('Content-Type: text/html; charset=utf-8') 就能解決只影響瀏覽器解碼,不影響 PHP→MySQL 傳輸編碼

徹底理解字元集流向,在連線建立時統一宣告 SET NAMES utf8mb4(或 DSN 指定 charset),即可從根源杜絕中文亂碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved