Mysql & 理解字符集、排序规则的作用

1、字符集是什么

在讲解字符集前,我们有必要思考一下,计算机是如何认识人类的语言的,也就是说是如何认识这些字符串的?

我们都知道计算机只认识一堆 00110011… 二进制的玩意,它是怎么通过二进制认识到我们所输入的数据呢?这一切都与字符集有关。
如果你懂得对象的键值对关系,对字符集的理解就轻松多了,比如:

'00100001': 'A'
'00100010': 'B'
...

当用户输入 A 字符时,就会映射对应的二进制,字符集其实就是一堆字符与二进制进行关联绑定的名词。

同样地,字符集的映射规则多姿多彩,比如上面的案例我也可以这样以3组二进制的方式来书写:

'000000010000000100000001': 'A'
'000000110000001100000011': 'B'
...

映射规则的不同就产生了不同的字符集种类,在早起时代采用的是 ASCII 字符集,这里借助百科图看看它们的映射关系:
在这里插入图片描述
看到这里相信你已经了解字符集的概念,我们从图中也看到了,ASCII 字符集只对英文字母和特殊符号的映射,共只有 128 个映射,而随着计算机不断的普及,世界各国都有自己的语言,它们都需要自己的一套映射规则才能让计算机展示出来,很明显 ASCII 这种字符集并不适用中文,咋们国家可是拥有 5000 多年的语言文学历史,128 个岂能满足!加上世界各地语言,少说也得十万个吧?

由于各国语言不同,各自研发了自己的字符集,但这样就会产生不统一性质,为了让世界各国能统一起来,国际组合制定了一套通用的字符集方案,俗称 Unicode,它是计算机科学领域的业界标准,它整理、编码了世界上大部分的文字系统,不过由于 Unicode 对字符的处理是采用 2个字节来编码(映射),而 ASCII 对英文只有1个字节编码,很显然这对英文存储开销很大,所以后来就有人基于 Unicode 标准由抽象出一套 UTF 系列的字符集,其原理就是对 Unicode 通过一些算法进行转换,所以 uffUnicode 的一部分,其中最广泛常用的就是 utf-8,它是的存储特性就是可变,不像 Unicode 那样是固定的,utf-8是以 1~4 个字节来动态存储字符,当遇到英文时它将英文作为 1个字节编码处理,当遇到中文时,它将视为 3个字节编码处理,从这里你也看到了,原来 Unicode 对中文的编码处理是2字节,而 utf-8 是3字节,但 uft-8 字符集目前是应用最广泛的。

关于字符集更多细节就不细说了,这里仅作为铺垫,想深入研究字符集种类、历史方案、字符编码规则等,感兴趣的同学可自行了解。

现在我们来实现打开文件产生所谓的‘乱码’问题:
首先我们创建 A.txt 文本文件内容如下

你好

然后我们保存时选择编码类型为:ANSI (以前的 GBK23212)
在这里插入图片描述
现在我们用 Subline Text 编辑器打开:
在这里插入图片描述

我们可以看到,你好变成了ÄãºÃ,产生这个问题的原因就是 Subline Text 默认情况下是以 utf-8 进行编码/解码的,我们可以通过 Subline Text 的 [setting] 选项查看
在这里插入图片描述

而我们的 A.txt 是以 ANSI 字符集进行编码保存的,所以就出现了鸡同鸭讲的画面,解决这个问题也很简单,要么将 Subline Text 默认编码设为 ANSI,那么重新另存 A.txt 的字符集为 utf-8 即可,然后重新打开看看效果
在这里插入图片描述

2、Mysql 字符集

知道了字符集后,理解 Mysql 字符集就是小菜一碟了,我们在创建数据库的时候会有一个字符集选项,这个选项其实就跟我们的保存文件一样,你要以什么编码形式存储这些数据,不过 Mysql 的字符集种类非常多,这里我们最常用的是 utf8 ,但是 Mysql 对 utf8 进行了阉割,它将默认的 1-4 字节改成 1-3 字节,如果需要 1-4字节的话,比如 emoji 表情就是以4个字节作为编码单位,可以设置为 utf8mb4 字符集
在这里插入图片描述

2、Mysql 排序规则

创建数据库时,还有一个排序规则,它也常常被称呼‘比较大小规则’,其实就是当出现
SQL 语句有 order by 时,你需要以什么算法来排序它们,比如 abcABC 时,你是想以
aAbBcC 不区分大小写算法来排序呢还是以二进制比较大小的方式来排序 abcABC 呢。

每种字符集都有一堆排序规则可以选择,这些排序规则的前缀表示只适用某种字符集,比如 utf8mb4 字符集 就只能使用以下排序规则
在这里插入图片描述
每个排序规则都有对应的后缀,常见的后缀有:ci、cs、bin 等,含义如下:
_ci 比较时不区分大小写。
_cs 比较时区分大小写。
_bin 以二进制方式比较。

每个排序规则中间那一部分表示比较规则所应用的语言,比如 utf8mb4_swedish_ci 中的 swedish 表示应用瑞典语言, utf8mb4_general_cigeneral 表示通用语言即也公共比较,不区分于哪种语言。

3、Mysql 字符集的优先级

数据库、表、列、在创建时可以单独指定字符集,如果三者都没有选择字符集,Mysql 则采用系统级别的字符集,比如创建数据库时如果没有指定字符集,则采用系统级别,类似的,如果表没有指定字符集,则采用数据库,以此类推,它们的优先级别如下:

系统 < 数据库 < 表 < 列

接下来我们来看看各个级别的字符集如何使用。

3.1 系统级别的字符集

3.1.1 查看系统的字符集和比较规则

SHOW VARIABLES LIKE 'character_set_server'; # 字符集
SHOW VARABILES LIKE 'collation_server'; # 字符集的比较规则

3.1.2 修改系统级别的字符集和比较规则可通过 set [variable_name]=[value]my.ini 配置项来设置,这里我用配置项举例

[server]
character_set_server=utf8
collation_server=utf8_unicode_ci

3.2 数据库的字符集

3.2.1 查看数据库的字符集和比较规则

SHOW VARIABLES LIKE 'character_set_database';
SHOW VARIABLES LIKE 'collation_database';

3.2.2 指定数据库的字符集和比较规则:在创建数据库时指定

CREATE DATABASE [database_name] 
... 
CHARACTER SET utf8 COLLATE utf8_unicode_ci;

3.3 表的字符集

3.3.1 查看表的字符集和比较规则

SHOW CREATE TABLE [table_name];

3.3.2 指定表的字符集和排序规则

# 创建表时指定
CREATE TABLE [table_name]
...
CHARACTER SET utf8 COLLATE utf8_unicode_ci;

# 修改表时重新指定
ALTER TABLE [table_name]
...
CHARACTER SET utf8 COLLATE utf8_unicode_ci;

3.4 列的字符集

3.4.1 查看列的字符集和比较规则

SHOW CREATE TABLE [table_name];

3.4.2 指定列的字符集和比较规则

# 在创建表时指定
CREATE TABLE [table_name]
user_name varchar(10) CHARASET SET utf8 COLLATE utf8_unicode_ci,
user_email varchar(10) CHARSET SET utf8 COLLATE utf8_unicode_ci);

# 修改列时重新指定
ALTER TABLE  [table_name] [column_name] [column_type] CHARACTER SET utf8 COLLATE utf8_general_ci;

4、Mysql 客户端字与服务器之间的字符集交互

4.1 服务器对客户端字符集的处理

在客户端输入 SELECT user_name FROM users WHERE user_name='张三' 时会告诉服务器找出名叫张三的用户,但是、但是、但是、我们知道计算机是有字符集的概念的,客户端默认的字符集是由系统决定的,在 windows 中,默认就是 gbk 字符集,我们可以通过 cmd 控制台中的[属性] 查看(windows 将字符集称为代码页)
在这里插入图片描述
所以 SELECT user_name FROM users WHERE user_name='张三' 这句话实际上会以 gbk 的形式进行编码发送给服务器,那么服务器是如何得知每个客户请求编码过来的是哪种字符集呢?答案是它是由character_set_client 系统变量决定的,character_set_client 默认就是操作系统的字符集,我们也可以修改 character_set_client 值将其覆盖。

现在思考一下,假如 user_name 字段所指定的字符集是 utf8,而客户端将 user_name 以 gbk 编码发送过去,由于客户端编码与存储的编码不一致,显然匹配不到,此时服务器是不是就会提示 empty 呢?

正常来讲应该返回 empty 才对,但毕竟是人类语言,mysql 作者会自动将 gbk 转化为 utf8 的形式去查找,当我们的客户端设置 gbk 实际上还是能正常访问 utf8 的数据的。

在这里插入图片描述

注意:

  1. 这是 mysql 自带行为,像在平时打开文件还是要保证编辑器的字符集与保存文件时所定的字符集一致,这样访问时才不会出现乱码问题。
  2. 服务器并不会将客户端的字符集都主动转成 utf8 ,例如客户端字符集设置为 latin1 时,这次服务器并不会将其转换 utf8 ,而是以正常 emtpy 形式提示找不到此用户,这里我认为应该是服务器会识别此类字符集是否包含中文编码,如果有就会将其转换 utf8,而 latin1 里没有中文编码,所以不会帮我们转换,当然了,若有对这方面熟悉的同学欢迎下方留言指明。

4.2 服务器响应时字符集的处理

当我们找到 user_name 为 张三 后服务器是否就会按照 utf8 的数据形式返回出去呢?
其实并没有,它也同样会转成合适的字符集返回给客户端,它的转换是依据 character_set_results 系统变量决定的,默认情况下character_set_resultscharacte_set_client 初始化时是一致的,这也就是为什么客户端展示数据时能正常的显示张三 这位用户,因为服务器响应时 character_set_results 值为 gbk

此时若想让客户端呈现时变成乱码的话就好办了,我们只需将 charset_set_result 值改为 latin1 即可看效果。

在这里插入图片描述

5、 总结

  • 我们在计算机看到的每个字,都是由字符集编码/解码的形式呈现出人类认识的语言,当出现乱码时,相信你已经知道是字符集搞的鬼了。

  • Mysql 对字符集有专门的处理,比如它会将 utf8 1-4 字节阉割成 1-3 字节,它也会对客户端请求以及响应时对字符集进行合适的转换处理。

猜你喜欢

转载自blog.csdn.net/cookcyq__/article/details/123512561