字符集參考文獻:
http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_character_set_connection
http://dev.mysql.com/doc/refman/5.6/en/faqs-cjk.html
今天,一開發同事使用jdbc連接數據庫執行一條語句無結果集,但是通過sqlyou執行相同的語句有返回結果。
執行的語句where條件中含有中文,這應該是字符集引起的
此開發測試實例剛遷移不久的,查看遷移前的環境默認字符集都是utf8
查看當前數據庫的字符集
mysql> show variables like '%charac%'; +--------------------------+----------------------------------+ | Variable_name | Value | +--------------------------+----------------------------------+ | character_set_client | utf8 | | character_set_connection | utf8 | | character_set_database | latin1 | | character_set_filesystem | binary | | character_set_results | utf8 | | character_set_server | latin1 | | character_set_system | utf8 | | character_sets_dir | /usr/local/mysql/share/charsets/ | +--------------------------+----------------------------------+ 8 rows in set (0.00 sec)
- character_set_client
- character_set_connection
- character_set_results
以上三個控制mysql client的字符集
- character_set_database
設置數據庫的默認字符集
- character_set_server
設置以上所有的默認字符集
發現server端的字符集和client端的全局字符集設置變量都是采用的默認值latin1
發現配置文件中沒有添加參數項 character-set-server=utf8
造成亂碼的原因:
數據存儲時的編碼解碼過程
jdbc=>character_set_client=>table character
每個環節的字符集編碼都是utf8,沒有轉碼過程
character_set_client變為latin1后,讀取數據的解碼過程為
jdbc<=character_set_client<=table character
表中存儲的是utf8編碼格式,判斷和character_set_client不一致則轉碼為latin1的二進制流,然后傳輸給遠端的客戶端,
客戶端jdbc通過設置的字符集展示結果,使用utf8展示latin1,所以出現了亂碼。
解決辦法
# character_set_filesystem 、character_set_system 、character_sets_dir除外都變更全局為utf8
所有的應用需要重連數據庫才能變更會話級別的字符集
對於在字符集設置為latin1期間插入的數據編碼存儲過程:
- 在terminal(這里為jdbc客戶端)中使用輸入法輸入
- terminal轉換成utf8二進制流
- 二進制流通過MySQL客戶端傳輸到MySQL Server
- Server通過character-set-client解碼
- 判斷character-set-client和目標表的charset是否一致,character-set-client為latin1,目標表的字符集為utf8
- 不一致則進行一次從client-charset到table-charset的一次字符編碼轉換,由latin1轉碼為utf8
- 將轉換后的字符編碼二進制流存入文件中
測試這種情況下將 中間環節的character-set-client變更為utf8,是否會出現亂碼
mysql> show variables like '%char%'; +--------------------------+----------------------------------+ | Variable_name | Value | +--------------------------+----------------------------------+ | character_set_client | latin1 | | character_set_connection | latin1 | | character_set_database | utf8 | | character_set_filesystem | binary | | character_set_results | latin1 | | character_set_server | utf8 | | character_set_system | utf8 | | character_sets_dir | /usr/local/mysql/share/charsets/ | +--------------------------+----------------------------------+ 8 rows in set (0.01 sec) mysql> update t1 set col='建軍節' where id=4; mysql> select * from t1 where id=4; +----+-----------+------+ | id | col | time | +----+-----------+------+ | 4 | 建軍節 | NULL | +----+-----------+------+ 解碼編碼轉儲 crt terminal =》character_set_client =》character_set_server utf8 latin1 utf8 如果查詢時任何一個環節的字符集變化都可能會造成亂碼 更改不同環節的字符集對應的數據顯示 1、改變客戶端的字符集 mysql> set names utf8; Query OK, 0 rows affected (0.00 sec) mysql> select * from t1 where id=4; +----+-----------------------+------+ | id | col | time | +----+-----------------------+------+ | 4 | ??o???è?? | NULL | +----+-----------------------+------+ 2、更改crt terminal 的字符集為default mysql> select * from t1 where id=4; +----+-----------+------+ | id | col | time | +----+-----------+------+ | 4 | 寤哄啗鑺 | NULL | +----+-----------+------+ 3、更改表字段字符集 ALTER TABLE t1 CHANGE col col varchar(10) CHARACTER SET latin1; mysql> select * from t1 where id=4; +----+-----------+------+ | id | col | time | +----+-----------+------+ | 4 | 建軍節 | NULL | +----+-----------+------+
更改表的字符集為latin1,讀取數據涉及到變更的環節變為
- 從文件讀出二進制數據流(utf8存入)
- 用表字符集latin1編碼進行解碼
- 將數據轉換為character-set-client的編碼laint1
對應的變更前的環節:
- 從文件讀出二進制數據流(utf8存入)
- 用表字符集utf8編碼進行解碼
- 將數據轉換為character-set-client的編碼laint1
可以看出更改表數據字符集沒有導致亂碼的原因是,字符集整體經歷的解碼和轉碼過程是一致的,都經歷了一次由utf8到latin1的轉碼。
另一有關字符集的問題:為了支持表情符號,將系統級別的utf8設置為utf8mb4且相應的表也做了字符集的轉變,重啟應用不生效,重啟數據庫和應用才會生效
參考文章為:
http://blog.sina.com.cn/s/blog_93b45b0f0101glfx.html
參考文章:
編碼解碼過程 : http://cenalulu.github.io/mysql/mysql-mojibake/
