现象

在使用MySQL客户端书写SQL语句的时候,我们可以在字符串前边加_charset_name的符号,其中的charset_name对应着某个具体的字符集,废话不多说,先写两个例子看一下:

mysql> SELECT _utf8'我';+-----+| 我 |+-----+| 我 |+-----+1 row in set (0.04 sec)mysql> SELECT _gbk'我';+-----+| 鎴 |+-----+| 鎴 |+-----+1 row in set, 1 warning (0.02 sec)

可以看到第一个查询结果正常,第二个查询出现了乱码。为什么呢?下边细细道来。

原因

我们知道MySQL是一个C/S架构的软件,可以有很多客户端连接到服务器进行交互。客户端发送给服务器的请求以及服务器发送给客户端的响应本质上都是一个二进制的字节串,每当我们从客户端发送一个请求到服务器,服务器处理完成之后再把响应返回给客户端的过程其实发生了很多字符集转换过程。

  • 首先请求会被MySQL客户端编码为字节序列之后通过网络传输到服务器。对于MySQL自带的客户端来说,这个编码过程使用的字符集和我们使用的操作系统的默认字符集是一样的,类Unix系统的默认字符集就是utf8,Windows系统的默认字符集就是gbk。
  • 服务器收到字节序列请求之后,会认为该字节串是按照character_set_client系统变量编码的,之后将其从character_set_client转换到character_set_connection,之后进行更深入的处理。
  • 最后再将响应发送到客户端的时候,又会按照character_set_results进行编码。
  • 客户端收到响应字节串之后,按照本客户端规定的字符集进行解码。对于MySQL自带的客户端来说,这个解码过程使用的字符集和我们使用的操作系统的默认字符集是一样的,类Unix系统的默认字符集就是utf8,Windows系统的默认字符集就是gbk。

总结一下这几个涉及到的通信字符集系统变量:

系统变量描述character_set_client服务器解码请求时使用的字符集character_set_connection服务器处理请求时会把请求字符串从character_set_client转为character_set_connectioncharacter_set_results服务器向客户端返回数据时使用的字符集

现在我的系统中的这几个系统变量的值都是utf8:

mysql> SHOW VARIABLES LIKE 'character_set_client';+----------------------+-------+| Variable_name | Value |+----------------------+-------+| character_set_client | utf8 |+----------------------+-------+1 row in set (0.24 sec)mysql> SHOW VARIABLES LIKE 'character_set_connection';+--------------------------+-------+| Variable_name | Value |+--------------------------+-------+| character_set_connection | utf8 |+--------------------------+-------+1 row in set (0.25 sec)mysql> SHOW VARIABLES LIKE 'character_set_results';+-----------------------+-------+| Variable_name | Value |+-----------------------+-------+| character_set_results | utf8 |+-----------------------+-------+1 row in set (0.30 sec)

如果我们使用了_charset_name前缀,意味着禁止服务器将后续字节从character_set_client转换到character_set_connection,而是默认使用_charset_name代表的字符集作为它后续字节的字符集。比方说:

mysql> SELECT _gbk'我';+-----+| 鎴 |+-----+| 鎴 |+-----+1 row in set, 1 warning (0.02 sec)

我现在使用的是macOS操作系统,所以

  • 客户端发送请求时会将字符'我'按照utf8进行编码,也就是:0xE68891。
  • 服务器收到请求后发现有前缀_gbk,则不会将其后边的字节0xE68891进行从character_set_client到character_set_connection的转换,而是直接把0xE68891认为是某个字符串由gbk编码后得到的字节序列。
  • 然后再把上述0xE68891从gbk转换为character_set_results,也就是utf8。0xE688在gbk中代表汉字'鎴',而0x91无法解码(我们可以看到上述查询结果中有1个warning)。我们紧接着上边的查询语句执行一下SHOW WARNINGS:
mysql> SHOW WARNINGSG*************************** 1. row *************************** Level: Warning Code: 1300Message: Invalid gbk character string: '91'1 row in set (0.01 sec)

之后将汉字'鎴'再按照utf8进行编码,得到的结果就是E98EB4,把它发送到客户端。

  • 客户端收到之后再解码到屏幕上,解码也使用utf8字符集,所以就出现了鎴。

扩展

如果在我的机器上我执行SELECT LENGTH(_gbk '我')会得到什么结果呢(LENGTH函数用来统计某个字符串共占用多少字节)?有很多小伙伴不经思考,脱口而出:2!哈哈,我们看一下结果验证一下:

mysql> SELECT LENGTH(_gbk '我');+--------------------+| LENGTH(_gbk '我') |+--------------------+| 3 |+--------------------+1 row in set, 1 warning (0.01 sec)

WTH?竟然是3?其实再回想一下我们上边所说的,因为'我'前边加了_gbk,所以不会经历从character_set_client到character_set_connection的转换过程,而是直接把0xE68891当作是一个采用gbk编码的字节串。这个字节串中有3个字节,当然结果就返回3了(虽然0x91这个字节在gbk字符集中是无效的,可以看到上边查询语句中也给出了Warning)。

思考

如果我现在不使用基于macOS操作系统的客户端,而采用基于Windows操作系统的客户端来发送请求,那么下边的语句的返回结果将会是什么呢:

SELECT LENGTH(_utf8 '我');

mysql set语句_不得不注意!那些容易被忽视的MySQL字符集问题?相关推荐

  1. mysql修改语句_序言:MySQL与Navicat安装Tips

    一.数据库相关的基础知识 1.1 数据分析师主要集中在select高效查找上,纯粹的底层运维就不需要太关注: 1.2 数据库与表 类比sheet是表,整个Excel文件是一个数据库: 1.3 行与列 ...

  2. mysql插入栏位语句_情人节福利,手把手教你学会mysql语句操作

    情人节福利,手把手教你学会mysql语句操作 一起来 日常学python 进入正题 mysql语句分为两种,一种是DDL,就是数据定义语句,比如那些CREATE,ALTER.另一种是DML,就是数据管 ...

  3. mysql 预处理语句_预处理语句

    MySQL 8.0支持服务器端预处理语句.这种支持利用了有效的客户端/服务器二进制协议.对参数值使用带占位符的预处理语句具有以下好处:每次执行语句时解析语句的开销都较小.通常,数据库应用程序处理大量几 ...

  4. mysql 使用表 语句_【mysql】mysql 经常使用建表语句

    [1]创建员工档案表 要求字段:员工员工编号,员工姓名,性别,工资,email,入职时间,部门. [2]合理选择数据类型及字段修饰符,要求有NOT NULL,auto_increment, prima ...

  5. mysql 帮助命令_一篇文章帮你搞定所有MySQL命令!

    MySQL的基本操作可以包括两个方面:MySQL常用语句如高频率使用的增删改查(CRUD)语句和MySQL高级功能,如存储过程,触发器,事务处理等.而这两个方面又可以细分如下: MySQL常用语句 表 ...

  6. mysql sqlserver对比_很用心的写了 9 道 MySQL 面试题

    MySQL 一直是本人很薄弱的部分,后面会多输出 MySQL 的文章贡献给大家,毕竟 MySQL 涉及到数据存储.锁.磁盘寻道.分页等操作系统概念,而且互联网对 MySQL 的注重程度是不言而喻的,后 ...

  7. 怎么在linux上修改mysql端口映射_如何在Linux中更改默认的MySQL / MariaDB端口

    在本指南中,我们将学习如何更改MySQL / MariaDB数据库在CentOS 7和基于Debian的Linux发行版中绑定的默认端口. MySQL数据库服务器在Linux和Unix下运行的默认端口 ...

  8. 二级mysql教程下载_全国计算机等级考试教程:二级MySQL数据库程序设计

    第一章 数据库技术的基本概念与方法 1.1 基本概念 1.2 数据库系统的特点 1.3 数据库系统的结构 1.3.1 数据库系统的三级模式结构 1.3.2 三级模式结构的两层映像与两级数据独立性 1. ...

  9. linux mysql恢复数据_删库不跑路详解MySQL数据恢复

    作者:程淇铭 出处:https://segmentfault.com/a/1190000020116271 日常工作中,总会有因手抖.写错条件.写错表名.错连生产库造成的误删库表和数据的事情发生,那么 ...

最新文章

  1. vCenter 升级错误 VCSServiceManager 1603
  2. 最简单的目标跟踪(模版匹配)
  3. 认识与入门 Markdown
  4. 数据结构与算法之霍夫曼编码解码实现
  5. 【Web缓存机制系列】2 – Web浏览器的缓存机制
  6. 自动生成宏程序软件_圆周等分孔钻孔宏程序计算器
  7. CVE-2017-4901 VMware虚拟机逃逸漏洞分析【Frida Windows实例】
  8. 免费的录屏软件Capture
  9. 网络编程 : 基于UDP的网络群聊聊天室
  10. 了解常见的开源协议(BSD, GPL, LGPL,MIT)涉及到GNU,LINUX,自由软件(免费开源),开源软件(自由免费开源和商业结合允许二次开发闭源),免费软件(免费使用不开源代码)
  11. 安防监控系统百问百答
  12. 重磅!Google推出了AI人体图像分割工具,惊呆了小伙伴.....
  13. 健康体适能答案2021(持续更新)
  14. 【JAVA】-- 黄金矿工小游戏(一)(实现思路+每步代码)
  15. 成都拓嘉启远:拼多多推广如何自己添加关键词
  16. six MySQL 主主
  17. android设备如何苹果,手机怎么玩:如何像安卓一样管理iPhone文件?_苹果 iPhone 5_手机生活新闻-中关村在线...
  18. oracle 误删数据恢复
  19. python实现杨辉三角思路_Python极简代码实现杨辉三角示例代码
  20. 山东自然人dai开给于企业核定征收纳税

热门文章

  1. java模拟数据库压测_写并发压测 java 脚本你必须会的 3 个类
  2. 多个服务器数据互通_3月21日部分服务器数据互通公告!
  3. 解决win10资源管理器右键菜单卡死问题
  4. 基于python的网站_基于Python的网站爬虫应用研究
  5. jsp 使用base标签 没有作用_JSP学习篇
  6. python一年365天、初始水平值_2020年11月7日,20201107
  7. bytes用str转还是用decode
  8. openmp并行编程_转载:多线程编程方法3OpenMPI框架
  9. 数组不能将数值传给int_java数组查找常见情况
  10. 2019编译ffepeg vs_2020/5-Win10下ffmpeg最简编译方法