為什麼修改 MySQL 的 character_set_server
後仍需重啟?從 mysql-connector-net 探討字元集的陷阱
在近期處理一個與 MySQL 字元集相關的問題時,我深入研究了 MySQL Server 的 Handshake 機制以及 mysql-connector-net
原始碼,發現了一個容易被忽略但可能會造成重大錯誤的細節——即使 character_set_server
是動態參數,但實際上修改後仍需要重啟 MySQL Server,否則會造成驅動端的解碼錯誤。
問題背景:為什麼驅動程式仍使用舊的字元集?
根據 MySQL Server 的設計,當 client 端連線時,Server 會在 Handshake Initial Packet 中回傳一些基本資訊,其中就包括伺服器的預設字元集(character_set_server
)。這段資訊是透過以下的程式碼取得:
1 | packet->append_int1(default_charset_info->number); |
根據 MySQL 官方文件,這段資訊會被封裝在 Handshake v10 Protocol 中傳送(官方文件)。
然而,這個值其實是在 MySQL Server 啟動時就被載入的,即使你在執行中動態修改 character_set_server
,重新連線後 greeting packet 裡的字元集值仍然不會更新。
驅動程式的行為:根據 Greeting 決定後續欄位解碼
mysql-connector-net
在收到 greeting packet 時,會將字元集儲存在 ConnectionCharSetIndex
:
1 | /* New protocol with 16 bytes to describe server characteristics */ |
🔗 連結程式碼位置
接下來,在處理每個欄位的資料時,若欄位的 charset 為 binary (63),會 fallback 回 connection 的預設值:
1 | if (CharacterSetIndex == 63) |
這裡的問題就產生了:在 MySQL protocol 中,即使是整數、時間戳等 binary 類型欄位,也會設定 charset 為 binary (63)。這意味著實際 decode 資料時會 fallback 到 UTF-16(預設 greeting 的字元集),導致錯誤解碼,產生亂碼或例外。
實驗觀察與錯誤範例
以下是我透過封包擷取與實際執行觀察到的問題:
Greeting Packet:
在連線時,我們觀察到 greeting charset 設為 54(UTF-16):
1 | Server Greeting |
整數欄位錯誤解析:
欄位的 characterSet
是 63 (binary),fallback 到 54(UTF-16)解碼,造成如下的 System.FormatException
:
1 | System.FormatException: Input string was not in a correct format. |
因為目前 mysql-connector-net client library 尚未完全支援 UTF16, 未來可能會處理此問題
修正建議與討論
為什麼需要重啟?
雖然 character_set_server
是 dynamic parameter,但 greeting 中的字元集是在啟動時就定義的(由 default_charset_info
載入)。因此如果你修改這個參數 但未重啟 MySQL Server,那麼新的連線仍然會收到舊的 greeting charset 資訊。
在使用 mysql-connector-net
這種驅動時,由於 greeting charset 會被 fallback 作為 binary 欄位的 decode 基準,這會導致我們解析數值型資料時發生錯誤或亂碼。
建議改善方向
驅動程式修正建議:
- 應該判斷 field type 是否為 binary 前,再決定是否 fallback 到 greeting charset。
- 例如:對於 Int32、Int64、Timestamp 等欄位,可跳過 charset fallback。
伺服器端操作建議:
- 若需更改
character_set_server
,務必搭配重新啟動 MySQL server,確保 greeting packet 同步更新。 - 如果您是使用 mysql-connector-net library 請使用 utf8mb4 取代 UTF16
- 若需更改
總結
這次調查突顯了 MySQL greeting packet 與驅動解碼邏輯間的一個不易察覺的錯誤來源。特別是在使用 mysql-connector-net
驅動、並處理 binary 或整數類型資料時,若伺服器端未重新啟動而 greeting charset 未更新,就可能引發解碼錯誤與系統例外。
__此文作者__:Daniel Shih(石頭)
__此文地址__: https://isdaniel.github.io/mysql-connector-net-charset-issue/
__版權聲明__:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW 許可協議。轉載請註明出處!