mysql字符集和校对规则(character sets and collations)详解
mysql字符集(character sets)是指一系列符號以及符號對應的編碼的集合,比如英文字母可以用ASCII編碼,中文可以用GBK或者UTF8編碼。校對規則(collations)則是指一種比較字符的規則,這種比較規則決定了mysql如何進行排序以及如何對字符比較大小。
mysql的character sets和collations有很多種,而且可以在多個維度去配置,包含服務器的配置和客戶端的配置,對于初學者往往容易搞混,有時候出了亂碼等問題也不知道怎么排查。今天筆者就詳細梳理一下mysql中的character sets和collations。
我們先來看看mysql都可以配置哪些字符集,輸入如下命令查看。
show variables like '%character%';?可以看到,有多達7個的character_set相關的參數,接下來我們就詳細說說這些參數。
查看mysql中支持的字符集和校對規則?
mysql中每個字符集都會對應多個校對規則,是一對多的關系。比如utf8對應的collation有utf8_general_ci,utf8_bin,utf8_unicode_ci等。而且每個character set會有個默認的collation與之對應,當我們在創建數據庫或者創建表時如果只指定character set,不指定collation,就會使用character set默認的collation。collation的命名是以對應的character set為開頭,比如collation為utf8_general_ci,我們就知道這個collation對應的字符集是utf8。
查看字符集
2種方式可以查看mysql中支持哪些字符集
1.?通過INFORMATION_SCHEMA.CHARACTER_SETS表來查看
2.?通過SHOW CHARACTER SET來查看,和上面表的結果是一樣的。
查看collations
通過 show collation?查看校對規則有哪些,還可在語句后面加where條件篩選。
配置與查看字符集與校對規則
服務器端相關character set和collation
mysql服務器端相關字符集是服務器對數據的存儲等相關的字符集,不涉及客戶端的問題。
mysql服務器端的字符集和校對規則可以在四個級別指定:server, database, table, column
server級別
在mysql5.7中,server character set 和 server collation 默認為 latin1 和 latin1_swedish_ci,在mysql8中,默認為utf8mb4 和 utf8mb4_0900_ai_ci
如果想要修改server級別的character set和collation,可以在啟動時指定參數
?mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_0900_ai_ci或者將上面的參數寫入到my.ini中[mysqld]下面,如下圖。
server級別的character set和collation的作用是為創建數據庫(CREAT DATABASE)時指定默認字符集和校對規則。也就是說創建數據庫時如果沒有指定character set和collation,就默認使用server級別的。
server級別的character set 和 collation可通過如下命令查看
show variables like 'character_set_server' show variables like 'collation_server'?
database級別
數據庫級別的character set和collation可以在創建和修改數據庫時指定,如果不指定,則使用server級別的。
CREATE DATABASE db_name?[[DEFAULT] CHARACTER SET charset_name]?[[DEFAULT] COLLATE collation_name]ALTER DATABASE db_name?[[DEFAULT] CHARACTER SET charset_name]?[[DEFAULT] COLLATE collation_name]舉例:create database demo character set utf8 collate utf8_general_ci;數據庫的character set和collation可通過INFORMATION_SCHEMA.SCHEMATA表查看。
比如下圖,我創建了三個數據庫test, test2, test3,test數據庫的character set和collation分別為utf8和utf8_general_ci,而test2和test3數據庫的character set和collation分別為latin1和latin1_swedish_ci。
也可以通過如下方式查看
USE db_name; SELECT @@character_set_database, @@collation_database;數據庫級別的character set和collation會影響以下行為:
1.?創建表時,會默認使用database的character set和collation。
2.?使用mysql的LOAD DATA加載數據時,默認采用database的character set和collation。
3.?作用于存儲過程和函數,在創建存儲過程和函數時,傳遞的參數默認采用database的character set。
比如我的database?character set為latin1,然后我創建一個存儲過程proc1(nameStr varchar(20)),當我調用存儲過程call proc1('張三')時,會報錯,因為存儲過程proc1的參數只支持latin1字符集。
table級別
table的character set和collation可在創建表時指定,不指定默認采用database級別的。
CREATE TABLE tbl_name (column_list)[[DEFAULT] CHARACTER SET charset_name][COLLATE collation_name]]ALTER TABLE tbl_name[[DEFAULT] CHARACTER SET charset_name][COLLATE collation_name]table級別的character set和collation會影響column級別的character set和collation,也就是column如果不指定character set和collation,就會默認繼承table級別的。
要想查看表的character set和collation,可通過information_schema.TABLES表查看,不過這個表只保存了collation信息,但是通過collation我們就能知道character set是什么了。
?或者通過如下命令查看
column級別
指定column級別的character set和collation,只有column的類型為字符型,如char,varchar,text等時,才可指定character set和collation。
col_name {CHAR | VARCHAR | TEXT} (col_length)[CHARACTER SET charset_name][COLLATE collation_name]舉例 CREATE TABLE t1 (col1 VARCHAR(5)CHARACTER SET latin1COLLATE latin1_german1_ci );ALTER TABLE t1 MODIFYcol1 VARCHAR(5)CHARACTER SET latin1COLLATE latin1_swedish_ci;要查看列的character set和collation,可通過information_schema.COLUMNS表查看。
客戶端相關character set和collation
客戶端可能和mysql服務器采用不同的字符集,比如我們的java應用用的是一套字符集,mysql服務器用的是另一套字符集,這種情況下就要指定客戶端和服務器連接交互時的字符集。和客戶端相關的字符集配置有3個參數:character_set_client、character_set_connection、character_set_results。
這3個參數都是session級別的,也就是說不同的客戶端連接時可以把這3個參數指定為不同的值,而且在客戶端和服務器連接建立成功后,可以動態修改這3個值,不需要重啟mysql。
參數作用
mysql服務器把從客戶端接收的數據從character_set_client 轉成?character_set_connection,然后進行后續處理。把查詢結果轉成character_set_results返回給客戶端。所以character_set_client和character_set_connection是在向mysql發送命令的時候起作用,character_set_results是在接收mysql數據時起作用。
參數查看與設置
在連接mysql成功后,通過如下2種方式查看這三個參數的值。
方式一:
SELECT * FROM performance_schema.session_variables WHERE VARIABLE_NAME IN ( 'character_set_client', 'character_set_connection', 'character_set_results', 'collation_connection' ) ORDER BY VARIABLE_NAME;方式二:
SHOW SESSION VARIABLES LIKE 'character\_set\_%'; SHOW SESSION VARIABLES LIKE 'collation\_%';設置這三個參數的值有以下3種方式。
方式一:
在mysql客戶端連接時指定,可以配置在my.ini中,或者寫在命令行后面。
[mysql]
default-character-set=utf8
mysql -uroot -p?--default-character-set=utf8
方式二:
set names?charset_name?
這個命令等同于如下命令
SET character_set_client = charset_name;
SET character_set_results = charset_name;
SET?character_set_connection?=?charset_name;
方式三:
set character set charset_name,注意區分和set names的區別。這個命令會把character_set_connection設置為和database一樣,而不是指定的charset_name。
等于如下命令。
SET character_set_client = charset_name;
SET character_set_results = charset_name;
SET collation_connection = @@collation_database;
character_set_client、character_set_connection、character_set_results深入理解
一般我們把character_set_client、character_set_connection、character_set_results這三個值設置為一樣的比較好,這樣可以避免不必要的麻煩。但是具體這幾個參數怎樣在發揮作用,下面我們通過幾個例子來看一下。注意,下面的例子可能會比較容易把人繞暈,如果不感興趣,可以不看。上面講解的關于字符集和校對規則的知識應該可以應對日常開發了。
character_set_results參數作用示例
這里我們可以做個試驗加深理解,我們先試驗一下character_set_results。
我們建一個表,字符集設置為utf8,在表中插入一行數據。注意我這里插入數據是用的mysql 連接工具datagrip,沒有在cmd窗口中用mysql命令行,因為用命令行可能會影響后面的分析結果。
create table demo (id int auto_increment,name varchar(20) null,constraint demo_pkprimary key (id) ) character set utf8; insert into demo(name) value ('gitcat熊');然后我們打開cmd窗口,連接mysql服務器。可以看到,連接好后,默認的character_set_client、character_set_connection、character_set_results都是gbk,這是因為windows系統默認的編碼是gbk。
然后我們查詢數據看看。可以看到中文可以正常顯示。這是因為mysql server端demo表的編碼是utf8,但是character_set_results配置的是gbk,所以mysql在返回數據之前把查出的數據轉成了gbk,又因為我們cmd窗口是以gbk編碼的,所以就正常顯示出了數據。?
我們把character_set_results設置成utf8再看看。可以看到顯示出問題了。因為我們告訴mysql server?character_set_results=utf8,所以mysql就會把查詢結果轉成utf8返回給我們,但是我們的cmd窗口是gbk編碼的,窗口會按照gbk解碼數據顯示,自然就顯示出問題了。?
我們可以驗證一下。熊的utf8編碼為E7868A,我們把這個編碼轉成gbk看看,因為gbk是兩個字節為單位編碼,所以我們先查詢一下gbk編碼E786對應的字是什么,從下圖可以看到正好是我們返回的select結果的第一個漢字“鐔”,然后再查一下8A,查不到可顯示的字符,因為GBK的編碼范圍是在8140-FEFE,所以返回結果顯示了個“□”。?
接下來我們把當前cmd窗口修改成用utf8編碼顯示,設置方式網上有很多,就是在cmd窗口執行一下命令 chcp 65001,會打開一個新的窗口,我們在新窗口重新連接mysql,可以看到,連接后的默認編碼已經變成utf8了。
這時我們再查詢一下,可以看到可以正常顯示。
這時如果我們把character_set_results設置成gbk,反而不能正常顯示了。因為我設置character_set_results=gbk,mysql server就會認為你要把查詢結果轉成gbk,mysql?server好心把結果給你轉成gbk了,結果你的窗口又以utf8解碼來顯示,當然就出問題了。 熊的gbk編碼是D0DC,Dü對應的unicode,正好也是D0和DC,印證了mysql服務器確實把熊這個字轉成gbk編碼傳給我們了。
細心的網友可能會發現,這里有個問題,就是為什么cmd窗口是以unicode解碼的,而不是utf8?因為我們返回的數據格式不符合utf8規則!
utf8編碼規則如下,可以看到,不是隨便給個2字節或者3字節的編碼,都能用utf8來解析出字符。所以猜測cmd窗口對于不能用utf8解析的編碼,只能以unicode來解碼了。如果我們返回的編碼正好是符合utf8規則的,那么cmd窗口就會以utf8正確解析出結果。
Unicode符號范圍 | UTF-8編碼方式 (十六進制) | (二進制) --------------------+--------------------------------------------- 0000 0000-0000 007F | 0xxxxxxx 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 以漢字“嚴”為例,演示如何實現UTF-8編碼。 已知“嚴”的unicode是4E25(100111000100101), 根據上表,可以發現4E25處在第三行的范圍內(0000 0800-0000 FFFF), 因此“嚴”的UTF-8編碼需要三個字節,即格式是“1110xxxx 10xxxxxx 10xxxxxx”。 然后,從“嚴”的最后一個二進制位開始,依次從后向前填入格式中的x,多出的位補0。 這樣就得到了,“嚴”的UTF-8編碼是“11100100 10111000 10100101”,轉換成十六進制就是E4B8A5。真的如我們猜測的這樣嗎?我們可以繼續做試驗來看。請接著往下看,下面會更精彩!
我們可以來做個實驗驗證上面我們的猜想。還是保持我們上面的實驗環境,即cmd窗口是utf8模式,設置character_set_results=gbk,然后我們在數據表demo中插入如下一條數據,注意我插入數據用的是datagrip,不是在當前cmd窗口,因為當前cmd窗口我們修改了一些參數,直接在這里插入會影響我們分析。
insert into demo(name) values('鍦ㄥ悧');然后查詢一下結果。
?
很神奇,竟然沒有"亂碼",當然其實是亂碼了,因為和我們數據庫中存入的結果不一樣了。我們分析一下這里發生了什么。
首先mysql server從數據庫中查出"鍦ㄥ悧"這3個字,然后發現character_set_results=gbk,好吧,那mysql server就按照要求,把這3個字轉成gbk格式發送給客戶端,這3個字對應的gbk編碼是E59C(鍦)A8E5(ㄥ)9097(悧),現在cmd窗口接收到這個編碼了,要顯示在屏幕上了,因為cmd窗口設置的是utf8編碼,所以就以utf8方式來解析收到的編碼E59CA8E59097,而這一串編碼正好對應utf8中的"在嗎"。這也印證了我們之前的猜想,如果server傳回來的數據是符合utf8編碼規則的,cmd窗口就會以utf8規則幫我們解碼出數據顯示。
character_set_client、character_set_connection參數作用
接下來我們試驗一下character_set_client、character_set_connection這兩個參數的設置對數據的影響。前面說了,server把從客戶端接收的數據從character_set_client 轉成character_set_connection,比如我們發送了一條insert語句,mysql server就會以為,你發送的語句是用character_set_client編碼的,但是我要轉成character_set_connection,然后再執行插入語句。
我們還是先以默認方式連接mysql數據庫,如下。
然后在我們上面建的demo表里插入一條數據。可以看到,數據被正確地插入和讀出。因為我們當前窗口的編碼是gbk,而設置的character_set_client和character_set_connection也是gbk,所以mysql就會把我們的數據從gbk轉成gbk,再存入數據庫,當然就沒問題了。當然存入數據庫時發現表的編碼是utf8,所以其實這里又轉成utf8存入數據庫的,這一步和character_set_client和character_set_connection就沒關系了,我們先不深究。
接下來繼續試驗,這里只說一下結果,就不截圖了。
cmd窗口為gbk編碼時的情況
1. 如果cmd窗口是gbk編碼,character_set_client是gbk,character_set_connection是utf8,插入和查詢也沒問題。
2. 如果cmd窗口是gbk編碼,character_set_client是utf8,character_set_connection是utf8,插入無法插入,會報錯如下。
我們來分析一下這種情況,因為當前窗口是gbk編碼,所以傳給server的編碼是gbk的,但是因為character_set_client=utf8,server誤以為是utf8的,按照utf8來解碼,發現和utf8編碼規則不符合,所以報錯了。如果我們傳給server的gbk編碼正好也符合utf8編碼,server是不會報錯的,會插入成功,只是插入的數據不是我們預期的。比如下圖的例子,我們還是以“鍦ㄥ悧”這三個字舉例,如前所述,“鍦ㄥ悧”這三個字的gbk編碼是E59C(鍦)A8E5(ㄥ)9097(悧),而這個編碼正好也符合的utf8的編碼,server在拿到這個編碼后,按照utf8的規則解碼可以解碼成功,并且解碼出來是“在嗎”,于是就不會報錯了。
?
?3. 如果cmd窗口是gbk編碼,character_set_client是utf8,character_set_connection是gbk,這種情況可以插入數據,但是插入的數據不對。
?因為我們gbk編碼的熊字不能正確被解碼為utf8,所以在解碼為utf8時就已經亂碼了,后面所有的操作都會是亂碼的,所以數據是不對的。
cmd窗口為utf8編碼時的情況
1. 如果cmd窗口是utf8編碼,character_set_client和character_set_connection設置成utf8,毫無疑問,這種情況肯定是沒問題的,因為我們用的編碼都一樣。
2.?如果cmd窗口是utf8編碼,character_set_client=gbk和character_set_connection=utf8,結果如下,可以看到utf8編碼的熊為E7868A,E786正好對應gbk的"鐔",說明server是按照gbk解碼了utf8編碼的數據。
下面附一張在情況2這種情況下的另一個插入語句,有了前面我們的分析,你能明白結果為什么是這樣了嗎?
3.?如果cmd窗口是utf8編碼,character_set_client=gbk和character_set_connection=gbk,2次插入語句結果分別如下。
4.?如果cmd窗口是utf8編碼,character_set_client=utf8和character_set_connection=gbk,結果如下。
?情況3和情況4大家可以根據前面的分析自己分析一下結果,看看是否理解了。
總結
以上是生活随笔為你收集整理的mysql字符集和校对规则(character sets and collations)详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安装SQL 2000 企业版
- 下一篇: mysql数据库character_关于