MySQL的字符编码体系可以分成两部分:一部分是关于数据库服务器本身存储数据表时如何管理字符数据的编码;另一部分是关于客户端与数据库服务器传输数据如何编码。上一篇MySQL的字符编码体系(一)――数据存储编码讨论了数据存储编码,本篇讨论数据传输编码。
MySQL的客户端可以分为两种:一种就是用C语言写的官方客户端――MySQL命令程序;一种就是平常程序员使用JDBC等connector API写成的客户端。这里只讨论第一种。
MySQL命令程序在Windows和Linux系统中关于字符编码处理的部分并不等效,下图是Windows系统的客户端字符编码转换逻辑:
其中的三个character变量存在于服务器上,而charset_info存在于客户端。
当客户端启动连接到服务器时,客户端将根据配置参数设置charset_info为指定编码,同时通知服务器让服务器把三个character变量设置为相同编码。
由于在Windows平台上MySQL程序在读取控制台时使用了Unicode Console Read API,所以程序从控制台获取的原始字符串实际上是UTF16编码,所以这里的“操作系统编码”并不是Windows通常的GBK,而应该看做UTF16。
下图是Linux系统中的MySQL客户端程序字符编码转换逻辑:
vcyoTXlTUUy/zbuntsuy6dGvzazSu7j2se21w7W9tcTItMrHwtLC66GjPGJyPgq/ydLU1eLR+cSjxOLJz8r2tcTH6b/2o7o8YnI+CrS0vajSu7j2se2jrMbk1tDWu7D8uqzSu7j2R0JL19a3+7Su19a2zrrNVVRGONfWt/u0rtfWts6ho0xpbnV41tDG9LavTXlTUUzBrL3Ttb3K/b7dv+K3/s7xxvejrL2rt/7O8cb3tcTI/bj2Y2hhcmFjdGVyseTBv7TTxKzIz7XEVVRGONDeuMTOqkdCS6Gjz/LK/b7dv+Ky5cjr1tDOxMr9vt2jrMGivLRzZWxlY3SjrL3hufvO3tLss6OjujwvcD4KPHA+PGltZyBzcmM9"http://www.bitsCN.com/uploadfile/Collfiles/20140714/2014071409163740.png" alt="/">
但是使用Windows的MySQL客户端查询时,结果却是乱码:
结合前面的数据传输流程,就能知道问题出在什么地方:
客户端从终端读取了一行utf8编码(Linux默认)的命令文本,忽略charset_info变量,直接把文本发送给服务器;服务器因为事先的命令charset gbk把三个character变量都设置为了GBK,所以服务器认为收到的文本是GBK编码;接下来服务器会不经过任何转码将文本字符串直接存入数据表中,因为数据表第一个字段也是GBK。到这里为止,数据表中存了一个UTF8字符串,而服务器却当它是GBK,在同一个Linux客户端查询时:如果Windows客户端也想看到正确的结果,那就要故意错误地配置:
执行命令charset utf8,这会将charset_info和三个服务器character都设置为UTF8;执行命令set names gbk,这只会将三个服务器character设置为GBK;现在select,结果看上去不再乱码了。