您好,登錄后才能下訂單哦!
這篇文章主要介紹“MySQL字符集和比較規則是什么”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“MySQL字符集和比較規則是什么”文章能幫助大家解決問題。
我們知道在計算機中只能存儲二進制數據,那該怎么存儲字符串呢?當然是建立字符與二進制數據的映射關系了,建立這個關系最起碼要搞清楚兩件事兒:
你要把哪些字符映射成二進制數據?
也就是界定清楚字符范圍。
怎么映射?
將一個字符映射成一個二進制數據的過程也叫做編碼
,將一個二進制數據映射到一個字符的過程叫做解碼
。
人們抽象出一個字符集
的概念來描述某個字符范圍的編碼規則。比方說我們來自定義一個名稱為xiaohaizi
的字符集,它包含的字符范圍和編碼規則如下:
包含字符'a'
、'b'
、'A'
、'B'
。
編碼規則如下:
采用1個字節編碼一個字符的形式,字符和字節的映射關系如下:
'a' -> 00000001 (十六進制:0x01) 'b' -> 00000010 (十六進制:0x02) 'A' -> 00000011 (十六進制:0x03) 'B' -> 00000100 (十六進制:0x04)
有了xiaohaizi
字符集,我們就可以用二進制形式表示一些字符串了,下邊是一些字符串用xiaohaizi
字符集編碼后的二進制表示:
'bA' -> 0000001000000011 (十六進制:0x0203) 'baB' -> 000000100000000100000100 (十六進制:0x020104) 'cd' -> 無法表示,字符集xiaohaizi不包含字符'c'和'd'
在我們確定了xiaohaizi
字符集表示字符的范圍以及編碼規則后,怎么比較兩個字符的大小呢?最容易想到的就是直接比較這兩個字符對應的二進制編碼的大小,比方說字符'a'
的編碼為0x01
,字符'b'
的編碼為0x02
,所以'a'
小于'b'
,這種簡單的比較規則也可以被稱為二進制比較規則,英文名為binary collation
。
二進制比較規則是簡單,但有時候并不符合現實需求,比如在很多場合對于英文字符我們都是不區分大小寫的,也就是說'a'
和'A'
是相等的,在這種場合下就不能簡單粗暴的使用二進制比較規則了,這時候我們可以這樣指定比較規則:
將兩個大小寫不同的字符全都轉為大寫或者小寫。
再比較這兩個字符對應的二進制數據。
這是一種稍微復雜一點點的比較規則,但是實際生活中的字符不止英文字符一種,比如我們的漢字有幾萬之多,對于某一種字符集來說,比較兩個字符大小的規則可以制定出很多種,也就是說同一種字符集可以有多種比較規則,我們稍后就要介紹各種現實生活中用的字符集以及它們的一些比較規則。
不幸的是,這個世界太大了,不同的人制定出了好多種字符集
,它們表示的字符范圍和用到的編碼規則可能都不一樣。我們看一下一些常用字符集的情況:
ASCII
字符集
共收錄128個字符,包括空格、標點符號、數字、大小寫字母和一些不可見字符。由于總共才128個字符,所以可以使用1個字節來進行編碼,我們看一些字符的編碼方式:
'L' -> 01001100(十六進制:0x4C,十進制:76) 'M' -> 01001101(十六進制:0x4D,十進制:77)
ISO 8859-1
字符集
共收錄256個字符,是在ASCII
字符集的基礎上又擴充了128個西歐常用字符(包括德法兩國的字母),也可以使用1個字節來進行編碼。這個字符集也有一個別名latin1
。
GB2312
字符集
收錄了漢字以及拉丁字母、希臘字母、日文平假名及片假名字母、俄語西里爾字母。其中收錄漢字6763個,其他文字符號682個。同時這種字符集又兼容ASCII
字符集,所以在編碼方式上顯得有些奇怪:
這種表示一個字符需要的字節數可能不同的編碼方式稱為變長編碼方式
。比方說字符串'愛u'
,其中'愛'
需要用2個字節進行編碼,編碼后的十六進制表示為0xB0AE
,'u'
需要用1個字節進行編碼,編碼后的十六進制表示為0x75
,所以拼合起來就是0xB0AE75
。
小貼士: 我們怎么區分某個字節代表一個單獨的字符還是代表某個字符的一部分呢?別忘了`ASCII`字符集只收錄128個字符,使用0~127就可以表示全部字符,所以如果某個字節是在0~127之內的,就意味著一個字節代表一個單獨的字符,否則就是兩個字節代表一個單獨的字符。
如果該字符在ASCII
字符集中,則采用1字節編碼。
否則采用2字節編碼。
GBK
字符集
GBK
字符集只是在收錄字符范圍上對GB2312
字符集作了擴充,編碼方式上兼容GB2312
。
utf8
字符集
收錄地球上能想到的所有字符,而且還在不斷擴充。這種字符集兼容ASCII
字符集,采用變長編碼方式,編碼一個字符需要使用1~4個字節,比方說這樣:
'L' -> 01001100(十六進制:0x4C) '啊' -> 111001011001010110001010(十六進制:0xE5958A)
小貼士: 其實準確的說,utf8只是Unicode字符集的一種編碼方案,Unicode字符集可以采用utf8、utf16、utf32這幾種編碼方案,utf8使用1~4個字節編碼一個字符,utf16使用2個或4個字節編碼一個字符,utf32使用4個字節編碼一個字符。更詳細的Unicode和其編碼方案的知識不是本書的重點,大家上網查查哈~ MySQL中并不區分字符集和編碼方案的概念,所以后邊嘮叨的時候把utf8、utf16、utf32都當作一種字符集對待。
對于同一個字符,不同字符集也可能有不同的編碼方式。比如對于漢字'我'
來說,ASCII
字符集中根本沒有收錄這個字符,utf8
和gb2312
字符集對漢字我
的編碼方式如下:
utf8編碼:111001101000100010010001 (3個字節,十六進制表示是:0xE68891) gb2312編碼:1011000010101110 (2個字節,十六進制表示是:0xB0AE)
我們上邊說utf8
字符集表示一個字符需要使用1~4個字節,但是我們常用的一些字符使用1~3個字節就可以表示了。而在MySQL
中字符集表示一個字符所用最大字節長度在某些方面會影響系統的存儲和性能,所以設計MySQL
的大叔偷偷的定義了兩個概念:
utf8mb3
:閹割過的utf8
字符集,只使用1~3個字節表示字符。
utf8mb4
:正宗的utf8
字符集,使用1~4個字節表示字符。
有一點需要大家十分的注意,在MySQL
中utf8
是utf8mb3
的別名,所以之后在MySQL
中提到utf8
就意味著使用1~3個字節來表示一個字符,如果大家有使用4字節編碼一個字符的情況,比如存儲一些emoji表情啥的,那請使用utf8mb4
。
MySQL
支持好多好多種字符集,查看當前MySQL
中支持的字符集可以用下邊這個語句:
SHOW (CHARACTER SET|CHARSET) [LIKE 匹配的模式];
其中CHARACTER SET
和CHARSET
是同義詞,用任意一個都可以。我們查詢一下(支持的字符集太多了,我們省略了一些):
mysql> SHOW CHARSET; +----------+---------------------------------+---------------------+--------+ | Charset | Description | Default collation | Maxlen | +----------+---------------------------------+---------------------+--------+ | big5 | Big5 Traditional Chinese | big5_chinese_ci | 2 | ... | latin1 | cp1252 West European | latin1_swedish_ci | 1 | | latin2 | ISO 8859-2 Central European | latin2_general_ci | 1 | ... | ascii | US ASCII | ascii_general_ci | 1 | ... | gb2312 | GB2312 Simplified Chinese | gb2312_chinese_ci | 2 | ... | gbk | GBK Simplified Chinese | gbk_chinese_ci | 2 | | latin5 | ISO 8859-9 Turkish | latin5_turkish_ci | 1 | ... | utf8 | UTF-8 Unicode | utf8_general_ci | 3 | | ucs2 | UCS-2 Unicode | ucs2_general_ci | 2 | ... | latin7 | ISO 8859-13 Baltic | latin7_general_ci | 1 | | utf8mb4 | UTF-8 Unicode | utf8mb4_general_ci | 4 | | utf16 | UTF-16 Unicode | utf16_general_ci | 4 | | utf16le | UTF-16LE Unicode | utf16le_general_ci | 4 | ... | utf32 | UTF-32 Unicode | utf32_general_ci | 4 | | binary | Binary pseudo charset | binary | 1 | ... | gb18030 | China National Standard GB18030 | gb18030_chinese_ci | 4 | +----------+---------------------------------+---------------------+--------+ 41 rows in set (0.01 sec)
可以看到,我使用的這個MySQL
版本一共支持41
種字符集,其中的Default collation
列表示這種字符集中一種默認的比較規則
。大家注意返回結果中的最后一列Maxlen
,它代表該種字符集表示一個字符最多需要幾個字節。為了讓大家的印象更深刻,我把幾個常用到的字符集的Maxlen
列摘抄下來,大家務必記住:
字符集名稱 | Maxlen |
---|---|
ascii | 1 |
latin1 | 1 |
gb2312 | 2 |
gbk | 2 |
utf8 | 3 |
utf8mb4 | 4 |
查看MySQL
中支持的比較規則的命令如下:
SHOW COLLATION [LIKE 匹配的模式];
我們前邊說過一種字符集可能對應著若干種比較規則,MySQL
支持的字符集就已經非常多了,所以支持的比較規則更多,我們先只查看一下utf8
字符集下的比較規則:
mysql> SHOW COLLATION LIKE 'utf8\_%'; +--------------------------+---------+-----+---------+----------+---------+ | Collation | Charset | Id | Default | Compiled | Sortlen | +--------------------------+---------+-----+---------+----------+---------+ | utf8_general_ci | utf8 | 33 | Yes | Yes | 1 | | utf8_bin | utf8 | 83 | | Yes | 1 | | utf8_unicode_ci | utf8 | 192 | | Yes | 8 | | utf8_icelandic_ci | utf8 | 193 | | Yes | 8 | | utf8_latvian_ci | utf8 | 194 | | Yes | 8 | | utf8_romanian_ci | utf8 | 195 | | Yes | 8 | | utf8_slovenian_ci | utf8 | 196 | | Yes | 8 | | utf8_polish_ci | utf8 | 197 | | Yes | 8 | | utf8_estonian_ci | utf8 | 198 | | Yes | 8 | | utf8_spanish_ci | utf8 | 199 | | Yes | 8 | | utf8_swedish_ci | utf8 | 200 | | Yes | 8 | | utf8_turkish_ci | utf8 | 201 | | Yes | 8 | | utf8_czech_ci | utf8 | 202 | | Yes | 8 | | utf8_danish_ci | utf8 | 203 | | Yes | 8 | | utf8_lithuanian_ci | utf8 | 204 | | Yes | 8 | | utf8_slovak_ci | utf8 | 205 | | Yes | 8 | | utf8_spanish3_ci | utf8 | 206 | | Yes | 8 | | utf8_roman_ci | utf8 | 207 | | Yes | 8 | | utf8_persian_ci | utf8 | 208 | | Yes | 8 | | utf8_esperanto_ci | utf8 | 209 | | Yes | 8 | | utf8_hungarian_ci | utf8 | 210 | | Yes | 8 | | utf8_sinhala_ci | utf8 | 211 | | Yes | 8 | | utf8_german2_ci | utf8 | 212 | | Yes | 8 | | utf8_croatian_ci | utf8 | 213 | | Yes | 8 | | utf8_unicode_520_ci | utf8 | 214 | | Yes | 8 | | utf8_vietnamese_ci | utf8 | 215 | | Yes | 8 | | utf8_general_mysql500_ci | utf8 | 223 | | Yes | 1 | +--------------------------+---------+-----+---------+----------+---------+ 27 rows in set (0.00 sec)
這些比較規則的命名還挺有規律的,具體規律如下:
比較規則名稱以與其關聯的字符集的名稱開頭。如上圖的查詢結果的比較規則名稱都是以utf8
開頭的。
后邊緊跟著該比較規則主要作用于哪種語言,比如utf8_polish_ci
表示以波蘭語的規則比較,utf8_spanish_ci
是以西班牙語的規則比較,utf8_general_ci
是一種通用的比較規則。
名稱后綴意味著該比較規則是否區分語言中的重音、大小寫啥的,具體可以用的值如下:
后綴 | 英文釋義 | 描述 |
---|---|---|
_ai | accent insensitive | 不區分重音 |
_as | accent sensitive | 區分重音 |
_ci | case insensitive | 不區分大小寫 |
_cs | case sensitive | 區分大小寫 |
_bin | binary | 以二進制方式比較 |
比如utf8_general_ci
這個比較規則是以ci
結尾的,說明不區分大小寫。
每種字符集對應若干種比較規則,每種字符集都有一種默認的比較規則,SHOW COLLATION
的返回結果中的Default
列的值為YES
的就是該字符集的默認比較規則,比方說utf8
字符集默認的比較規則就是utf8_general_ci
。
MySQL
有4個級別的字符集和比較規則,分別是:
服務器級別
數據庫級別
表級別
列級別
我們接下來仔細看一下怎么設置和查看這幾個級別的字符集和比較規則。
服務器級別
MySQL
提供了兩個系統變量來表示服務器級別的字符集和比較規則:
系統變量 | 描述 |
---|---|
character_set_server | 服務器級別的字符集 |
collation_server | 服務器級別的比較規則 |
我們看一下這兩個系統變量的值:
mysql> SHOW VARIABLES LIKE 'character_set_server'; +----------------------+-------+ | Variable_name | Value | +----------------------+-------+ | character_set_server | utf8 | +----------------------+-------+ 1 row in set (0.00 sec) mysql> SHOW VARIABLES LIKE 'collation_server'; +------------------+-----------------+ | Variable_name | Value | +------------------+-----------------+ | collation_server | utf8_general_ci | +------------------+-----------------+ 1 row in set (0.00 sec)
可以看到在我的計算機中服務器級別默認的字符集是utf8
,默認的比較規則是utf8_general_ci
。
我們可以在啟動服務器程序時通過啟動選項或者在服務器程序運行過程中使用SET
語句修改這兩個變量的值。比如我們可以在配置文件中這樣寫:
[server] character_set_server=gbk collation_server=gbk_chinese_ci
當服務器啟動的時候讀取這個配置文件后這兩個系統變量的值便修改了。
數據庫級別
我們在創建和修改數據庫的時候可以指定該數據庫的字符集和比較規則,具體語法如下:
CREATE DATABASE 數據庫名 [[DEFAULT] CHARACTER SET 字符集名稱] [[DEFAULT] COLLATE 比較規則名稱]; ALTER DATABASE 數據庫名 [[DEFAULT] CHARACTER SET 字符集名稱] [[DEFAULT] COLLATE 比較規則名稱];
其中的DEFAULT
可以省略,并不影響語句的語義。比方說我們新創建一個名叫charset_demo_db
的數據庫,在創建的時候指定它使用的字符集為gb2312
,比較規則為gb2312_chinese_ci
:
mysql> CREATE DATABASE charset_demo_db -> CHARACTER SET gb2312 -> COLLATE gb2312_chinese_ci; Query OK, 1 row affected (0.01 sec)
如果想查看當前數據庫使用的字符集和比較規則,可以查看下面兩個系統變量的值(前提是使用USE
語句選擇當前默認數據庫,如果沒有默認數據庫,則變量與相應的服務器級系統變量具有相同的值):
系統變量 | 描述 |
---|---|
character_set_database | 當前數據庫的字符集 |
collation_database | 當前數據庫的比較規則 |
我們來查看一下剛剛創建的charset_demo_db
數據庫的字符集和比較規則:
mysql> USE charset_demo_db; Database changed mysql> SHOW VARIABLES LIKE 'character_set_database'; +------------------------+--------+ | Variable_name | Value | +------------------------+--------+ | character_set_database | gb2312 | +------------------------+--------+ 1 row in set (0.00 sec) mysql> SHOW VARIABLES LIKE 'collation_database'; +--------------------+-------------------+ | Variable_name | Value | +--------------------+-------------------+ | collation_database | gb2312_chinese_ci | +--------------------+-------------------+ 1 row in set (0.00 sec) mysql>
可以看到這個charset_demo_db
數據庫的字符集和比較規則就是我們在創建語句中指定的。需要注意的一點是: character_set_database 和 collation_database 這兩個系統變量是只讀的,我們不能通過修改這兩個變量的值而改變當前數據庫的字符集和比較規則。
數據庫的創建語句中也可以不指定字符集和比較規則,比如這樣:
CREATE DATABASE 數據庫名;
這樣的話將使用服務器級別的字符集和比較規則作為數據庫的字符集和比較規則。
表級別
我們也可以在創建和修改表的時候指定表的字符集和比較規則,語法如下:
CREATE TABLE 表名 (列的信息) [[DEFAULT] CHARACTER SET 字符集名稱] [COLLATE 比較規則名稱]] ALTER TABLE 表名 [[DEFAULT] CHARACTER SET 字符集名稱] [COLLATE 比較規則名稱]
比方說我們在剛剛創建的charset_demo_db
數據庫中創建一個名為t
的表,并指定這個表的字符集和比較規則:
mysql> CREATE TABLE t( -> col VARCHAR(10) -> ) CHARACTER SET utf8 COLLATE utf8_general_ci; Query OK, 0 rows affected (0.03 sec)
如果創建和修改表的語句中沒有指明字符集和比較規則,將使用該表所在數據庫的字符集和比較規則作為該表的字符集和比較規則。假設我們的創建表t
的語句是這么寫的:
CREATE TABLE t( col VARCHAR(10) );
因為表t
的建表語句中并沒有明確指定字符集和比較規則,則表t
的字符集和比較規則將繼承所在數據庫charset_demo_db
的字符集和比較規則,也就是gb2312
和gb2312_chinese_ci
。
列級別
需要注意的是,對于存儲字符串的列,同一個表中的不同的列也可以有不同的字符集和比較規則。我們在創建和修改列定義的時候可以指定該列的字符集和比較規則,語法如下:
CREATE TABLE 表名( 列名 字符串類型 [CHARACTER SET 字符集名稱] [COLLATE 比較規則名稱], 其他列... ); ALTER TABLE 表名 MODIFY 列名 字符串類型 [CHARACTER SET 字符集名稱] [COLLATE 比較規則名稱];
比如我們修改一下表t
中列col
的字符集和比較規則可以這么寫:
mysql> ALTER TABLE t MODIFY col VARCHAR(10) CHARACTER SET gbk COLLATE gbk_chinese_ci; Query OK, 0 rows affected (0.04 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql>
對于某個列來說,如果在創建和修改的語句中沒有指明字符集和比較規則,將使用該列所在表的字符集和比較規則作為該列的字符集和比較規則。比方說表t
的字符集是utf8
,比較規則是utf8_general_ci
,修改列col
的語句是這么寫的:
ALTER TABLE t MODIFY col VARCHAR(10);
那列col
的字符集和編碼將使用表t
的字符集和比較規則,也就是utf8
和utf8_general_ci
。
小貼士
在轉換列的字符集時需要注意,如果轉換前列中存儲的數據不能用轉換后的字符集進行表示會發生錯誤。比方說原先列使用的字符集是utf8,列中存儲了一些漢字,現在把列的字符集轉換為ascii的話就會出錯,因為ascii字符集并不能表示漢字字符。
僅修改字符集或僅修改比較規則
由于字符集和比較規則是互相有聯系的,如果我們只修改了字符集,比較規則也會跟著變化,如果只修改了比較規則,字符集也會跟著變化,具體規則如下:
只修改字符集,則比較規則將變為修改后的字符集默認的比較規則。
只修改比較規則,則字符集將變為修改后的比較規則對應的字符集。
不論哪個級別的字符集和比較規則,這兩條規則都適用,我們以服務器級別的字符集和比較規則為例來看一下詳細過程:
只修改字符集,則比較規則將變為修改后的字符集默認的比較規則。
mysql> SET character_set_server = gb2312; Query OK, 0 rows affected (0.00 sec) mysql> SHOW VARIABLES LIKE 'character_set_server'; +----------------------+--------+ | Variable_name | Value | +----------------------+--------+ | character_set_server | gb2312 | +----------------------+--------+ 1 row in set (0.00 sec) mysql> SHOW VARIABLES LIKE 'collation_server'; +------------------+-------------------+ | Variable_name | Value | +------------------+-------------------+ | collation_server | gb2312_chinese_ci | +------------------+-------------------+ 1 row in set (0.00 sec)
我們只修改了character_set_server
的值為gb2312
,collation_server
的值自動變為了gb2312_chinese_ci
。
只修改比較規則,則字符集將變為修改后的比較規則對應的字符集。
mysql> SET collation_server = utf8_general_ci; Query OK, 0 rows affected (0.00 sec) mysql> SHOW VARIABLES LIKE 'character_set_server'; +----------------------+-------+ | Variable_name | Value | +----------------------+-------+ | character_set_server | utf8 | +----------------------+-------+ 1 row in set (0.00 sec) mysql> SHOW VARIABLES LIKE 'collation_server'; +------------------+-----------------+ | Variable_name | Value | +------------------+-----------------+ | collation_server | utf8_general_ci | +------------------+-----------------+ 1 row in set (0.00 sec) mysql>
我們只修改了collation_server
的值為utf8_general_ci
,character_set_server
的值自動變為了utf8
。
各級別字符集和比較規則小結
我們介紹的這4個級別字符集和比較規則的聯系如下:
如果創建或修改列時沒有顯式的指定字符集和比較規則,則該列默認用表的字符集和比較規則
如果創建表時沒有顯式的指定字符集和比較規則,則該表默認用數據庫的字符集和比較規則
如果創建數據庫時沒有顯式的指定字符集和比較規則,則該數據庫默認用服務器的字符集和比較規則
知道了這些規則之后,對于給定的表,我們應該知道它的各個列的字符集和比較規則是什么,從而根據這個列的類型來確定存儲數據時每個列的實際數據占用的存儲空間大小了。比方說我們向表t
中插入一條記錄:
mysql> INSERT INTO t(col) VALUES('我我'); Query OK, 1 row affected (0.00 sec) mysql> SELECT * FROM t; +--------+ | s | +--------+ | 我我 | +--------+ 1 row in set (0.00 sec)
首先列col
使用的字符集是gbk
,一個字符'我'
在gbk
中的編碼為0xCED2
,占用兩個字節,兩個字符的實際數據就占用4個字節。如果把該列的字符集修改為utf8
的話,這兩個字符就實際占用6個字節啦~
編碼和解碼使用的字符集不一致的后果
說到底,字符串在計算機上的體現就是一個字節串,如果你使用不同字符集去解碼這個字節串,最后得到的結果可能讓你撓頭。
我們知道字符'我'
在utf8
字符集編碼下的字節串長這樣:0xE68891
,如果一個程序把這個字節串發送到另一個程序里,另一個程序用不同的字符集去解碼這個字節串,假設使用的是gbk
字符集來解釋這串字節,解碼過程就是這樣的:
首先看第一個字節0xE6
,它的值大于0x7F
(十進制:127),說明是兩字節編碼,繼續讀一字節后是0xE688
,然后從gbk
編碼表中查找字節為0xE688
對應的字符,發現是字符'鎴'
繼續讀一個字節0x91
,它的值也大于0x7F
,再往后讀一個字節發現木有了,所以這是半個字符。
所以0xE68891
被gbk
字符集解釋成一個字符'鎴'
和半個字符。
假設用iso-8859-1
,也就是latin1
字符集去解釋這串字節,解碼過程如下:
先讀第一個字節0xE6
,它對應的latin1
字符為?
。
再讀第二個字節0x88
,它對應的latin1
字符為?
。
再讀第三個字節0x91
,它對應的latin1
字符為‘
。
所以整串字節0xE68891
被latin1
字符集解釋后的字符串就是'??‘'
可見,如果對于同一個字符串編碼和解碼使用的字符集不一樣,會產生意想不到的結果,作為人類的我們看上去就像是產生了亂碼一樣。
字符集轉換的概念
如果接收0xE68891
這個字節串的程序按照utf8
字符集進行解碼,然后又把它按照gbk
字符集進行編碼,最后編碼后的字節串就是0xCED2
,我們把這個過程稱為字符集的轉換
,也就是字符串'我'
從utf8
字符集轉換為gbk
字符集。
MySQL中字符集的轉換
我們知道從客戶端發往服務器的請求本質上就是一個字符串,服務器向客戶端返回的結果本質上也是一個字符串,而字符串其實是使用某種字符集編碼的二進制數據。這個字符串可不是使用一種字符集的編碼方式一條道走到黑的,從發送請求到返回結果這個過程中伴隨著多次字符集的轉換,在這個過程中會用到3個系統變量,我們先把它們寫出來看一下:
系統變量 | 描述 |
---|---|
character_set_client | 服務器解碼請求時使用的字符集 |
character_set_connection | 服務器處理請求時會把請求字符串從character_set_client 轉為character_set_connection |
character_set_results | 服務器向客戶端返回數據時使用的字符集 |
這幾個系統變量在我的計算機上的默認值如下(不同操作系統的默認值可能不同):
mysql> SHOW VARIABLES LIKE 'character_set_client'; +----------------------+-------+ | Variable_name | Value | +----------------------+-------+ | character_set_client | utf8 | +----------------------+-------+ 1 row in set (0.00 sec) mysql> SHOW VARIABLES LIKE 'character_set_connection'; +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | character_set_connection | utf8 | +--------------------------+-------+ 1 row in set (0.01 sec) mysql> SHOW VARIABLES LIKE 'character_set_results'; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | character_set_results | utf8 | +-----------------------+-------+ 1 row in set (0.00 sec)
大家可以看到這幾個系統變量的值都是utf8
,為了體現出字符集在請求處理過程中的變化,我們這里特意修改一個系統變量的值:
mysql> set character_set_connection = gbk; Query OK, 0 rows affected (0.00 sec)
所以現在系統變量character_set_client
和character_set_results
的值還是utf8
,而character_set_connection
的值為gbk
。現在假設我們客戶端發送的請求是下邊這個字符串:
SELECT * FROM t WHERE s = '我';
為了方便大家理解這個過程,我們只分析字符'我'
在這個過程中字符集的轉換。
現在看一下在請求從發送到結果返回過程中字符集的變化:
客戶端發送請求所使用的字符集
一般情況下客戶端所使用的字符集和當前操作系統一致,不同操作系統使用的字符集可能不一樣,如下:
例如我在使用的macOS
操作系統時,客戶端使用的就是utf8
字符集。所以字符'我'
在發送給服務器的請求中的字節形式就是:0xE68891
小貼士: 如果你使用的是可視化工具,比如navicat之類的,這些工具可能會使用自定義的字符集來編碼發送到服務器的字符串,而不采用操作系統默認的字符集(所以在學習的時候還是盡量用黑框框哈)。
類Unix
系統使用的是utf8
Windows
使用的是gbk
服務器接收到客戶端發送來的請求其實是一串二進制的字節,它會認為這串字節采用的字符集是character_set_client
,然后把這串字節轉換為character_set_connection
字符集編碼的字符。
由于我的計算機上character_set_client
的值是utf8
,首先會按照utf8
字符集對字節串0xE68891
進行解碼,得到的字符串就是'我'
,然后按照character_set_connection
代表的字符集,也就是gbk
進行編碼,得到的結果就是字節串0xCED2
。
因為表t
的列col
采用的是gbk
字符集,與character_set_connection
一致,所以直接到列中找字節值為0xCED2
的記錄,最后找到了一條記錄。
小貼士: 如果某個列使用的字符集和character_set_connection代表的字符集不一致的話,還需要進行一次字符集轉換。
上一步驟找到的記錄中的col
列其實是一個字節串0xCED2
,col
列是采用gbk
進行編碼的,所以首先會將這個字節串使用gbk
進行解碼,得到字符串'我'
,然后再把這個字符串使用character_set_results
代表的字符集,也就是utf8
進行編碼,得到了新的字節串:0xE68891
,然后發送給客戶端。
由于客戶端是用的字符集是utf8
,所以可以順利的將0xE68891
解釋成字符我
,從而顯示到我們的顯示器上,所以我們人類也讀懂了返回的結果。
如果你讀上邊的文字有點暈,可以參照這個圖來仔細分析一下這幾個步驟:
從這個分析中我們可以得出這么幾點需要注意的地方:
服務器認為客戶端發送過來的請求是用character_set_client
編碼的。
假設你的客戶端采用的字符集和 character_set_client 不一樣的話,這就會出現意想不到的情況。比如我的客戶端使用的是utf8
字符集,如果把系統變量character_set_client
的值設置為ascii
的話,服務器可能無法理解我們發送的請求,更別談處理這個請求了。
服務器將把得到的結果集使用character_set_results
編碼后發送給客戶端。
假設你的客戶端采用的字符集和 character_set_results 不一樣的話,這就可能會出現客戶端無法解碼結果集的情況,結果就是在你的屏幕上出現亂碼。比如我的客戶端使用的是utf8
字符集,如果把系統變量character_set_results
的值設置為ascii
的話,可能會產生亂碼。
character_set_connection
只是服務器在將請求的字節串從character_set_client
轉換為character_set_connection
時使用,它是什么其實沒多重要,但是一定要注意,該字符集包含的字符范圍一定涵蓋請求中的字符,要不然會導致有的字符無法使用character_set_connection
代表的字符集進行編碼。比如你把character_set_client
設置為utf8
,把character_set_connection
設置成ascii
,那么此時你如果從客戶端發送一個漢字到服務器,那么服務器無法使用ascii
字符集來編碼這個漢字,就會向用戶發出一個警告。
知道了在MySQL
中從發送請求到返回結果過程里發生的各種字符集轉換,但是為啥要轉來轉去的呢?不暈么?
答:是的,很頭暈,所以我們通常都把 character_set_client 、character_set_connection、character_set_results 這三個系統變量設置成和客戶端使用的字符集一致的情況,這樣減少了很多無謂的字符集轉換。為了方便我們設置,MySQL
提供了一條非常簡便的語句:
SET NAMES 字符集名;
這一條語句產生的效果和我們執行這3條的效果是一樣的:
SET character_set_client = 字符集名; SET character_set_connection = 字符集名; SET character_set_results = 字符集名;
比方說我的客戶端使用的是utf8
字符集,所以需要把這幾個系統變量的值都設置為utf8
:
mysql> SET NAMES utf8; Query OK, 0 rows affected (0.00 sec) mysql> SHOW VARIABLES LIKE 'character_set_client'; +----------------------+-------+ | Variable_name | Value | +----------------------+-------+ | character_set_client | utf8 | +----------------------+-------+ 1 row in set (0.00 sec) mysql> SHOW VARIABLES LIKE 'character_set_connection'; +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | character_set_connection | utf8 | +--------------------------+-------+ 1 row in set (0.00 sec) mysql> SHOW VARIABLES LIKE 'character_set_results'; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | character_set_results | utf8 | +-----------------------+-------+ 1 row in set (0.00 sec) mysql>
另外,如果你想在啟動客戶端的時候就把character_set_client
、character_set_connection
、character_set_results
這三個系統變量的值設置成一樣的,那我們可以在啟動客戶端的時候指定一個叫default-character-set
的啟動選項,比如在配置文件里可以這么寫:
[client] default-character-set=utf8
它起到的效果和執行一遍SET NAMES utf8
是一樣一樣的,都會將那三個系統變量的值設置成utf8
。
結束了字符集的漫游,我們把視角再次聚焦到比較規則
,比較規則
的作用通常體現比較字符串大小的表達式以及對某個字符串列進行排序中,所以有時候也稱為排序規則
。比方說表t
的列col
使用的字符集是gbk
,使用的比較規則是gbk_chinese_ci
,我們向里邊插入幾條記錄:
mysql> INSERT INTO t(col) VALUES('a'), ('b'), ('A'), ('B'); Query OK, 4 rows affected (0.00 sec) Records: 4 Duplicates: 0 Warnings: 0 mysql>
我們查詢的時候按照t
列排序一下:
mysql> SELECT * FROM t ORDER BY col; +------+ | col | +------+ | a | | A | | b | | B | | 我 | +------+ 5 rows in set (0.00 sec)
可以看到在默認的比較規則gbk_chinese_ci
中是不區分大小寫的,我們現在把列col
的比較規則修改為gbk_bin
:
mysql> ALTER TABLE t MODIFY col VARCHAR(10) COLLATE gbk_bin; Query OK, 5 rows affected (0.02 sec) Records: 5 Duplicates: 0 Warnings: 0
由于gbk_bin
是直接比較字符的編碼,所以是區分大小寫的,我們再看一下排序后的查詢結果:
mysql> SELECT * FROM t ORDER BY s; +------+ | s | +------+ | A | | B | | a | | b | | 我 | +------+ 5 rows in set (0.00 sec) mysql>
所以如果以后大家在對字符串做比較或者對某個字符串列做排序操作時沒有得到想象中的結果,需要思考一下是不是比較規則
的問題~
小貼士: 列`col`中各個字符在使用gbk字符集編碼后對應的數字如下: 'A' -> 65 (十進制) 'B' -> 66 (十進制) 'a' -> 97 (十進制) 'b' -> 98 (十進制) '我' -> 25105 (十進制)
字符集
指的是某個字符范圍的編碼規則。
比較規則
是針對某個字符集中的字符比較大小的一種規則。
在MySQL
中,一個字符集可以有若干種比較規則,其中有一個默認的比較規則,一個比較規則必須對應一個字符集。
查看MySQL
中查看支持的字符集和比較規則的語句如下:
SHOW (CHARACTER SET|CHARSET) [LIKE 匹配的模式]; SHOW COLLATION [LIKE 匹配的模式];
MySQL有四個級別的字符集和比較規則
服務器級別
character_set_server
表示服務器級別的字符集,collation_server
表示服務器級別的比較規則。
數據庫級別
創建和修改數據庫時可以指定字符集和比較規則:
CREATE DATABASE 數據庫名 [[DEFAULT] CHARACTER SET 字符集名稱] [[DEFAULT] COLLATE 比較規則名稱]; ALTER DATABASE 數據庫名 [[DEFAULT] CHARACTER SET 字符集名稱] [[DEFAULT] COLLATE 比較規則名稱];
character_set_database
表示當前數據庫的字符集,collation_database
表示當前默認數據庫的比較規則,這兩個系統變量是只讀的,不能修改。如果沒有指定當前默認數據庫,則變量與相應的服務器級系統變量具有相同的值。
表級別
創建和修改表的時候指定表的字符集和比較規則:
CREATE TABLE 表名 (列的信息) [[DEFAULT] CHARACTER SET 字符集名稱] [COLLATE 比較規則名稱]]; ALTER TABLE 表名 [[DEFAULT] CHARACTER SET 字符集名稱] [COLLATE 比較規則名稱];
列級別
創建和修改列定義的時候可以指定該列的字符集和比較規則:
CREATE TABLE 表名( 列名 字符串類型 [CHARACTER SET 字符集名稱] [COLLATE 比較規則名稱], 其他列... ); ALTER TABLE 表名 MODIFY 列名 字符串類型 [CHARACTER SET 字符集名稱] [COLLATE 比較規則名稱];
從發送請求到接收結果過程中發生的字符集轉換:
在這個過程中各個系統變量的含義如下:
系統變量 | 描述 |
---|---|
character_set_client | 服務器解碼請求時使用的字符集 |
character_set_connection | 服務器處理請求時會把請求字符串從character_set_client 轉為character_set_connection |
character_set_results | 服務器向客戶端返回數據時使用的字符集 |
一般情況下要使用保持這三個變量的值和客戶端使用的字符集相同。
客戶端使用操作系統的字符集編碼請求字符串,向服務器發送的是經過編碼的一個字節串。
服務器將客戶端發送來的字節串采用character_set_client
代表的字符集進行解碼,將解碼后的字符串再按照character_set_connection
代表的字符集進行編碼。
如果character_set_connection
代表的字符集和具體操作的列使用的字符集一致,則直接進行相應操作,否則的話需要將請求中的字符串從character_set_connection
代表的字符集轉換為具體操作的列使用的字符集之后再進行操作。
將從某個列獲取到的字節串從該列使用的字符集轉換為character_set_results
代表的字符集后發送到客戶端。
客戶端使用操作系統的字符集解析收到的結果集字節串。
比較規則的作用通常體現比較字符串大小的表達式以及對某個字符串列進行排序中。
關于“MySQL字符集和比較規則是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。