MySQL中文乱码问题

Posted by     "erichwu" on Saturday, December 17, 2022

现象

用户反馈datatalk查询结果中文乱码,如下所示

企业微信截图_16709148686192

数据源配置如下:

用户db表为utf8编码,并且也是通过utf-8编码的jdbc连接用户db查数,中文无法正常显示。

排查过程

  1. 跟用户索要db账号密码,使用公司db客户端连接,发现查询中文依旧为乱码

  2. 用户反馈在链接mysql时需加上–default-character-set=latin1字段才可正常显示中文

  3. 使用terminal直连用户mysql server,查询结果仍然是中文乱码;此时用户反馈需要在连接server的时候加上--default-character-set=latin1的字段,结果才可以正常显示。查阅相关资料,该命令会影响character_set_client,character_set_connectoin,character_set_server这三个系统变量。

  4. 如果不指定--default-character-set=latin1,执行以下命令show variables like '%char%' 结果如下

    image-20221214195405258

  5. 加上--default-character-set=latin1,重新执行show variables like '%char%'结果如下

    image-20221214195538755

定位问题

上述的三个MySQL系统变量与乱码问题息息相关,其分别为:

  1. character_set_client: server认为客户端的编码格式
  2. character_set_connection: server真正处理请求时会转为该编码格式
  3. character_set_results: server返回结果时会将字符集转为该格式

其关系如下:

image-20221214200817395

最标准的不显示乱码的样式:

本机字符集=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编码,其编码表如下:

image-20221217225141684

可见其只是定义了英文字符以及一些特殊标点,对于中文自然无法涵盖,为此Unicode应运而生。

image-20221217225354870

Unicode会使用两个字节表示一个元素(注意是元素,不一定是一个有意义的文字),但是我们该如何对其编码呢?对于ASCII而言,其范围是0~127,那我们同样的用一个字节的二进制就可以对其进行编码。因为字符到数字的映射以及转二进制的方法是唯一的,所以我们就将这两步统称为ASCII编码。

但是对于Unicode而言,其范围是0~2^32-1,如果我们使用4字节对齐进行编码(该编码为UTF-32),对于同样的d字符而言,所占的容量是ASCII编码的4倍,因此又开发了UTF-16编码和UTF-8编码,UTF-8编码规则如下:

image-20221217230713090

其规则是首先将Unicode映射后的数字转为二进制,然后按照上述规则填入二进制编码的格式中。可见UTF-8是一种可变长度的编码格式。

In a word,Unicode是字符和数字之间的映射规则,utf8,utf16和utf32是具体的编码方式。