這篇文章詳細介紹一下MySQL中的字符集和字符序相關的問題,里里外外地了解一下字符集和字符序的方方面面,同時重點說明一下開發中需要注意的問題。
文章基于MySQL 8.0,也會涉及到5.7版本。主要參考MySQL手冊:https://dev.mysql.com/doc/refman/8.0/en/
1. 太長不愛看版
- 字符集(Character Set)是字符的編碼規則,字符序(Collation)是字符的排序規則;
- 每一個字符集都包含一定范圍的字符;
- 每一個字符集都有一個或多個字符序,其中一個字符序為默認字符序;
- 每一個字符序都有一個相關聯的字符集;
- 兩個不同的字符集沒有相同的字符序;
- 相同數據使用不同的字符序排序結果可能會不同;
- MySQL支持服務(server)、數據庫(database)、表(table)、字段(column)以及字符串字面量(string literal)等多個級別的字符集與字符序的設置;
- 庫表創建以及程序中盡可能使用
utf8mb4
字符集(可支持emoji); - MySQL中的
utf8
字符集是utf8mb3
字符集的別名,避免使用; - MySQL 5.7的服務默認字符集是
latin1
,而8.0中是utf8mb4
; - 通過
SHOW CHARACTER SET
查看當前服務器所支持的字符集; - 通過
SHOW COLLATION
查看所有的字符序; - MySQL中有多個關于字符集和字符序的系統變量,可以針對全局以及當前session進行設置;
- 通過
SHOW VARIABLES LIKE "character_set%"
查看當前服務以及數據庫的字符集設置; - 使用client客戶端連接數據庫時,如果編碼有問題,可以通過
SET NAMES charset_name
來設置和數據庫一致的字符集; - 庫表內容在不同字符集之前轉換可能會發生數據丟失問題。
2. 什么是字符集與字符序
在大多數情況下,我們并不需要了解字符集與字符序,但是在涉及到不同字符集的轉換時可能會出現問題,這時了解一下相關的知識還是有幫助的。
我們首先了解一下字符集的原理。
2.1 字符與字符集
字符是我們經常接觸到的東西,比如a、中、?,以及😀等都是字符。
我們知道計算機是通過bit來存儲數據的,將人類可識別的字符轉換成計算機可存儲的形式,這個過程就是編碼;字符編碼的結果,就是內存編碼。
一個字符需要用多少個bit來存儲,那就需要知道一共有多少個字符。
所有的字符放在一起就是字符集。
顯然,由于使用范圍不同,就出現了不同的字符集。比如:
- 漢語中的所有字符構成一個字符集(也包括不是漢字的字符,比如標點符號等);
- 英語中的所有字符構成一個字符集;
- 等等
對于字符集中的每個字符來說,都有兩個屬性:
- 一個是這個字符在所屬字符集中的位置,可以叫做字符序號,或碼點(code point);
- 第二個就是這個字符在計算機中的數據表示,即內存編碼。
比如ASCII碼,只需要8個bit就可以存儲所有需要使用的字符了。
但對于漢語來說顯然是不夠的,因此漢語字符需要更多的bit,來將每個字符進行編碼。
這樣對于每個國家來說,都可能需要一個將自己使用的字符集編碼成計算機內存編碼的規則。
那么同一個內存編碼,對于不同的字符集來說就可能代表不同的字符:

比如GB18030字符集中的“地球”兩個字符的內存編碼分別是0xB5D8和0xC7F2,但這兩個內存編碼在字符集BIG5中代表的字符卻是“華⑩”。
這將對我們的程序有很大的影響。
2.2 Unicode字符集與UTF-8
為了解決不同語言編碼之間不兼容的問題,Unicode出現了。
Unicode字符集致力于為全世界每一個語言的每一個字符都有統一且唯一的編碼:

那么如何將Unicode中的字符映射到內存編碼呢?主要有UTF-8、UTF-16和UTF-32等,其中最常用的就是UTF-8。
UTF-8使用1到4個不等的字節來表示所有的字符,其中前128個字符與ASCII一致。
關于Unicdoe的詳細信息,可以參考https://home.unicode.org/
2.3 字符序
一個字符集中有多個字符,那么如何對其中的字符進行排序呢?這就是字符序。
簡單來說,字符序就是字符排序的規則集合。比如一個字符集有下面幾個字符(以及內存編碼):
字符 | 內存編碼 |
---|---|
A | 00 |
B | 01 |
a | 10 |
b | 11 |
當然我們可以直接按照A>B>a>b的規則來進行排序,這就是這個簡單字符集的一個字符序。
如果想讓小寫字母放在前面,比如a>b>A>B,這又是一種字符序。
如果還想加上大小寫無關或大小寫相關,那么排序的規則集就會有相應的編碼,這就產生了不同的字符序。
字符序主要對字符的排序有影響。
3. MySQL中的字符集與字符序
了解了字符集和字符序之后,來看看MySQL中的字符集與字符序。
3.1 MySQL中的字符集
通過下面的語句來查看MySQL中支持的字符集:
SHOW CHARACTER SET;
結果:
+----------+---------------------------------+---------------------+--------+
| Charset | Description | Default collation | Maxlen |
+----------+---------------------------------+---------------------+--------+
| armscii8 | ARMSCII-8 Armenian | armscii8_general_ci | 1 |
| ascii | US ASCII | ascii_general_ci | 1 |
| big5 | Big5 Traditional Chinese | big5_chinese_ci | 2 |
| binary | Binary pseudo charset | binary | 1 |
| cp1250 | Windows Central European | cp1250_general_ci | 1 |
| cp1251 | Windows Cyrillic | cp1251_general_ci | 1 |
| cp1256 | Windows Arabic | cp1256_general_ci | 1 |
| cp1257 | Windows Baltic | cp1257_general_ci | 1 |
| cp850 | DOS West European | cp850_general_ci | 1 |
| cp852 | DOS Central European | cp852_general_ci | 1 |
| cp866 | DOS Russian | cp866_general_ci | 1 |
| cp932 | SJIS for Windows Japanese | cp932_japanese_ci | 2 |
| dec8 | DEC West European | dec8_swedish_ci | 1 |
| eucjpms | UJIS for Windows Japanese | eucjpms_japanese_ci | 3 |
| euckr | EUC-KR Korean | euckr_korean_ci | 2 |
| gb18030 | China National Standard GB18030 | gb18030_chinese_ci | 4 |
| gb2312 | GB2312 Simplified Chinese | gb2312_chinese_ci | 2 |
| gbk | GBK Simplified Chinese | gbk_chinese_ci | 2 |
| geostd8 | GEOSTD8 Georgian | geostd8_general_ci | 1 |
| greek | ISO 8859-7 Greek | greek_general_ci | 1 |
| hebrew | ISO 8859-8 Hebrew | hebrew_general_ci | 1 |
| hp8 | HP West European | hp8_english_ci | 1 |
| keybcs2 | DOS Kamenicky Czech-Slovak | keybcs2_general_ci | 1 |
| koi8r | KOI8-R Relcom Russian | koi8r_general_ci | 1 |
| koi8u | KOI8-U Ukrainian | koi8u_general_ci | 1 |
| latin1 | cp1252 West European | latin1_swedish_ci | 1 |
| latin2 | ISO 8859-2 Central European | latin2_general_ci | 1 |
| latin5 | ISO 8859-9 Turkish | latin5_turkish_ci | 1 |
| latin7 | ISO 8859-13 Baltic | latin7_general_ci | 1 |
| macce | Mac Central European | macce_general_ci | 1 |
| macroman | Mac West European | macroman_general_ci | 1 |
| sjis | Shift-JIS Japanese | sjis_japanese_ci | 2 |
| swe7 | 7bit Swedish | swe7_swedish_ci | 1 |
| tis620 | TIS620 Thai | tis620_thai_ci | 1 |
| ucs2 | UCS-2 Unicode | ucs2_general_ci | 2 |
| ujis | EUC-JP Japanese | ujis_japanese_ci | 3 |
| utf16 | UTF-16 Unicode | utf16_general_ci | 4 |
| utf16le | UTF-16LE Unicode | utf16le_general_ci | 4 |
| utf32 | UTF-32 Unicode | utf32_general_ci | 4 |
| utf8 | UTF-8 Unicode | utf8_general_ci | 3 |
| utf8mb4 | UTF-8 Unicode | utf8mb4_0900_ai_ci | 4 |
+----------+---------------------------------+---------------------+--------+
字段含義:
- Charset: 字符集的名稱;
- Description:字符集的簡單描述;
- Default collation:該字符集的默認字符序;
- Maxlen:該字符集中字符最大存儲長度。
通過加入條件查詢一部分字符集:
SHOW CHARATER SET LIKE 'utf%';
結果展示所有Unicode字符集:
+---------+------------------+--------------------+--------+
| Charset | Description | Default collation | Maxlen |
+---------+------------------+--------------------+--------+
| utf16 | UTF-16 Unicode | utf16_general_ci | 4 |
| utf16le | UTF-16LE Unicode | utf16le_general_ci | 4 |
| utf32 | UTF-32 Unicode | utf32_general_ci | 4 |
| utf8 | UTF-8 Unicode | utf8_general_ci | 3 |
| utf8mb4 | UTF-8 Unicode | utf8mb4_0900_ai_ci | 4 |
+---------+------------------+--------------------+--------+
后面會有關于Unicode的每種字符集的詳細信息。
3.2 MySQL中的字符序
每個字符集都有一個或多個字符序,可以通過下面的語句查看所有的字符序:
SHOW COLLATION;
結果(只展示一部分):
+---------------------+----------+-----+---------+----------+---------+---------------+
| Collation | Charset | Id | Default | Compiled | Sortlen | Pad_attribute |
+---------------------+----------+-----+---------+----------+---------+---------------+
| armscii8_bin | armscii8 | 64 | | Yes | 1 | PAD SPACE |
| armscii8_general_ci | armscii8 | 32 | Yes | Yes | 1 | PAD SPACE |
| ascii_bin | ascii | 65 | | Yes | 1 | PAD SPACE |
| ascii_general_ci | ascii | 11 | Yes | Yes | 1 | PAD SPACE |
| big5_bin | big5 | 84 | | Yes | 1 | PAD SPACE |
| big5_chinese_ci | big5 | 1 | Yes | Yes | 1 | PAD SPACE |
| binary | binary | 63 | Yes | Yes | 1 | NO PAD |
| cp1250_bin | cp1250 | 66 | | Yes | 1 | PAD SPACE |
| cp1250_croatian_ci | cp1250 | 44 | | Yes | 1 | PAD SPACE |
| cp1250_czech_cs | cp1250 | 34 | | Yes | 2 | PAD SPACE |
| cp1250_general_ci | cp1250 | 26 | Yes | Yes | 1 | PAD SPACE |
| cp1250_polish_ci | cp1250 | 99 | | Yes | 1 | PAD SPACE |
| ...... | ...... | ...| ... | ... | ... | ...... |
+---------------------+----------+-----+---------+----------+---------+---------------+
字段含義如下:
- Collation:字符序名稱;
- Charset:該字符序關聯的字符集;
- Id:字符序ID;
- Default:該字符序是否是所關聯的字符集的默認字符序。比
armscii8_general_ci
就是armscii8
的默認字符序,而armscii8_bin
就不是; - Compiled:字符集是否已編譯到服務器中;
- Sortlen:這與對以字符集表示的字符串進行排序所需的內存量有關;
- Pad_attribute:這表明了字符序在比較字符串時對末尾padding的處理。
NO PAD
表明在比較字符串時,末尾的padding也會考慮進去,否則不考慮。
也可以指定條件查詢:
SHOW COLLATION WHERE Charset = 'utf8mb4';
這里查詢的就是utf8mb4
字符集的所有字符序。
每個字符序都是以該字符序所關聯的字符集為前綴的,同時還有一些有規律的后綴。
這些后綴有:
- bin:二進制;
- ci:大小寫不敏感;
- cs:大小寫敏感;
- ai:口音(Accent)不敏感;
- as:口音敏感;
- ks:假名(Kanatype)敏感。
同時有的字符序是面向某種語言的,也會在字符序名字中有所體現,比如big5_chinese_ci
。
3.3 字符集與字符序的關系
字符集與字符序的關系可以用下面的圖來表示:

- 每個字符集都有一個或多個字符序;
- 每個字符集都有一個默認的字符序;
- 每個字符序都關聯一個且只有一個字符集;
- 兩個不同的字符集沒有相同的字符序。
3.4 MySQL中的相關變量
MySQL中有一些變量用于字符集與字符序的設置。
3.4.1 字符集變量
通過下面的語句來查看與字符集相關的變量:
SHOW VARIABLES LIKE 'character_set\_%'; -- 當前會話
SHOW GLOBAL VARIABLES LIKE 'character_set\_%'; -- 全局
結果:
+--------------------------------+---------+
| Variable_name | Value |
+--------------------------------+---------+
| character_set_client | utf8mb4 |
| character_set_client_handshake | ON |
| character_set_connection | utf8mb4 |
| character_set_database | utf8mb4 |
| character_set_filesystem | binary |
| character_set_results | utf8mb4 |
| character_set_server | utf8mb4 |
| character_set_system | utf8 |
+--------------------------------+---------+
變量含義:
變量 | 含義 | 作用域 | 默認值 |
---|---|---|---|
character_set_client | 客戶端發出SQL語句的字符集 | 全局,會話 | utf8mb4 |
character_set_client_handshake | 不忽略客戶端發出的字符集信息 | 全局,會話 | ON(不忽略) |
character_set_connection | 沒有指定字符集的字符串字面值所使用的字符集 | 全局,會話 | utf8mb4 |
character_set_database | 數據庫默認使用的字符集 | 全局,會話 | utf8mb4 |
character_set_filesystem | 文件系統默認的字符集 | 全局,會話 | binary |
character_set_results | 服務器返回給客戶端的結果使用的字符集 | 全局,會話 | utf8mb4 |
character_set_server | 服務器默認的字符集 | 全局,會話 | utf8mb4 |
character_set_system | 服務器存儲元數據使用的字符集 | 全局 | utf8bm3 |
3.4.2 character_set_system
這個變量是MySQL數據庫元數據使用的字符集。
所有描述數據庫的數據都是元數據,比如表名、列名等等。
對元數據的存儲有如下幾個要求:
所有的元數據必須使用相同的字符集;
這個字符集必須包含所有語言的字符。
MySQL使用UTF-8,具體的就是utf8mb3
字符集,在MySQL中utf8
就是utf8mb3
,不過后續的版本可能會有所改變。
3.4.3 字符序變量
通過下面的語句來查看與字符序相關的變量:
SHOW VARIABLES LIKE '%collation%'; -- 當前會話
SHOW GLOBAL VARIABLES LIKE '%collation%'; -- 全局
結果:
+----------------------+--------------------+
| Variable_name | Value |
+----------------------+--------------------+
| collation_connection | utf8_general_ci |
| collation_database | utf8mb4_general_ci |
| collation_server | utf8mb4_general_ci |
+----------------------+--------------------+
變量含義:
變量 | 含義 | 作用域 |
---|---|---|
collation_connection | 沒有指定字符集的字符串字面值所使用的字符序 | 全局,會話 |
collation_database | 數據庫默認使用的字符序 | 全局,會話 |
collation_server | 服務器默認使用的字符序 | 全局,會話 |
3.4.3 重要變量
本文主要關注于下面幾個變量:
character_set_server
,collation_server
character_set_database
,collation_database
character_set_client
character_set_connection
,collation_connection
character_set_results
其中前前兩組涉及庫表設計時字符集與字符序的配置,后三組涉及到客戶端與服務器連接時的字符集與字符序的配置。
4. 設置字符集與字符序
MySQL中支持多種字符集與字符序,對此,MySQL能夠為我們做到:
- 使用不同字符集存儲字符串;
- 使用不同的字符序對字符串進行排序;
- 在同一個服務器中,或同一個數據庫中,甚至同一張表中使用不同的字符集或字符序;
- 對于多個級別的字符集與字符序進行設置。
4.1 服務器的設置
MySQL中,服務器有一個默認的字符集與字符序,其中:
- 5.7以后版本默認字符集是
utf8mb4
; - 5.7的默認字符序是
utf8mb4_general_ci
; - 8.0的默認字符序是
utf8mb4_0900_ai_ci
。
服務器的字符集與字符序可以通過多種方式設置:
- 服務器啟動時:
mysqld
mysqld --character-set-server=utf8mb4
mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_0900_ai_ci
這三種方式效果一樣。
- 服務運行時:設置
character_set_server
和collation_server
變量; - 還可以編譯MySQL服務時進行設置。
服務器字符集與字符序的影響:當創建數據庫時沒有指定字符集與字符序,就是用服務器的字符集與字符序。
除此之外沒有別的影響。
4.2 庫表設計中的設置
在創建庫表時,需要指定數據庫、表以及字段所使用的字符集與字符序。
如果沒有指定,MySQL有一系列規則來使用字符集與字符序的默認值。
4.2.1 數據庫的設置
在創建數據庫的時候可以指定該數據庫所使用的字符集與字符序:
CREATE DATABASE db_name CHARACTER SET latin1 COLLATE latin1_swedish_ci;
MySQL使用下面的規則來設置數據庫的字符集與字符序:
- 如果創建數據庫的時候指定了字符集與字符序,就是用這個字符集與字符序:
CREATE DATABASE db_name CHARACTER SET latin1 COLLATE latin1_swedish_ci;
這里數據庫的字符集就是latin1
,字符序就是latin1_swedish_ci
。
- 如果創建數據庫時指定了字符集而沒有指定字符序,那字符集就是這個值,而字符序就是字符集的默認字符序:
CREATE DATABASE db_name CHARACTER SET latin1;
字符集就是latin1
,字符序就是latin1
的默認字符序latin1_swedish_ci
。
- 如果創建數據庫時指定了字符序而沒有指定字符集,那么字符集就是該字符序所對應的字符集:
CREATE DATABASE db_name CHARACTER COLLATE latin1_swedish_ci;
字符序就是latin1_swedish_ci
,字符集就是這個字符序關聯的字符集latin1
。
- 如果字符集與字符序都沒有指定,那么就是用服務器默認的字符集(
character_set_server
)與字符序(collation_server
)。
對于當前數據庫所使用的字符集與字符序,可以通過查看下面兩個變量的值:
USE db_name;
SELECT @@character_set_database, @@collation_database;
這兩個變量的值有如下的影響:
- 如果創建表時沒有指定該表使用的字符集與字符序,就是用這兩個變量所對應的字符集與字符序;
LOAT DATA
語句沒有指定字符集時,服務器使用character_set_database
來解析文件中的信息。
4.2.2 表的設置
創建表時也可以制定該表所使用的字符集與字符序:
CREATE TABLE t1 ( ... )
CHARACTER SET latin1 COLLATE latin1_danish_ci;
MySQL選擇表的字符集與字符序的規則和數據庫類似,不同在于如果創建表時沒有指定字符集與字符序,就會使用變量character_set_database
和 collation_database
所指定的字符集與字符序。
4.2.3 字段的設置
表里的每個字段也可以擁有自己的字符集與字符序:
CREATE TABLE t1
(col1 VARCHAR(5)CHARACTER SET latin1COLLATE latin1_german1_ci
);
?
ALTER TABLE t1 MODIFYcol1 VARCHAR(5)CHARACTER SET latin1COLLATE latin1_swedish_ci;
與數據庫和表類似,MySQL也是按照層級來制定字符集與字符序的。
如果字段沒有指定,那么就是用表所使用的字符集與字符序。
4.2.4 小結
上面的幾個小節中關于庫表設計的字符集與字符序設置,可以用下圖來表示:

4.3 客戶端連接中的設置
當我們使用mysql
這個客戶端與MySQL服務器連接的時候,也會涉及到字符集與字符序的設置。
4.3.1 與連接有關的變量
每一個連接到服務器的客戶端都有一個對應的字符集與字符序。
涉及到的變量有:
character_set_client
character_set_connection
,collation_connection
character_set_results
這三個關于字符集的變量是這樣使用的:

- 客戶端的語句從客戶端出發時,使用的字符集是
character_set_client
; - 語句到達服務器時,服務器將語句轉換成
character_set_connection
字符集; - 服務器執行完,將結果返回給客戶端時,使用的是
character_set_results
字符集。
4.3.2 客戶端連接字符集設置
當服務器使用的字符集與客戶端連接使用的字符集不同時,可能會有問題:
MySQL [test]> select title from article where id = 2;
+-------+
| title |
+-------+
| ????? |
+-------+
這時需要將客戶端連接的字符集設置成與服務器保持一致。
SET NAMES utf8mb4;
這樣就可以了:
MySQL [test]> select title from article where id = 2;
+-----------------+
| title |
+-----------------+
| 未命名項目 |
+-----------------+
上面的語句等同于:
SET character_set_client = utf8mb4;
SET character_set_results = utf8mb4;
SET character_set_connection = utf8mb4;
4.3.3 一些限制
在設置character_set_client
變量時,下面的字符集不可用:
ucs2
utf16
utf16le
utf32
否則就會報錯:
MySQL [test]> set names utf16;
ERROR 1231 (42000): Variable 'character_set_client' can't be set to the value of 'utf16'
4.3.4 程序中連接的設置
使用數據庫的程序也是一個客戶端,在連接數據庫的時候也需要指定字符集。
Python:
conn = mysql.connect(host='127.0.0.1',user='user',passwd='passwd',db='db',charset='utf8')
Golang:
dsn := `root:root@tcp(127.0.0.1 :3306)/DB_NAME?charset=utf8mb4`
dbConn, _:= sql.Open(`mysql`, dsn)
5. MySQL對Unicode的支持
前面提到過一種包含所有語言所有字符的字符集Unicode,它的碼點分為兩個部分:
BMP(Basic Multilingual Plane): 基本多文種平面,范圍是0x0000到0xFFFF;
補充平面(Supplementary): 補充平面,范圍是0x10000到0x10FFFF。
5.1 BMP
BMP有如下的特點:
范圍是0x0000到0xFFFF,一共65535個字符;
可以編碼成變長編碼,使用1到3個字節;
也可以編碼成定長編碼,使用2個字節;
對于主要語言的大多數字符來說是足夠了。
5.2 Supplementary
補充平面有如下的特點:
范圍是0x10000到0x10FFFF;
編碼所需的字節大于BMP,需要4個字節。
5.3 MySQL的支持
MySQL中有幾個字符集支持了Unicode:
字符集 | 支持的字符 | 每個字符所需的存儲大小 | 備注 |
---|---|---|---|
utf8mb3 , utf8 | BMP | 1、2或3個字節 | MySQL 8.0中已棄用 |
ucs2 | BMP | 2個字節 | MySQL 8.0中已棄用 |
utf8mb4 | BMP和Supplementary | 1、2、3或3個字節 | 推薦使用 |
utf16 | BMP和Supplementary | 2或4個字節 | |
utf16le | BMP和Supplementary | 2或4個字節 | little-endian,和utf16 一樣 |
utf32 | BMP和Supplementary | 4個字節 |