SQL调优 头图.png

此篇介绍 MySQL 字符集、排序规则、相关的元数据、参数等设置以及使用情况。

概念

  • 字符集的内容包含:字符集(character set)和排序规则(collation rule)
  • 每种字符集可对应一到多个排序规则,每种排序规则对应一种字符集
  • 字符集是一套字符与一套编码的映射集合,像这样:
字符 编码
A 0
B 1
  • 排序规则是字符集内用来比较每个字符的一套规则,也就是字符的排序方式

    比如要比较字符 A 和 B 的大小,最简单直观的方法就是对比他们对应的编码。显然编码 0 < 1,这种规则下 A < B。那么类似这样的规则集合就是排序规则。单字节字符编码如此,多字节的编码排序也以此类推。

那么接下来我来详细介绍下字符集相关的介绍以及使用场景。

一、字符编码的分类

1、 ASCII

用途:用来映射简单的单字节字符,比如大小写英文字母,阿拉伯数字,及常用的标点符、运算符、控制字符等。

编码范围: U+0000 - U+007F

注意:对于用这类字符的场景够用了,但是却无法表达比如汉字,日文等编码。

2、UNICODE

用途:用来映射包含 ASCII 以内的其他的所有字符。

编码范围: U+0000 - U+10FFFF

注意:ASCII 是 UNICODE 的子集,ASCII 编码的字符可以无损转换为 UNICODE 编码的字符。

二、MySQL 常用字符集

1、Latin1

Latin1 是 cp1252 或者 ISO-8859-1 的别名。ISO-8859-1 编码是单字节编码,向下兼容 ASCII。

编码范围:U+0000 - U+00FF

范围 字符内容 说明
U+0000 - U+007F 单字节字符 与 ASCII 完全一致
U+0080 - U+009F 控制字符
U+0080 - U+00FF 文字符号

ISO-8859-1 收录的字符除 ASCII 收录的字符外,还包括西欧语言、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号。

单字节内的空间都被 ISO-8859-1 编码占用,所以能够用 ISO-8859-1 编码存储、传输其他任何编码的字节流。

比如把一个 Utf8mb4 的编码或者 GBK 的编码存入 Latin1,不会有任何问题。因为 Latin1 保留了原始的字节流,这也就是 MySQL 长期以来把 Latin1 做默认字符集的原因。

但是由于 Latin1 对任何字符都存放字节流,造成了字符个数的浪费。

比如:

CHAR(10) CHARACTER  SET LATIN1;
CHAR(10) CHARACTER SET  UTF8;

该字段中存储字符个数 UTF8 是 Latin1 的三倍!!!

2、GB18030

GB18030 是中国官方标准字符集,向前兼容 GBK、GB2312,是这两个的超集。用 1、2、4 个字节分别表示一个符号。比如对一般中文字符,默认是用两个字节编码存储。Windows 系统,默认用的就是 GB18030。

若只是存储中文字符,那 GB18030 最佳。

原因有两点:

1.占用空间小,比如比 UTF8 小。

2.存储的汉字根据拼音来排序,检索快。

3、UTF8

UTF8 是 Unicode 的编码实现,可以存储 UNICODE 编码对应的任何字符, 这也是使用最多的一种编码。最大的特点就是变长的编码方式,用 1 到 4 个字节表示一个符号,可以根据不同的符号编码字节长度。

字母或数字用 1 字节,汉字用 3 字节,emoji 表情符号用 4 字节。UTF8 字符集目前是使用最广泛的。

注意!常说的 UTF8 是 UTF8MB3 的别名,UTF8MB3 是 UTF8MB4 的子集,UTF8MB4 才是真正的 4 字节 UTF8 字符集!

UTF8MB3 表示最大支持 3 个字节存储字符,UTF8MB4 表示最大 4 个字节存储字符。根据实际需要和未来展望,MySQL 8.0 已经默认用 UTF8MB4 基础字符集。

三、查看字符集

基本上现在的字符集 MySQL 都支持,查看 MySQL 支持的字符集列表, 有两种方法:

1、SQL 语句

-- 过滤指定字符集

mysql> show character set where description like '%unicode%' and charset like 'utf8%';
+---------+---------------+--------------------+--------+
| Charset | Description   | Default collation  | Maxlen |
+---------+---------------+--------------------+--------+
| utf8    | UTF-8 Unicode | utf8_general_ci    |      3 |
| utf8mb4 | UTF-8 Unicode | utf8mb4_0900_ai_ci |      4 |
+---------+---------------+--------------------+--------+
2 rows in set (0.01 sec)

2、查看元数据字典表

-- 过滤指定字符集

mysql> select * from information_schema.character_sets where description like '%Unicode%' and character_set_name like 'utf8%';
+--------------------+----------------------+---------------+--------+
| CHARACTER_SET_NAME | DEFAULT_COLLATE_NAME | DESCRIPTION   | MAXLEN |
+--------------------+----------------------+---------------+--------+
| utf8               | utf8_general_ci      | UTF-8 Unicode |      3 |
| utf8mb4            | utf8mb4_0900_ai_ci   | UTF-8 Unicode |      4 |
+--------------------+----------------------+---------------+--------+
2 rows in set (0.00 sec)

查询结果:

1.第一列代表字符集名字;
2.第二列表示字符集排序规则;
3.第三列表示字符集描述;
4.第四列表示字符集编码的最大字节数。

四、查看排序规则

1、SQL 语句

-- 检索出字符集为 utf8mb4 的默认排序规则
mysql> show collation where charset = 'utf8mb4' and `default` = 'yes';
+--------------------+---------+-----+---------+----------+---------+---------------+
| Collation          | Charset | Id  | Default | Compiled | Sortlen | Pad_attribute |
+--------------------+---------+-----+---------+----------+---------+---------------+
| utf8mb4_0900_ai_ci | utf8mb4 | 255 | Yes     | Yes      |       0 | NO PAD        |
+--------------------+---------+-----+---------+----------+---------+---------------+
1 row in set (0.00 sec)

2、查看元数据字典表

-- 检索出排序规则包含 utf8mb4%_bin 的
mysql> select * from information_schema.collations where collation_name like 'utf8mb4%_bin';
+------------------+--------------------+-----+------------+-------------+---------+---------------+
| COLLATION_NAME   | CHARACTER_SET_NAME | ID  | IS_DEFAULT | IS_COMPILED | SORTLEN | PAD_ATTRIBUTE |
+------------------+--------------------+-----+------------+-------------+---------+---------------+
| utf8mb4_bin      | utf8mb4            |  46 |            | Yes         |       1 | PAD SPACE     |
| utf8mb4_0900_bin | utf8mb4            | 309 |            | Yes         |       1 | NO PAD        |
+------------------+--------------------+-----+------------+-------------+---------+---------------+
2 rows in set (0.01 sec)

查询结果:

1.第一列代表排序规则名称;
2.第二列表示对应字符集名称;
3.第四列表示是否为默认排序规则;
4.最后一列表示排序时是否需要比较字符后面的空格。

3、NO PAD vs PAD SPACE

NO PAD(处理)

如果字符后面有空格,那就把空格当作一个字符处理。也就是在对比的时候不会忽视空格的存在。

PAD SPACE(忽略)

表示如果字符后面有空格,可以忽略空格来比较。也就是空格可有可无。

示例:

-- 排序规则 utf8mb4_bin 属性为 PAD SPACE。 
mysql> SET NAMES utf8mb4 COLLATE utf8mb4_bin;
Query OK, 0 rows affected (0.00 sec)

mysql> set @a='mysql      ';
Query OK, 0 rows affected (0.00 sec)

mysql> set @b='mysql';
Query OK, 0 rows affected (0.00 sec)

-- 验证变量 @a 和变量 @b 是否相同,结果为相同,也就是尾部的空格不参与比较。
mysql> select if(@a=@b,' @a 和 @b 相同','@a 和 @b 不同') as '比较结果';
+-------------------+
| 比较结果          |
+-------------------+
|  @a 和 @b 相同    |
+-------------------+
1 row in set (0.00 sec)

-- 把排序规则变为 utf8mb4_0900_bin,这个属性为 NO PAD。
mysql>  SET NAMES utf8mb4 COLLATE utf8mb4_0900_bin;
Query OK, 0 rows affected (0.00 sec)

-- 同样验证上面的结果,结果跟描述相悖。原因在于,@a 和 @b 还保持着之前的排序规则,
mysql> select if(@a=@b,' @a 和 @b 相同','@a 和 @b 不同') as '比较结果';
+-------------------+
| 比较结果          |
+-------------------+
|  @a 和 @b 相同    |
+-------------------+
1 row in set (0.00 sec)

-- 重新给@a和@b赋值
mysql> set @a='mysql      ';
Query OK, 0 rows affected (0.00 sec)

mysql> set @b='mysql';
Query OK, 0 rows affected (0.00 sec)

-- 再次验证结果,和之前描述一致。
mysql> select if(@a=@b,' @a 和 @b 相同','@a 和 @b 不同') as '比较结果';
+------------------+
| 比较结果         |
+------------------+
| @a 和 @b 不同    |
+------------------+
1 row in set (0.00 sec)

五、UTF8MB3 与 UTF8MB4 的互相迁移

1、UTF8 到 UTF8MB4 升级

这种顺序可以做到无损迁移,前者就是后者的子集。比如从 MySQL 5.7 到 MySQL 8.0 的字符集升级,这样的场景不会有任何问题。

示例

-- 表 t1 字段 a 字符集 utf8
mysql> create table t1 (a varchar(10) charset utf8);
Query OK, 0 rows affected, 1 warning (0.10 sec)

-- 表 t2 字段 a 字符集 utf8mb4.
mysql> create table t2 (a varchar(10) charset utf8mb4);
Query OK, 0 rows affected (0.05 sec)

mysql> insert into t1 values ('消灭病毒,中国无敌!');
Query OK, 1 row affected (0.01 sec)

-- 表 t1 的数据可以直接插入到 t2。
mysql> insert into t2 select * from t1;
Query OK, 1 row affected (0.02 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> select * from  t2;
+--------------------------------+
| a                              |
+--------------------------------+
| 消灭病毒,中国无敌!           |
+--------------------------------+
1 row in set (0.00 sec)

2、UTF8MB4 到 UTF8 兼容

这种相当于降级模式,如果utf8mb4包含的字符没有超出了utf8的范围,则可以顺序进行;否则失败。

-- t2 的字段 a 虽然是 utf8mb4,但是包含的字符没有超出 utf8 的范围,所以可以顺利的进行
mysql> truncate t1;
Query OK, 0 rows affected (0.09 sec)

mysql> insert into t1 select * from t2;
Query OK, 1 row affected (0.02 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> select * from t1;
+--------------------------------+
| a                              |
+--------------------------------+
| 消灭病毒,中国无敌!           |
+--------------------------------+
1 row in set (0.01 sec)

-- 清空表 t1,t2,    
mysql> truncate table t2;
Query OK, 0 rows affected (0.07 sec)

mysql> truncate t1;
Query OK, 0 rows affected (0.10 sec)

-- 插入 EMOJI 表情字符'哈哈 ????'    
mysql> insert into t2 values ('哈哈????');
Query OK, 1 row affected (0.01 sec)

-- 这些字符不包含在 utf8mb3 中,所以插入报错。
mysql> insert into t1 select * from t2;
ERROR 1366 (HY000): Incorrect string value: '\xF0\x9F\x8D\x80\xF0\x9F...' for column 'a' at row 1

六、字符集系统参数

MySQL 字符集涉及到的参数有以下几个:

1、MySQL 服务层

以下两个设置 MySQL 服务层字符集和排序规则,代表 MySQL 服务启动后,默认的字符集和排序规则。

character_set_server:服务层默认字符集编码

collation_server:服务层默认排序规则

2、客户端层

character_set_client:设置客户端的字符集。

对任何可以连接到 MySQL 服务端的客户端生效。

3、数据库层

character_set_database:设置创建新数据库时默认的字符集

collation_database:设置创建新数据库时默认排序规则名称

4、元数据层

数据库名,表名,列名,用户名等。

character_set_system: MySQL 元数据默认的字符集,目前不可设置,固定为 UTF8。

5、结果集层

character_set_results:设置从服务端发送数据到客户端的字符集。包括查询结果,错误信息输出等。

6、连接层

character_set_connection:设置客户端发送请求到服务端,但是服务端还没有接受之前的数据编码。

比如普通字符串,或者已经写好的 SQL 语句但还没有执行。

collation_connection: 连接层的排序规则。

7、文件系统层

character_set_filesystem: 设置语句中涉及到的文件名字字符集。

比如 load data into table t1 '/tmp/t1.txt'; 这里代表文件名字 /tmp/t1.txt 是以何种编码被 MySQL 解析。

客户端层、连接层、结果集层,这三层一般都是一起设置。比如 set names utf8; 同时设置这三个层次的参数;

服务层一定得选择好对应的编码,否则可能会造成接下来的表、字段、存储过程等默认字符集不正确,产生字符集升级。如果兼容还好,不兼容就可能出现乱码或者其他的错误。

总结

那关于 MySQL 字符集的概念大致就介绍到此。简单总结下本篇,本篇主要介绍字符集相关基本概念以及 MySQL 字符集相关参数大致情况,并且举例说明 UTF8MB3 和 UTF8MB4 的相互转换注意事项,希望对大家有帮助。

杨涛涛自媒体.png


爱可生开源社区
426 声望207 粉丝

成立于 2017 年,以开源高质量的运维工具、日常分享技术干货内容、持续的全国性的社区活动为社区己任;目前开源的产品有:SQL审核工具 SQLE,分布式中间件 DBLE、数据传输组件DTLE。