为了便于阅读后文,这里先介绍一下Unicode标准和UTF-8编码。
Unicode标准
Unicode标准由非盈利组织Unicode联盟维护(https://home.unicode.org/),致力于整理和编码世界上大部分文字系统。Unicode标准最新版为2022年9月发布的15.0.0版本,收录超过14万字符,每个字符都被分配了独一无二的码点(数值编号)。Unicode已经成为ISO国际标准的一部分,最常见的Unicode编码格式有与ASCII兼容的UTF-8和与UCS-2兼容的UTF-16。
Unicode标准将编码空间划分为17个平面,编号从0到16,其中第0平面称为基本多文种平面(BMP,U+0000到U+FFFF),而第1到16平面被称为辅助平面(U+10000到U+10FFFF),这些平面与BMP平面一起至少需要21bit的编码空间,略少于3个字节。BMP平面的码点可以使用单个UTF-16编码单位(2字节)表示,或者使用1~3个字节的UTF-8进行编码;辅助平面的码点在UTF-8中使用4个字节进行编码,在UTF-16中使用4个字节进行编码。
UTF-8编码
出于节省空间等目的,实际上对Unicode标准进行编码有不同实现方式,Unicode的实现方式被称为Unicode转换格式(Unicode Transformation Format,简称为UTF),最常见的当属UTF-8编码,其他实现方式还包括UTF-16(字符用两个字节或四个字节表示)和UTF-32(字符用四个字节表示)等,下面给出一些示例:
字符 | Unicode码点 | UTF-8编码 |
a | 0x0061 | 0x61 (1 byte) |
ắ | 0x1EAF | 0xe1baaf (3 bytes) |
0x1D538 | 0xf09d94b8 (4 bytes) |
可以看出Unicode给字符分配的原始码点(编号)和UTF-8具体编码还是有很大不同的,这是因为UTF-8编码以8bit(1字节)为单位对Unicode字符进行变长编码,具体转换规则如下表所示(单元格内首行为16进制表示,次行为2进制表示):
Unicode码点范围(3字节可表示) | UTF-8编码 | 注释 |
0x000000 - 0x00007F 00000000 00000000 0zzzzzzz | 0x00 - 0x7F 0zzzzzzz | ASCII字符范围,1字节 |
0x000080 - 0x0007FF 00000000 00000yyy yyzzzzzz | 0xC080 - 0xDFBF 110yyyyy 10zzzzzz | 第1个字节由110开始,第2个字节由10开始,共2字节 |
0x000800 - 0x00D7FF 0x00E000 - 0x00FFFF 00000000 xxxxyyyy yyzzzzzz | 0xE08080 - 0xEFBFBF 1110xxxx 10yyyyyy 10zzzzzz | 第1个字节由1110开始,第2-3个字节由10开始,共3字节 |
0x010000 - 0x10FFFF 000wwwxx xxxxyyyy yyzzzzzz | 0xF0808080 - 0xF7BFBFBF 11110www 10xxxxxx 10yyyyyy 10zzzzzz | 第1个字节由11110开始,第2-4个字节由10开始,共4字节 |
可以看出转换规则其实很直观,就是把Unicode码点对应的数值用3个字节存下来(最多用21个bit),然后根据自己所处的范围将bit位依次填入UTF-8对应空位即可。
汉字的码点空间如下(取自https://www.zhangxinxu.com/study/201611/chinese-language-unicode-range.html),utf16对于BMP平面字符(在下表中Unicode码点仅需两字节的部分)会使用2字节编码,而utf8对于BMP平面的汉字需要使用3字节编码,非BMP平面的汉字utf16和utf8都需要4字节编码。所以如果存储字符基本都是汉字时,utf16字符集的编码长度始终优于或等于utf8字符集的编码长度,可以有效帮助减少存储空间,最多减少1/3的存储空间。
字符集 | 字数 | Unicode码点 |
20902字 | 4E00-9FA5 | |
38字 | 9FA6-9FCB | |
6582字 | 3400-4DB5 | |
42711字 | 20000-2A6D6 | |
4149字 | 2A700-2B734 | |
222字 | 2B740-2B81D | |
214字 | 2F00-2FD5 | |
115字 | 2E80-2EF3 | |
477字 | F900-FAD9 | |
542字 | 2F800-2FA1D | |
81字 | E815-E86F | |
452字 | E400-E5E8 | |
207字 | E600-E6CF | |
36字 | 31C0-31E3 | |
12字 | 2FF0-2FFB | |
22字 | 3105-3120 | |
22字 | 31A0-31BA | |
1字 | 3007 |
MySQL内支持Unicode标准的Charset
MySQL支持多种Unicode Charset:
utf8mb4:使用一到四个字节表示每个字符的Unicode字符集的UTF-8编码。
utf8mb3:使用一到三个字节表示每个字符的Unicode字符集的UTF-8编码,仅支持表示基本多文种平面(BMP)。在MySQL 8.0中,这个字符集已被deprecated,应该尽快改用utf8mb4。
utf8:utf8mb3的别名。在MySQL 8.0中,这个别名已被deprecated,应改用utf8mb4。预计在未来的版本中,utf8将成为utf8mb4的别名。
ucs2:使用两个字节表示每个字符的UCS-2编码,仅支持表示基本多文种平面(BMP)。在MySQL 8.0.28中已被deprecated,预计在未来的版本中将被移除。
utf16:使用两个或四个字节表示每个字符的UTF-16编码,类似于UCS-2,但包含了对补充辅助平面的扩展。
utf16le:类似于utf16,但是小端序而不是大端序。
utf32:使用四个字节表示每个字符的UTF-32编码。