彻底解决MySQL中的乱码问题( 三 )

服务器接收请求服务器接收到到的请求本质上就是一个字节序列,服务器将其看作是采用系统变量 character_set_client 代表的字符集进行编码的字节序列 。character_set_client 是一个SESSION级别的系统变量,也就是说每个客户端和服务器建立连接后,服务器都会为该客户端维护一个单独的 character_set_client 变量,每个客户端在登录服务器的时候都会将客户端的默认字符集通知给服务器,然后服务器设置该客户端专属的 character_set_client。
我们可以使用SET命令单独修改 character_set_client 对应的值,就像这样:
SET character_set_client=gbk;需要注意的是,character_set_client 对应的字符集一定要包含请求中的字符,比方说我们把 character_set_client 设置成 ascii,而请求中发送了一个汉字 '我',将会发生这样的事情:
mysql> SET character_set_client=ascii;Query OK, 0 rows affected (0.00 sec)mysql> SHOW VARIABLES LIKE 'character%';+--------------------------+------------------------------------------------------+| Variable_name| Value|+--------------------------+------------------------------------------------------+| character_set_client| ascii|| character_set_connection | utf8|| character_set_database| utf8|| character_set_filesystem | binary|| character_set_results| utf8|| character_set_server| utf8|| character_set_system| utf8|| character_sets_dir| /usr/local/Cellar/mysql/5.7.21/share/mysql/charsets/ |+--------------------------+------------------------------------------------------+8 rows in set (0.00 sec)mysql> SELECT '我';+-----+| ??? |+-----+| ??? |+-----+1 row in set, 1 warning (0.00 sec)mysql> SHOW WARNINGSG*************************** 1. row ***************************Level: WarningCode: 1300Message: Invalid ascii character string: '\xE6\x88\x91'1 row in set (0.00 sec)如图所示,最后提示了 'E6、88、91' 并不是正确的ascii字符 。
小贴士:可以将character_set_client设置为latin1,看看还会不会报告WARNINGS,以及为什么~
服务器处理请求服务器在处理请求时会将请求中的字符再次转换为一种特定的字符集,该字符集由系统变量 character_set_connection 表示,该系统变量也是SESSION级别的 。每个客户端在登录服务器的时候都会将客户端的默认字符集通知给服务器,然后服务器设置该客户端专属的 character_set_connection。
不过我们之后可以通过SET命令单独修改这个 character_set_connection 系统变量 。比方说客户端发送给服务器的请求中包含字节序列 0xE68891,然后服务器针对该客户端的系统变量 character_set_client 为 utf8,那么此时服务器就知道该字节序列其实是代表汉字 '我',如果此时服务器针对该客户端的系统变量 character_set_connection 为gbk,那么在计算机内部还需要将该字符转换为采用gbk字符集编码的形式,也就是 0xCED2。
有同学可能会想这一步有点儿像脱了裤子放屁的意思,但是大家请考虑下边这个查询语句:
mysql> SELECT 'a' = 'A';请问大家这个查询语句的返回结果应该是TRUE还是FALSE?其实结果是不确定 。这是因为我们并不知道比较两个字符串的大小到底比的是什么!我们应该从两个方面考虑:

  • 考虑一:这些字符串是采用什么字符集进行编码的呢?
  • 考虑二:在我们确定了编码这些字符串的字符集之后,也就意味着每个字符串都会映射到一个字节序列,那么我们怎么比较这些字节序列呢,是直接比较它们二进制的大小,还是有别的什么比较方式?比方说 'a' 和 'A' 在utf8字符集下的编码分别为 0x61 和 0x41,那么 'a' = 'A' 是应该直接比较 0x61 和 0x41 的大小呢,还是将 0x61 减去32之后再比较大小呢?其实这两种比较方式都可以,每一种比较方式我们都称作一种 比较规则 (英文名: collation ) 。
MySQL 中支持若干种字符集,我们可以使用 SHOW CHARSET 命令查看,如下图所示(太多了,只展示几种,具体自己运行一下该命令):
mysql> SHOW CHARSET;+----------+---------------------------------+---------------------+--------+| Charset| Description| Default collation| Maxlen |+----------+---------------------------------+---------------------+--------+| big5| Big5 Traditional Chinese| big5_chinese_ci|2 || latin1| cp1252 West European| latin1_swedish_ci|1 || latin2| ISO 8859-2 Central European| latin2_general_ci|1 || ascii| US ASCII| ascii_general_ci|1 || gb2312| GB2312 Simplified Chinese| gb2312_chinese_ci|2 || gbk| GBK Simplified Chinese| gbk_chinese_ci|2 || utf8| UTF-8 Unicode| utf8_general_ci|3 || utf8mb4| UTF-8 Unicode| utf8mb4_general_ci|4 || utf16| UTF-16 Unicode| utf16_general_ci|4 || utf16le| UTF-16LE Unicode| utf16le_general_ci|4 || utf32| UTF-32 Unicode| utf32_general_ci|4 || binary| Binary pseudo charset| binary|1 || gb18030| China National Standard GB18030 | gb18030_chinese_ci|4 |+----------+---------------------------------+---------------------+--------+41 rows in set (0.04 sec)


推荐阅读