從根上理解 MySQL 的字符集和比較規則

zhangdeTalk發表於2020-01-31

字符集

抽象的描述某個字元範圍的編碼規則(charset),比如ASCII、GBK、UTF8等。

編碼:把一個字元對映成一個二進位制資料的過程
解碼:將一個二進位制資料對映成一個字元的過程

比較規則

是針對某個字符集中的字元比較大小的一種規則(collation),比如gbk_chinese_ci、utf8_general_ci等。

一些重要的字符集

ASCII

一共128個字元,包括空格、標點符號、數字、大小寫字母和一些不可見字元

1個位元組編碼一個字元

例如:L
01001100(二進位制)
0x4c(十六進位制)
76(十進位制)

ISO 8859-1

一共256個字元,在ASCII基礎擴增128個西歐常用字元

1個位元組編碼一個字元

別名又叫 Latin1

GB2312

收錄漢字以及拉丁字母、希臘字母、日文字母、俄語字母,相容ASCII

漢字6763個,其它文字元號682個

1~2個位元組編碼一個字元

編碼規則:變長編碼方式(表示一個字元需要的位元組數可能不同)
1. 如果該字元在ASCII範圍內,則採用1位元組編碼1個字元
2. 否則採用2個位元組編碼1個字元
例如:i你
01101001 1100010011100011(二進位制)
0x69 CE3(十六進位制)
105 19227(十進位制)

GBK

對GB2312進行擴充,相容GB2312

1~2個位元組編碼一個字元

例如:'我'
1100111011010010(二進位制)
CE‌D2(十六進位制)

UTF8

收錄地球上能想到的所有字元,而且還在不斷擴充,相容ASCII

變長編碼方式,編碼1個字元需要1~4個位元組

例如:
'L'
01001100(二進位制)
0x4C(十六進位制)

'我'
111001101000100010010001(二進位制)
0xE6‌88‌91(十六進位制)

MySQL中支援的字符集和排序規則

utf8和utf8mb4

utf8:utf8mb3 閹割過的utf8字符集,只使用1~3個位元組編碼字元。(預設)

utf8mb4:正宗的utf8字符集,使用1~4個位元組編碼字元。(可以儲存emoji表情)

字符集的檢視

SHOW (CHARACTER SET|CHARSET) [LIKE 匹配的模式];
mysql> show character set;
+----------+---------------------------------+---------------------+--------+
| Charset  | Description                     | Default collation   | Maxlen |
+----------+---------------------------------+---------------------+--------+
| big5     | Big5 Traditional Chinese        | big5_chinese_ci     |      2 |
| dec8     | DEC West European               | dec8_swedish_ci     |      1 |
| cp850    | DOS West European               | cp850_general_ci    |      1 |
| hp8      | HP West European                | hp8_english_ci      |      1 |
| koi8r    | KOI8-R Relcom Russian           | koi8r_general_ci    |      1 |
| latin1   | cp1252 West European            | latin1_swedish_ci   |      1 |
| latin2   | ISO 8859-2 Central European     | latin2_general_ci   |      1 |
| swe7     | 7bit Swedish                    | swe7_swedish_ci     |      1 |
| ascii    | US ASCII                        | ascii_general_ci    |      1 |
| ujis     | EUC-JP Japanese                 | ujis_japanese_ci    |      3 |
| sjis     | Shift-JIS Japanese              | sjis_japanese_ci    |      2 |
| hebrew   | ISO 8859-8 Hebrew               | hebrew_general_ci   |      1 |
| tis620   | TIS620 Thai                     | tis620_thai_ci      |      1 |
| euckr    | EUC-KR Korean                   | euckr_korean_ci     |      2 |
| koi8u    | KOI8-U Ukrainian                | koi8u_general_ci    |      1 |
| gb2312   | GB2312 Simplified Chinese       | gb2312_chinese_ci   |      2 |
| greek    | ISO 8859-7 Greek                | greek_general_ci    |      1 |
| cp1250   | Windows Central European        | cp1250_general_ci   |      1 |
| gbk      | GBK Simplified Chinese          | gbk_chinese_ci      |      2 |
| latin5   | ISO 8859-9 Turkish              | latin5_turkish_ci   |      1 |
| armscii8 | ARMSCII-8 Armenian              | armscii8_general_ci |      1 |
| utf8     | UTF-8 Unicode                   | utf8_general_ci     |      3 |
| ucs2     | UCS-2 Unicode                   | ucs2_general_ci     |      2 |
| cp866    | DOS Russian                     | cp866_general_ci    |      1 |
| keybcs2  | DOS Kamenicky Czech-Slovak      | keybcs2_general_ci  |      1 |
| macce    | Mac Central European            | macce_general_ci    |      1 |
| macroman | Mac West European               | macroman_general_ci |      1 |
| cp852    | DOS Central European            | cp852_general_ci    |      1 |
| latin7   | ISO 8859-13 Baltic              | latin7_general_ci   |      1 |
| utf8mb4  | UTF-8 Unicode                   | utf8mb4_general_ci  |      4 |
| cp1251   | Windows Cyrillic                | cp1251_general_ci   |      1 |
| utf16    | UTF-16 Unicode                  | utf16_general_ci    |      4 |
| utf16le  | UTF-16LE Unicode                | utf16le_general_ci  |      4 |
| cp1256   | Windows Arabic                  | cp1256_general_ci   |      1 |
| cp1257   | Windows Baltic                  | cp1257_general_ci   |      1 |
| utf32    | UTF-32 Unicode                  | utf32_general_ci    |      4 |
| binary   | Binary pseudo charset           | binary              |      1 |
| geostd8  | GEOSTD8 Georgian                | geostd8_general_ci  |      1 |
| cp932    | SJIS for Windows Japanese       | cp932_japanese_ci   |      2 |
| eucjpms  | UJIS for Windows Japanese       | eucjpms_japanese_ci |      3 |
| gb18030  | China National Standard GB18030 | gb18030_chinese_ci  |      4 |
+----------+---------------------------------+---------------------+--------+
41 rows in set (0.00 sec)

比較規則的檢視

SHOW COLLATION [LIKE 匹配的模式];
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_spanish2_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 |
| utf8mb4_general_ci       | utf8mb4 |  45 | Yes     | Yes      |       1 |
| utf8mb4_bin              | utf8mb4 |  46 |         | Yes      |       1 |
| utf8mb4_unicode_ci       | utf8mb4 | 224 |         | Yes      |       8 |
| utf8mb4_icelandic_ci     | utf8mb4 | 225 |         | Yes      |       8 |
| utf8mb4_latvian_ci       | utf8mb4 | 226 |         | Yes      |       8 |
| utf8mb4_romanian_ci      | utf8mb4 | 227 |         | Yes      |       8 |
| utf8mb4_slovenian_ci     | utf8mb4 | 228 |         | Yes      |       8 |
| utf8mb4_polish_ci        | utf8mb4 | 229 |         | Yes      |       8 |
| utf8mb4_estonian_ci      | utf8mb4 | 230 |         | Yes      |       8 |
| utf8mb4_spanish_ci       | utf8mb4 | 231 |         | Yes      |       8 |
| utf8mb4_swedish_ci       | utf8mb4 | 232 |         | Yes      |       8 |
| utf8mb4_turkish_ci       | utf8mb4 | 233 |         | Yes      |       8 |
| utf8mb4_czech_ci         | utf8mb4 | 234 |         | Yes      |       8 |
| utf8mb4_danish_ci        | utf8mb4 | 235 |         | Yes      |       8 |
| utf8mb4_lithuanian_ci    | utf8mb4 | 236 |         | Yes      |       8 |
| utf8mb4_slovak_ci        | utf8mb4 | 237 |         | Yes      |       8 |
| utf8mb4_spanish2_ci      | utf8mb4 | 238 |         | Yes      |       8 |
| utf8mb4_roman_ci         | utf8mb4 | 239 |         | Yes      |       8 |
| utf8mb4_persian_ci       | utf8mb4 | 240 |         | Yes      |       8 |
| utf8mb4_esperanto_ci     | utf8mb4 | 241 |         | Yes      |       8 |
| utf8mb4_hungarian_ci     | utf8mb4 | 242 |         | Yes      |       8 |
| utf8mb4_sinhala_ci       | utf8mb4 | 243 |         | Yes      |       8 |
| utf8mb4_german2_ci       | utf8mb4 | 244 |         | Yes      |       8 |
| utf8mb4_croatian_ci      | utf8mb4 | 245 |         | Yes      |       8 |
| utf8mb4_unicode_520_ci   | utf8mb4 | 246 |         | Yes      |       8 |
| utf8mb4_vietnamese_ci    | utf8mb4 | 247 |         | Yes      |       8 |
+--------------------------+---------+-----+---------+----------+---------+
53 rows in set (0.00 sec)
字尾 英文解釋 描述
_ai accent insensitive 不區分重音
_as     accent sensitive     區分重音   
_ci case insensitive 不區分大小寫
_cs     case sensitive     區分大小寫   
_bin binary 以二進位制方式比較

後邊緊跟著該比較規則主要作用於哪種語言,比如utf8_polish_ci表示以波蘭語的規則比較,utf8_spanish_ci是以西班牙語的規則比較,utf8_general_ci是一種通用的比較規則。

字符集和比較規則的應用

伺服器級別

檢視

SHOW VARIABLES LIKE 'character_set_server';//伺服器級別字符集
SHOW VARIABLES LIKE 'collation_server';//伺服器級別比較規則
mysql> show variables like 'character_set_server';
+----------------------+-------+
| Variable_name        | Value |
+----------------------+-------+
| character_set_server | gbk   |
+----------------------+-------+

mysql> show variables like 'collation_server';
+------------------+----------------+
| Variable_name | Value |
+------------------+----------------+
| collation_server | gbk_chinese_ci |
+------------------+----------------+

設定

通過配置檔案永久設定(當然也可以通過命令列進行當前會話設定)
[server] 
character_set_server=gbk 
collation_server=gbk_chinese_ci

資料庫級別

檢視

use 資料庫;//需先選擇對應的資料庫
SHOW VARIABLES LIKE 'character_set_database';//資料庫級別的字符集
SHOW VARIABLES LIKE 'collation_database';//資料庫級別的比較規則
mysql> use school;
Database changed
mysql> show variables like 'character_set_database';
+------------------------+--------+
| Variable_name          | Value  |
+------------------------+--------+
| character_set_database | latin1 |
+------------------------+--------+

mysql> show variables like 'collation_database';
+--------------------+-------------------+
| Variable_name | Value  |
+--------------------+-------------------+
| collation_database | latin1_swedish_ci |
+--------------------+-------------------+

設定

CREATE DATABASE 資料庫名
[[DEFAULT] CHARACTER SET 字符集名稱]
[[DEFAULT] COLLATE 比較規則名稱];

ALTER DATABASE 資料庫名 
[[DEFAULT] CHARACTER SET 字符集名稱] 
[[DEFAULT] COLLATE 比較規則名稱];
備註:
character_set_database 和 collation_database 這兩個系統變數是隻讀的,我們不能通過修改這兩個變數的值而改變當前資料庫的字符集和比較規則。
CREATE DATABASE 資料庫名;
如在新建資料庫不設定字符集和比較規則的話,將使用伺服器級別的字符集和比較規則作為資料庫的字符集和比較規則。

表級別

檢視

show create table 表名 \G

設定

CREATE TABLE 表名 (列的資訊)
[[DEFAULT] CHARACTER SET 字符集名稱]
[COLLATE 比較規則名稱]]

ALTER TABLE 表名 
[[DEFAULT] CHARACTER SET 字符集名稱] 
[COLLATE 比較規則名稱]

如在新建資料表不設定字符集和比較規則的話,將使用資料庫級別的字符集和比較規則作為資料庫的字符集和比較規則。

列級別

檢視

show create table 表名 \G

設定

CREATE TABLE 表名(
    列名 字串型別 [CHARACTER SET 字符集名稱] [COLLATE 比較規則名稱],
    其他列...
);

ALTER TABLE 表名 MODIFY 列名 字串型別 [CHARACTER SET 字符集名稱] [COLLATE 比較規則名稱];

如在新建資料表列不設定字符集和比較規則的話,將使用資料表級別的字符集和比較規則作為資料庫的字符集和比較規則。

備註:

在轉換列的字符集時需要注意,如果轉換前列中儲存的資料不能用轉換後的字符集進行表示會發生錯誤。比方說原先列使用的字符集是utf8,列中儲存了一些漢字,現在把列的字符集轉換為ascii的話就會出錯,因為ascii字符集並不能表示漢字字元。

僅修改字符集或僅修改比較規則

* 只修改字符集,則比較規則將變為修改後的字符集預設的比較規則
* 只修改比較規則,則字符集將變為修改後的比較規則對應的字符集
例如:
mysql> show variables like 'character_set_server';
+----------------------+-------+
| Variable_name | Value |
+----------------------+-------+
| character_set_server | gbk  |
+----------------------+-------+
1 row in set (0.01 sec)
mysql> show variables like 'collation_server';
+------------------+----------------+
| Variable_name | Value |
+------------------+----------------+
| collation_server | gbk_chinese_ci |
+------------------+----------------+
1 row in set (0.01 sec)
mysql> set character_set_server=utf8;
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.01 sec)
mysql> show variables like 'collation_server';
+------------------+-----------------+
| Variable_name | Value  |
+------------------+-----------------+
| collation_server | utf8_general_ci |
+------------------+-----------------+
1 row in set (0.01 sec)

客戶端和伺服器通訊中的字符集

檢視

SHOW VARIABLES LIKE 'character_set_client';//伺服器解碼請求時使用的字符集
SHOW VARIABLES LIKE 'character_set_connection';//伺服器處理請求時會把請求字串從character_set_client轉為character_set_connection
SHOW VARIABLES LIKE 'character_set_results';//伺服器向客戶端返回資料時使用的字符集

流程

1、客戶端使用作業系統的字符集編碼請求字串,向伺服器傳送的是經過編碼的一個位元組串

2、伺服器將客戶端傳送來的位元組串採用character_set_client代表的字符集進行解碼,將解碼後的字串再按照character_set_connection代表的字符集進行編碼

3、如果character_set_connection代表的字符集和具體操作的列使用的字符集一致,則直接進行相應操作,否則的話需要將請求中的字串從character_set_connection代表的字符集轉換為具體操作的列使用的字符集之後再進行操作

4、將從某個列獲取到的位元組串從該列使用的字符集轉換為character_set_results代表的字符集後傳送到客戶端。

5、客戶端使用作業系統的字符集解析收到的結果集位元組串。

從根上理解 MySQL 的字符集和比較規則

設定

方法一:(當前會話有效)
SET NAMES 字符集名;

方法二:(當前會話有效)
SET character_set_client = 字符集名; 
SET character_set_connection = 字符集名; 
SET character_set_results = 字符集名;

方法三:配置檔案永久生效
[client] default-character-set=utf8

亂碼情況

mysql> show variables like 'character_set_client';
+----------------------+-------+
| Variable_name        | Value |
+----------------------+-------+
| character_set_client | gbk   |
+----------------------+-------+

mysql> show variables like 'character_set_connection';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| character_set_connection | utf8 |
+--------------------------+-------+

mysql> show variables like 'character_set_results';
+-----------------------+-------+
| Variable_name  | Value |
+-----------------------+-------+
| character_set_results | utf8 |
+-----------------------+-------+

mysql> show create table student \G
*************************** 1. row ***************************
 Table: student
Create Table: CREATE TABLE `student` (
 `id` int(10) NOT NULL AUTO_INCREMENT,
 `name` varchar(30) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4

mysql> insert into student(name) values('張胖');
Query OK, 1 row affected (0.00 sec)
mysql> select * from student where name = '張胖';//顯示結果出現亂碼
+----+-----------+
| id | name |
+----+-----------+
| 3 | 寮犺儢 |
+----+-----------+
1 row in set (0.00 sec)

出現亂碼原因是character_set_client設定的字符集與資料的字符集不一致導致的

解決辦法:把character_set_client設定成utf8即可

備註:我們通常都把 character_set_client、character_set_connection、character_set_results 這三個系統變數設定成和客戶端使用的字符集一致的情況,這樣減少了很多無謂的字符集轉換,也可以避免亂碼情況的發生。

參考:掘金小冊《MySQL 是怎樣執行的:從根兒上理解 MySQL》

書籍《MySQL高效能》

本作品採用《CC 協議》,轉載必須註明作者和本文連結

阿德

相關文章