现象
用户反馈datatalk查询结果中文乱码,如下所示
数据源配置如下:
用户db表为utf8编码,并且也是通过utf-8编码的jdbc连接用户db查数,中文无法正常显示。
排查过程
-
跟用户索要db账号密码,使用公司db客户端连接,发现查询中文依旧为乱码
-
用户反馈在链接mysql时需加上–default-character-set=latin1字段才可正常显示中文
-
使用terminal直连用户mysql server,查询结果仍然是中文乱码;此时用户反馈需要在连接server的时候加上
--default-character-set=latin1
的字段,结果才可以正常显示。查阅相关资料,该命令会影响character_set_client
,character_set_connectoin
,character_set_server
这三个系统变量。 -
如果不指定
--default-character-set=latin1
,执行以下命令show variables like '%char%'
结果如下 -
加上
--default-character-set=latin1
,重新执行show variables like '%char%'
结果如下
定位问题
上述的三个MySQL系统变量与乱码问题息息相关,其分别为:
- character_set_client: server认为客户端的编码格式
- character_set_connection: server真正处理请求时会转为该编码格式
- character_set_results: server返回结果时会将字符集转为该格式
其关系如下:
最标准的不显示乱码的样式:
本机字符集=character_set_client=character_set_connection=character_set_results
character_set_client,character_set_connection,character_set_results可以通过set names 字符集名
来设置或者在连接时通过--default-character-set=字符集
来设置
本机字符集utf8=character_set_client=character_set_connection=character_set_results不能正常显示中文,但是为latin1的时候却能正常显示,说明用户的数据本身是有问题的。(这里怀疑在–default-character-set=latin1的环境下添加了数据,中文数据本身就是乱码)
解决问题
联系用户自行解决。
但是这里有个问题,期间联系用户连接数据源时将编码格式转为latin1(前文提到,用户使用--default-character-set=latin1
后可以正常显示),但是依然没有得到正确的中文输出。那jdbc连接的编码格式和上面那条指令有什么区别呢?搜索了下,发现这个文档。
总结一下jdbc url里设置characterEncoding就相当于将character_set_client和character_set_connection设置成characterEncoding,character_set_results设置为NULL(查询出来的具体字段不会从字段的编码转成character_set_results,而是保留了原有的编码)。与--default-character-set=latin1
相比,少了一步结果集转到latin1编码格式的转化,自然无法得到正确结果。
多聊一嘴:Unicode和UTF-8的关系
计算机世界不论什么东西,都需要转换为01进行存储,对于数字而言,可以将其转为二进制存储,但是对于字符而言,需要通过一种映射将其先转为一个数字,然后在转为二级制存储。
最熟悉的是ASCII编码,其编码表如下:
可见其只是定义了英文字符以及一些特殊标点,对于中文自然无法涵盖,为此Unicode应运而生。
Unicode会使用两个字节表示一个元素(注意是元素,不一定是一个有意义的文字),但是我们该如何对其编码呢?对于ASCII而言,其范围是0~127,那我们同样的用一个字节的二进制就可以对其进行编码。因为字符到数字的映射以及转二进制的方法是唯一的,所以我们就将这两步统称为ASCII编码。
但是对于Unicode而言,其范围是0~2^32-1,如果我们使用4字节对齐进行编码(该编码为UTF-32),对于同样的d字符而言,所占的容量是ASCII编码的4倍,因此又开发了UTF-16编码和UTF-8编码,UTF-8编码规则如下:
其规则是首先将Unicode映射后的数字转为二进制,然后按照上述规则填入二进制编码的格式中。可见UTF-8是一种可变长度的编码格式。
In a word,Unicode是字符和数字之间的映射规则,utf8,utf16和utf32是具体的编码方式。