圖解MySQL | [原理解析] 設置字符集的參數控制了哪些行爲

原創做者:黃炎,王悅html


引  言mysql

本文是由愛可生研發團隊出品的「圖解MySQL」系列文章,不按期更新,但篇篇精品。sql

愛可生開源社區持續運營維護的小目標:數據庫

  • 每週至少推送一篇高質量技術文章服務器

  • 每個月研發團隊發佈開源組件新版大數據

  • 每一年1024開源一款企業級組件編碼

  • 2019年至少25場社區活動spa

歡迎你們持續關注~code

本文以 一個字符集設置引發的故障現象 入手, 介紹 MySQL字符集的各個參數 的做用.server


故障現象描述

在向MySQL導入數據時,先設置`set names gbk`,而後經過source導入一個很大的SQL文件 (文件字符集爲gbk),發現以下行爲:

  1. 正常狀況下,SQL文件中的SQL以分號分割,發往MySQL的每個數據包會帶有一個SQL。

  2. 在語句"INSERT INTO ... VALUES(...,'璡',...); "以後,全部數據會打包在一塊兒,經過大數據包協議一塊兒發往MySQL (若單個數據大於16M,MySQL則使用大數據包協議,參考[1])。

初步猜想, 認爲是字符集的設置問題。


MySQL的字符集參數

在談及MySQL字符集時,還必須介紹校驗集。字符集(character set)表示字符以何種規則進行編碼,校驗集(collation)表示字符以何種規則進行比較和排序 (例如:是否大小寫敏感)。

MySQL有如下字符集相關的設置:

  1. 三組設置字符集+校驗集的參數:

    1. character_set_connection/collation_connection

    2. character_set_server/collation_server

    3. character_set_database/collation_database,此組參數已被廢棄。

  2. 四個只能設置字符集的參數:

    1. character_set_client

    2. character_set_results

    3. character_set_filesystem

    4. character_set_system,此參數值固定爲utf8,且不可改變。本文不涉及此項。

  3. MySQL client中, 有一個內存變量`charset_info`,本文中稱其爲`mysql.client.charset`

  4. 存儲層:數據庫/數據表/數據列 均由單獨的字符集+校驗集參數,經過CREATE語句可進行設置。MySQL文檔中有詳細記述。

(後文描述字符集+校驗集時,以字符集爲例,校驗集的出現位置與對應的字符集相同)

各類配置的關係以下圖所示,下圖示例爲用source命令導入sql.txt,其中包含一個`SELECT...INTO OUTFILE`語句:

(爲描述方便,以後將`character_set_xxx`簡寫爲`cs_xxx`)

 

說明:

  1. client從sql.txt讀取SQL。SQL在sql.txt中遵循文件的字符集,client按照`mysql.client.charset`進行讀取。

  2. client將SQL發往server。SQL按照`cs_client`字符集進行發送。server接收SQL後, 將其中的字符串常量 轉換成`cs_connection`字符集。

  3. server接收SQL後, 將其中的 **文件名常量 **轉換成`cs_filesystem`。

  4. server將常量傳給存儲層InnoDB時, 需將常量轉換成存儲層的字符集。

  5. SQL中的文件名,以`cs_filesystem`字符集寫入文件系統。

  6. 若查詢結果要存入文件,可在`SELECT...INTO...`語句中指定字符集,或默認使用binary。

  7. 若查詢結果直接返回client,則將結果轉換爲`cs_results`後返回。

 

關於存儲層的字符集: 

數據庫/數據表/數據列 級別的字符集可分別指定,如圖中左下部分所示,子級別可指定字符集 或 從父級別繼承。

其中`cs_database`已被廢棄,但還沒有移除。

**關於字符集的設置:**除直接設置變量,字符集的常見設置方法爲:

  1. client鏈接握手時指定字符集,經過--default-character-set參數啓動client可設置。

  2. `SET NAMES `語句

  3. `SET CHARSET `語句

其三種設置方式對參數的影響參看圖中最後的列表, 能夠看出:

  1. `SET NAMES` 並不會影響客戶端解析SQL文件的字符集`mysql.client.charset`

  2. `SET CHARSET` 會將 `cs_connection` 設置成 `cs_database`的值,而並不是設置的字符集。


故障分析

瞭解MySQL的字符集各項參數後,故障的緣由就比較明顯了:

  1. `SET NAMES`並不影響`mysql.client.charset`,所以MySQL解析sql.txt時,使用了默認字符集utf8。

  2. "璡"字的二進制編碼爲ad5c,5c的對應字符爲"\"。所以"(璡)(單引號)"會被誤讀爲"(以前字符的編碼xx + ad對應的字符)(被轉義的單引號)",致使後面的SQL由於單引號不封閉而被認爲是一個字符串,所以MySQL client沒法正確切分SQL。


回顧

經過以前的說明,能夠猜想MySQL的字符串參數的意圖:

- 能夠 設置****字符集+校驗集 的機制,都須要進行字符串的比較。好比`cs_connection` 可用於 WHERE 條件中的比較運算,存儲層的字符集 可用於 存儲內的排序操做。

- 能夠 設置字符集但不能設置校驗集 的機制,都跟外部系統相關 (相對於MySQL server)。好比`cs_client`和`cs_results`跟MySQL client相關,`cs_filesystem`與服務器的文件系統相關。

- `mysql.client.charset`在MySQL文檔中並未有介紹,但會影響MySQL client對SQL文件的解析。


擴展閱讀

https://dev.mysql.com/doc/refman/5.7/en/charset.html

https://www.cnblogs.com/chyingp/p/mysql-character-set-collation.html


[1] https://dev.mysql.com/doc/internals/en/sending-more-than-16mbyte.html

相關文章
相關標籤/搜索