热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

MySQL乱码问题终极指南_MySQL

为了让大家尽量在工作中少受或者不受乱码的困扰,这篇文章主要为大家分享了MySQL乱码问题终极指南,感兴趣的小伙伴们可以参考一下
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的字符处理机制】

详细的处理机制如下图:



我们模拟一下一条数据从插入到读取的处理流程,看看在整个流程中,字符集是如何辗转腾挪的。
【插入流程】
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发送包


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

2. latin1接收包


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

3. latin1不显示乱码


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

4. utf8接收包


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

5. utf8显示包


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

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

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


附: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乱码问题,更多相关内容请关注PHP中文网(www.php1.cn)!

推荐阅读
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • PHP设置MySQL字符集的方法及使用mysqli_set_charset函数
    本文介绍了PHP设置MySQL字符集的方法,详细介绍了使用mysqli_set_charset函数来规定与数据库服务器进行数据传送时要使用的字符集。通过示例代码演示了如何设置默认客户端字符集。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文由编程笔记小编整理,介绍了PHP中的MySQL函数库及其常用函数,包括mysql_connect、mysql_error、mysql_select_db、mysql_query、mysql_affected_row、mysql_close等。希望对读者有一定的参考价值。 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
author-avatar
cqj6832154
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有