作者:秦廣飛
愛可生 DBA 團隊成員,負責專案日常問題處理及公司平臺問題排查,對資料庫有興趣,對技術有想法。一入 IT 深似海,從此節操是路人。
本文來源:原創投稿
*愛可生開源社群出品,原創內容未經授權不得隨意使用,轉載請聯絡小編並註明來源。
問題背景
我司某客戶最近在檢查一批新安裝的 MySQL 資料庫時,發現了下面的現象:
- 該批次的 MySQL 客戶端字符集全部為 Latin1 ;
- 而之前使用同樣引數模板部署的 MySQL ,客戶端字符集卻為 utf8 ;
已知 MySQL 版本為 5.7.32 ,伺服器作業系統為 Redhat 7 ,那麼為什麼兩次安裝的 MySQL 字符集會不一樣呢?
字符集介紹
首先我們簡單回顧下 MySQL 字符集的相關知識,MySQL 字符集大體可以分為下面兩個方面:
Server 級別字符集
- Server 級別的字符集,即資料儲存到資料庫時使用的字符集,又可以細化分為庫級別、表級別和欄位級別;
- 一般來說,如果建庫建表時沒有特別指定,那麼就會使用 Server 級別的字符集;
- Server 級別的字符集可以使用 character_set_server 引數指定;
Client 級別字符集
Client 級別的字符集,即客戶端連線進資料庫時使用的字符集,分別由下面幾個引數控制:
- character_set_client:Server認為Client傳送過來的請求是用該引數進行編碼的,因此在收到請求後會使用該引數進行解碼;
- character_set_connection:Server內部處理請求字串時,會從character_set_client轉為character_set_connection,因此兩個引數要一致;
- character_set_results:Server返回查詢結果給Client時,會根據character_set_results進行編碼,然後再返回,因此也需要和character_set_client保持一致;
- 也就是說,控制 Client 級別字符集的三個引數需要一致,可以使用下面的方法:
-- 資料庫中直接修改
mysql> set names utf8;
-- 配置檔案中修改,客戶端重新連線
shell> vi /etc/my.cnf
[mysql]
default-character-set = utf8
shell>
-- mysql客戶端使用的my.cnf檔案,可以根據mysql --help | grep "my.cnf" 確認
shell> mysql --help | grep "my.cnf"
order of preference, my.cnf, $MYSQL_TCP_PORT,
/etc/my.cnf /etc/mysql/my.cnf /usr/local/mysql/etc/my.cnf ~/.my.cnf
shell>
排查過程
回到這個問題,我們首先檢查了之前安裝的 MySQL 的字符集,確實為 utf8
之後,檢視伺服器上所有的 my.cnf 檔案,發現無論是之前安裝的還是最近一批安裝的,都僅有 MySQL 執行時指定的 my.cnf 檔案中有字符集相關引數:character_set_server=utf8mb4
最後,又檢視了伺服器上作業系統的字符集,發現有問題的為 en_US ,而原先的為 en_US.UTF-8
好像找到了問題出在哪裡,測試環境驗證下,果然當伺服器字符集設定為 en_US 後,MySQL 客戶端字符集變為了 Latin1
那麼,為什麼會這樣呢,我們看下官方文件上是怎麼說的:https://dev.mysql.com/doc/ref...
翻譯下來,大致有兩點含義:
- mysql, mysqladmin, mysqlcheck, mysqlimport, and mysqlshow 這些客戶端工具都有一個預設的字符集,MySQL 5.7 是 latin1 ,MySQL 8.0為 utf8mb4 ;
- 每個客戶端工具都會檢測作業系統的字符集,比如Linux系統LANG環境變數的值,如果MySQL支援作業系統的字符集,就會使用作業系統的(這裡支援包括不完全精確匹配時,OS字符集將對映到最接近的MySQL字符集);如果不支援,就使用客戶端預設字符集;
我們知道en_US最接近的字符集就是Latin1,所以回到我們的問題,當伺服器的字符集為en_US後,我們看到MySQL客戶端字符集為Latin1 ,是不是可以理解了
而使用MySQL 8.0的客戶端,能進一步驗證當不能精確匹配時,就使用MySQL最接近的字符集:
調整伺服器(Redhat 7 或 CentOS 7)的字符集可以參考下面的方式:
-- 立即生效,但重啟後會丟失
shell> export LANG="en_US.UTF-8"
-- 永久生效
shell> vim /etc/locale.conf
LANG="en_US.UTF-8"
shell> source /etc/locale.conf
總結
- 本次字符集與之前不一致的原因是,該批次虛擬機器映象的字符集有點問題,一般來說,Redhat 7 和 CentOS 7 都會是 en_US.UTF-8 ;
- MySQL Server 級別的字符集,可以用 character_set_server 引數指定;
- 控制 MySQL Client 級別字符集的三個引數需要保持一致,一般來說為utf8(MySQL 8.0 為 utf8mb4),同時又與 MySQL Client 所在伺服器的字符集有關;