3.3.2.1 编码和解码使用的字符集不一致的后果
我们知道字符 '我' 在 utf8 字符集编码下的字节串长这样: 0xE68891 ,如果一个程序把这个字节串发送到另一个程序里,另一个程序用不同的字符集去解码这个字节串,假设使用的是 gbk 字符集来解释这串字节,解码过程就是这样的:
1. 首先看第一个字节 0xE6 ,它的值大于 0x7F (十进制:127),说明是两字节编码,继续读一字节后是0xE688 ,然后从 gbk 编码表中查找字节为 0xE688 对应的字符,发现是字符 '鎴'
2. 继续读一个字节 0x91 ,它的值也大于 0x7F ,再往后读一个字节发现木有了,所以这是半个字符。
3. 所以 0xE68891 被 gbk 字符集解释成一个字符 '鎴' 和半个字符。
3.3.2.2 字符集转换的概念
如果接收 0xE68891 这个字节串的程序按照 utf8 字符集进行解码,然后又把它按照 gbk 字符集进行编码,最后编码后的字节串就是 0xCED2 ,我们把这个过程称为 字符集的转换 ,也就是字符串 '我' 从 utf8 字符集转换为gbk 字符集。
3.3.2.3 MySQL中字符集的转换
系统变量 | 描述 |
character_set_client | 服务器解码请求时使用的字符集 |
character_set_connection | 服务器处理请求时会把请求字符串从 character_set_client 转为 character_set_connection |
character_set_results | 服务器向客户端返回数据时使用的字符集 |
这几个系统变量在我的计算机上的默认值如下(不同操作系统的默认值可能不同):
大家可以看到这几个系统变量的值都是 utf8 ,为了体现出字符集在请求处理过程中的变化,我们这里特意修改一个系统变量的值:
mysql> set character_set_connection = gbk;
从这个分析中我们可以得出这么几点需要注意的地方:
服务器认为客户端发送过来的请求是用 character_set_client 编码的。
假设你的客户端采用的字符集和 character_set_client 不一样的话,这就会出现意想不到的情况。比如我的客户端使用的是 utf8 字符集,如果把系统变量 character_set_client 的值设置为 ascii 的话,服务器可能无法理解我们发送的请求,更别谈处理这个请求了。
服务器将把得到的结果集使用 character_set_results 编码后发送给客户端。
假设你的客户端采用的字符集和 character_set_results 不一样的话,这就可能会出现客户端无法解码结果集的情况,结果就是在你的屏幕上出现乱码。比如我的客户端使用的是 utf8 字符集,如果把系统变量character_set_results 的值设置为 ascii 的话,可能会产生乱码。
character_set_connection 只是服务器在将请求的字节串从 character_set_client 转换为character_set_connection 时使用,它是什么其实没多重要,但是一定要注意,该字符集包含的字符范围一定涵盖请求中的字符,要不然会导致有的字符无法使用 character_set_connection 代表的字符集进行编码。比如你把 character_set_client 设置为 utf8 ,把 character_set_connection 设置成 ascii ,那么此时你如果从客户端发送一个汉字到服务器,那么服务器无法使用 ascii 字符集来编码这个汉字,就会向用户发出一个警告。
我们通常都把 character_set_client 、character_set_connection、character_set_results 这三个系统变量设置成和客户端使用的字符集一致的情况,这样减少了很多无谓的字符集转换。为了方便我们设置, MySQL 提供了一条非常简便的语句:
SET NAMES 字符集名;
这一条语句产生的效果和我们执行这3条的效果是一样的:
SET character_set_client = 字符集名;
SET character_set_connection = 字符集名;
SET character_set_results = 字符集名;
比方说我的客户端使用的是 utf8 字符集,所以需要把这几个系统变量的值都设置为 utf8 :
mysql> SET NAMES utf8;
比方说我的客户端使用的是 utf8 字符集,所以需要把这几个系统变量的值都设置为 utf8 :
另外,如果你想在启动客户端的时候就把 character_set_client 、 character_set_connection 、character_set_results 这三个系统变量的值设置成一样的,那我们可以在启动客户端的时候指定一个叫default-character-set 的启动选项,比如在配置文件里可以这么写:
[client]default-character-set=utf8
它起到的效果和执行一遍 SET NAMES utf8 是一样的,都会将那三个系统变量的值设置成 utf8 。
3.3.3 比较规则的应用
比较规则 的作用通常体现比较字符串大小的表达式以及对某个字符串列进行排序中,所以有时候也称为 排序规则 。比方说表 t 的列 col 使用的字符集是 gbk ,使用的比较规则是 gbk_chinese_ci ,我们向里边插入几条记录:
我们查询的时候按照 t 列排序一下:
可以看到在默认的比较规则 gbk_chinese_ci 中是不区分大小写的,我们现在把列 col 的比较规则修改为gbk_bin :
由于 gbk_bin 是直接比较字符的编码,所以是区分大小写的,我们再看一下排序后的查询结果: