MySQL出现乱码问题如何解决

  • 时间:
  • 6176人关注

为了让大家尽量在工作中少受或者不受乱码的困扰,这篇文章主要为大家分享了MySQL乱码问题终极指南,感兴趣的小伙伴们可以参考一下,另外这篇文章主要知识点是关于MySQL、MySQL的内容,如果大家想对相关知识点有系统深入的学习,可以参阅以下电子资料:

教程详情电子资料
  • 教程类别:MySQL
  • 编辑整理:欧星洲
  • 教程字数:3912字节
  • 阅读时间:大概9分钟
  • 下载本教程(DOC版)
  • MySQL是怎样运行的:从根儿上理解 MySQL
  • Centos7下安装mysql5.7的步骤
  • 高性能MySQL
  • MySQL8从入门到精通
  • MySQL8 Cookbook(中文版)
  • mysql的字符集设置众多,从客户端到连接到结果集,从服务器到库到表到列,都可以设置字符集,灵活很强大,但就是很容易出问题,如果不了解其机制,很容易就出现乱码问题。

    为了让大家尽量在工作中少受或者不受乱码的困扰,这里我结合之前其它同学在论坛的发帖,并结合自己的理解和实践,详细分析总结了一下,以飨各位看官。

    关于字符集和乱码的基础知识这里就不详细说明了(请自行搜索),但有一个问题需要特别强调一下:乱码是怎么产生的?
    这个问题相信很多同学都是模棱两可,或者没有认真想过,反正理解就是”字符编码“不对导致乱码,但没有真正想过为什么”字符编码“会导致乱码。
    答案其实很简单:“转换导致乱码”!
    根据这个原则来判断,各种情况就很简单了:

    1)数据传送过程中不会导致乱码
    2)数据存储不会导致乱码
    3)数据输入和输出(包括显示)可能导致乱码
    4)数据接收和发送可能导致乱码

    更详细的解释:转换导致乱码是指本来是A字符集的数据被当成了B字符集进行解析,而不是说正确的A字符集转换为B字符集。
    例如:如下mysql字符处理机制流程图中,mysql客户端发送的实际上是2个gbk字符(4字节),但character_set_connection
    设置了utf8,于是mysql服务器将收到的4字节gbk数据按照utf8解析,得到1个中文字符+1个字节,这时就产生乱码了;

    如果character_set_connection 设置为gbk,mysql服务器收到数据后按照gbk解析,得到两个正确的中文,然后再转换为这两个中文对应的utf8编码,这就不会产生乱码。)

    【mysql的字符处理机制】

    详细的处理机制如下图:

    MySQL乱码问题终极指南
     

    我们模拟一下一条数据从插入到读取的处理流程,看看在整个流程中,字符集是如何辗转腾挪的。
    【插入流程】
    1. 客户端设定了自己的编码(character_set_client),接收用户的输入;
    2. 客户端将用户的输入“转换”成连接的编码(character_set_connection) =====> 第一次转换
    3. 客户端将转换后的数据发送给服务器;                               =====> 传输不会导致编码转换
    4. 服务器收到客户端的数据,再判断数据列的字符集,进行字符转换       =====> 第二次转换
    5. 服务器将数据存储(例如磁盘)                                     =====> 存储不会导致编码转换  

    【读取流程】
    略去前面的sql语句处理流程,从数据读取开始
    1. 服务器从存储(例如磁盘)读取数据                                 =====> 存储不会导致编码转换,因此从存储读取也不需要
    2. 服务器判断当前连接返回结果的字符集(character_set_results),
       将读取的数据转换为结果集要求的数据                               =====> 逆向的第一次转换,对应正向的第二次编码转换
    3. 服务器将数据发送给客户端                                         =====> 传输不会导致编码转换
    4. 客户端收到服务器的数据,根据客户端的字符集(character_set_client)进行编码转换          =====> 逆向第二次转换,对应正向第一次编码转换
    5. 客户端显示数据                                                   =====> 你能看到乱码的时候

    有了这个流程,我们就很容易定位乱码可能产生的地方,以及产生乱码的字符集配置究竟是哪个了。
    理想的情况是整个流程中,所有涉及字符转换的地方都不需要转换,这样就不会产生乱码了。

    有了上面的理论分析后,我们再结合一个乱码的抓包实例,加深理解,其中有一些问题,请大家思考一下,看看是否真的理解了。

    环境:
    +--------------------------+-----------------------------------------------------+
    | Variable_name            | Value                                               |
    +--------------------------+-----------------------------------------------------+
    | character_set_client     | latin1                                              |
    | character_set_connection | latin1                                              |
    | character_set_database   | utf8                                                |
    | character_set_filesystem | binary                                              |
    | character_set_results    | latin1                                              |
    | character_set_server     | utf8                                                |

    测试语句是插入一个中文字符“你”,其utf8编码为"0xE4 0xBD 0xA0",

    1. latin1发送包 

    MySQL乱码问题终极指南

    思考一下1:为什么客户端和连接都设置了latin1,但最终发送的是正确的utf8编码呢?

    2. latin1接收包 

    MySQL乱码问题终极指南

    思考一下2:为什么接收到的还是正确的utf8编码?

    3. latin1不显示乱码 

    MySQL乱码问题终极指南

    思考一下3:为什么latin1显示了正确的utf8字符?

    4. utf8接收包 

    MySQL乱码问题终极指南

    思考一下4:为什么连接的字符集和数据库的字符集设置成一样了,接收的数据反而不是utf8了?(请与latin1接收数据包对比)

    5. utf8显示包

    MySQL乱码问题终极指南

    思考一下5:为什么连接的字符集和数据库的字符集设置成一样了,显示反而乱码了? 

    怎么样,上面的思考题是否都有答案了,如果没有,相信下面这幅图能够帮助你:

    这个抓包案例的字符变化图解:

    MySQL乱码问题终极指南

    附:mysql字符编码操作技巧
    【查看字符集设置】

    mysql> show variables like '%char%';
    +--------------------------+-----------------------------------------------------+
    | Variable_name      | 说明                        |
    +--------------------------+-----------------------------------------------------+
    | character_set_client   | 客户端字符集                    |
    | character_set_connection | 当前连接字符集                   |
    | character_set_database  | 数据库字符集                    |
    | character_set_filesystem | 文件系统字符集,不要修改,使用binary即可      |
    | character_set_results  | 返回结果集字符集                  |
    | character_set_server   | 服务器默认字符集,当数据库、表、列没有设置时,   |
    |             |   默认使用此字符集                |
    | character_set_system   | 固定为utf8                     |
    +--------------------------+-----------------------------------------------------+ 
    

    【修改字符集设置】
    服务器的配置在服务器建立的时候就由DBA设置好了,不推荐后续再改
    通过SET NAMES utf8命令同时设置character_set_client/character_set_connection/character_set_results的字符集
    建议所有配置都设置成utf8

    【问题答案】

    思考一下1:为什么客户端和连接都设置了latin1,但最终发送的是正确的utf8编码呢?
    客户端设置了latin1,而我的语句是从notepad++中写好的,是utf8格式的;
    中文utf8是3个字节,而latin1是按照单个字节解析的,虽然进行了转换,但不会导致二进制内容的变化,但实际上mysql客户端认为我输入了3个latin1字符;
    如果客户端设置的编码是2个字节的gbk,这时转换就会发生乱码,utf8的3个字节会被转换为1个gbk字符(可能是乱码,也可能不是乱码)加上一个西欧字符(小于128就是英文,大于128就是其它西欧文)

    思考一下2:为什么接收到的还是正确的utf8编码?
    这是因为mysql服务器从将数据从“列”的编码(utf8)转换为latin1了,而列存储的数据并不是真正的utf8的中文“你”对应的"0xe4 0xbd 0xa0",
    而是后面抓包看到的“c3a4 c2bd c2a0”(6个字节),mysql服务器将utf8的c3a4转换为latin1的0xe4,c2bd转换为0xbd, c2a0转换为0xa0

    思考一下3:为什么latin1显示了正确的utf8字符?
    因为mysql客户端收到了mysql服务器转换后的"0xe4 0xbd 0xa0",并把这个数据当做latin1的3个字符处理,然后抛给终端(我的是SecureCRT),
    SecureCRT又把这三个latin1当做uft8处理,结果中文的“你”就显示出来了。

    思考一下4:为什么连接的字符集和数据库的字符集设置成一样了,接收的数据反而不是utf8了?(请与latin1接收数据包对比)
    字符集都一样的情况下,整个流程中不需要进行编码转换,直接将存储的“c3a4 c2bd c2a0”返回给客户端

    思考一下5:为什么连接的字符集和数据库的字符集设置成一样了,显示反而乱码了?
    参考思考4,客户端收到数据后也直接抛给终端显示,终端认为是两个utf8字符,并且找到了对应字符并显示,但我们看不懂,所以知道是乱码了,但这两个字符显示并没有错,如果真正找不到字符,可能会显示问号或者字符集规定的缺省符号。

    以上就是关于MySQL乱码问题大集合,希望能够帮助大家解决MySQL乱码问题,谢谢大家的阅读。


    上一篇:mysql图文安装详细步骤细节

    下一篇:MySQL数据库的高可用方案汇总

    相关内容

    • Python使用pycharm导入pymysql教程

      这篇文章主要介绍了Python使用pycharm导入pymysql教程,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

      04-19Python pycharm导入pymysql

      阅读更多
    • 如何解决JDBC连接Mysql 8.0.11出现的各种问题

      这篇文章主要介绍了使用JDBC连接Mysql 8.0.11出现了各种错误的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学

      01-16使用JDBC连接Mysql 8.0.11出现了各种错误的解决

      阅读更多
    • 示例讲解php+mysql查询实现无限下级分类树输出

      这篇文章主要介绍了php+mysql查询实现无限下级分类树输出,结合实例形式分析了php+MySQL查询实现的树状分类输出功能,涉及php数据库查询与数组遍历等相关操作技巧,需要的朋友可以参考下

      02-14php+mysql查询实现无限下级分类树输出示例

      阅读更多
    • JDBC中使用Java8的日期LocalDate和LocalDateTime操作mysql、postgresql

      这篇文章主要给大家介绍了关于JDBC中如何使用Java8的日期LocalDate和LocalDateTime的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下

      07-08JDBC中使用Java8的日期LocalDate和LocalDateTime操作mysql、postgresql

      阅读更多
    • mysql服务1067错误多种解决方案汇总

      今天我的mysql服务器突然出来了1067错误提示,无法正常启动了,我今天从网上找寻了大量的解决mysql服务1067错误的办法,有需要的朋友可以看看

      01-29mysql服务1067错误多种解决方案分享

      阅读更多
    • MySQL 8 Cookbook

      MySQL 8 Cookbook

      MySQL 8的更新幅度在MySQL历史上可谓空前,增添很多重磅新特性——“数据字典”取消了已存在几十年的frm文件,将插件式数据库的重心偏向InnoDB,极大提高meta data的访问性能,并支持了DDL原子性

      大小:206.5 MBMySQL

      点击下载
    • PHP&MySQL跨设备网站开发实例精粹

      PHP&MySQL跨设备网站开发实例精粹

      这本书从易学实用的角度详细讲解了PHP、HTML5语法,MySQL数据库存取,网页之间的信息传递、表单的后端处理等,提供了丰富的网站开发范例,欢迎下载

      大小:80.9 MBPHP编程

      点击下载
    • MySQL 8从入门到精通

      MySQL 8从入门到精通

      MySQL被设计方案为1个可移植的数据库查询,基本上能在当今全部的电脑操作系统上运作,如Linux、Solaris、FreeBSD、Mac和Windows。开源系统MySQL数据库查询发展趋势到今日,早已具备了十分普遍的客

      大小:168 MBMySQL8

      点击下载
    • PHP+MySQL程序设计及项目开发

      PHP+MySQL程序设计及项目开发

      大小:105.1MBPHP+MySQL

      点击下载
    • MySQL运维内参

      MySQL运维内参

      《MySQL运维内参:MySQL、Galera、Inception核心原理与最佳实践》是一本介绍MySQL数据库知识的专业书籍,从核心原理到最佳实践,深入浅出、抽丝剥茧地进行讲解,不仅从源码和运维两个角度介绍

      大小:162 MBMySQL

      点击下载
    • MySQL王者晋级之路

      MySQL王者晋级之路

      本书深入剖析MySQL数据库体系结构,实战演练备份恢复、主从复制,详解高可用集群架构的设计与实践过程,详细梳理优化思路,展现新版本的特性,并与真实生产案例相结合,通过核心原理到“王者”实战,全面覆盖MySQL数据库的知识点

      大小:76.5 MBMySQL数据库

      点击下载
    • MySQL与MariaDB学习指南

      MySQL与MariaDB学习指南

      MySQL与MariaDB学习指南 使读者不仅能够深入了解MySQL这种主流数据库,还能全面掌握作为开源数据库新秀的MariaDB的使用方法。书中内容由浅至深、层层深入,从分步介绍如何安装MySQL和MariaDB,到

      大小:68.9 MB数据库

      点击下载

    学习笔记