从根上理解 MySQL 的字符集和比较规则

字符集

抽象的描述某个字符范围的编码规则(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高性能》

上一篇:在SAP Smart Business workspace里创建KPI tile的错误消息分析


下一篇:Docker+Jenkins+ 码云仓库实现 PHP 代码自动化部署