原創做者:黃炎,王悅html
引 言mysql
本文是由愛可生研發團隊出品的「圖解MySQL」系列文章,不按期更新,但篇篇精品。sql
愛可生開源社區持續運營維護的小目標:數據庫
每週至少推送一篇高質量技術文章服務器
每個月研發團隊發佈開源組件新版大數據
每一年1024開源一款企業級組件編碼
2019年至少25場社區活動spa
歡迎你們持續關注~code
本文以 一個字符集設置引發的故障現象 入手, 介紹 MySQL字符集的各個參數 的做用.server
在向MySQL導入數據時,先設置`set names gbk`,而後經過source導入一個很大的SQL文件 (文件字符集爲gbk),發現以下行爲:
正常狀況下,SQL文件中的SQL以分號分割,發往MySQL的每個數據包會帶有一個SQL。
在語句"INSERT INTO ... VALUES(...,'璡',...); "以後,全部數據會打包在一塊兒,經過大數據包協議一塊兒發往MySQL (若單個數據大於16M,MySQL則使用大數據包協議,參考[1])。
初步猜想, 認爲是字符集的設置問題。
在談及MySQL字符集時,還必須介紹校驗集。字符集(character set)表示字符以何種規則進行編碼,校驗集(collation)表示字符以何種規則進行比較和排序 (例如:是否大小寫敏感)。
MySQL有如下字符集相關的設置:
三組設置字符集+校驗集的參數:
character_set_connection/collation_connection
character_set_server/collation_server
character_set_database/collation_database,此組參數已被廢棄。
四個只能設置字符集的參數:
character_set_client
character_set_results
character_set_filesystem
character_set_system,此參數值固定爲utf8,且不可改變。本文不涉及此項。
MySQL client中, 有一個內存變量`charset_info`,本文中稱其爲`mysql.client.charset`
存儲層:數據庫/數據表/數據列 均由單獨的字符集+校驗集參數,經過CREATE語句可進行設置。MySQL文檔中有詳細記述。
(後文描述字符集+校驗集時,以字符集爲例,校驗集的出現位置與對應的字符集相同)
各類配置的關係以下圖所示,下圖示例爲用source命令導入sql.txt,其中包含一個`SELECT...INTO OUTFILE`語句:
(爲描述方便,以後將`character_set_xxx`簡寫爲`cs_xxx`)
說明:
client從sql.txt讀取SQL。SQL在sql.txt中遵循文件的字符集,client按照`mysql.client.charset`進行讀取。
client將SQL發往server。SQL按照`cs_client`字符集進行發送。server接收SQL後, 將其中的字符串常量 轉換成`cs_connection`字符集。
server接收SQL後, 將其中的 **文件名常量 **轉換成`cs_filesystem`。
server將常量傳給存儲層InnoDB時, 需將常量轉換成存儲層的字符集。
SQL中的文件名,以`cs_filesystem`字符集寫入文件系統。
若查詢結果要存入文件,可在`SELECT...INTO...`語句中指定字符集,或默認使用binary。
若查詢結果直接返回client,則將結果轉換爲`cs_results`後返回。
關於存儲層的字符集:
數據庫/數據表/數據列 級別的字符集可分別指定,如圖中左下部分所示,子級別可指定字符集 或 從父級別繼承。
其中`cs_database`已被廢棄,但還沒有移除。
**關於字符集的設置:**除直接設置變量,字符集的常見設置方法爲:
client鏈接握手時指定字符集,經過--default-character-set
參數啓動client可設置。
`SET NAMES `語句
`SET CHARSET `語句
其三種設置方式對參數的影響參看圖中最後的列表, 能夠看出:
`SET NAMES` 並不會影響客戶端解析SQL文件的字符集`mysql.client.charset`
`SET CHARSET` 會將 `cs_connection` 設置成 `cs_database`的值,而並不是設置的字符集。
瞭解MySQL的字符集各項參數後,故障的緣由就比較明顯了:
`SET NAMES`並不影響`mysql.client.charset`,所以MySQL解析sql.txt時,使用了默認字符集utf8。
"璡"字的二進制編碼爲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