您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
浅析MySQL协议——从一个bug谈起
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
浅析MySQL协议——从一个bug谈起
Apache ShardingSphere
2021-01-13
IP归属:未知
23160浏览
# Bug描述 关于bug的描述,这里使用了项目的issue模板,大家在提bug的时候也请一定遵循这个模板。在本文中使用中文描述,但大家提issue的时候记得用英文,毕竟老外也会来看的。 **Which version of Sharding-Sphere do you using?** 3.0.0.M2-SNAPSHOT **Which project do you using? Sharding-JDBC or Sharding-Proxy?** Sharding-Proxy **Expected behavior** 持续正常的INSERT数据。 **Actual behavior** 循环插入200万数据,当插入到100万左右的时候,客户端无响应。 **Reason analyze** 客户端无响应可能是Proxy阻塞引起的,也可能是Proxy导致客户端阻塞引起的。 **Steps to reproduce the behavior** 持续插入数据到100万条以上即可复现。 # Bug初步定位 经过初步分析,Proxy日志正常,MySQL里也能查到用户插入的最后一条数据。那么,问题应该就出在最后一条数据插入成功之后。可能有两种情况:1. 插入数据成功后,MySQL返回的消息被Proxy错误解码,导致Proxy异常;2. Proxy返回给客户端的消息编码错误,导致客户端异常。 第一种情况,如果Proxy解码错误,Netty会抛异常,TCP连接断开,Proxy会把异常打印到日志,所以这种情况可能性不大。第二种情况可能性较大,需要分析Proxy和客户端之间的消息交互,进一步定位。 # MySQL协议 准确的说应该是MySQL Client/Server协议,另一个叫X Protocol的暂不涉及。任何MySQL客户端(CLI、GUI、MySQL驱动)与MySQL服务器通信,都需要使用这个协议。这个协议包含一系列的命令消息(COM)和响应消息(Response)、编解码方式和交互流程。 ![](//img1.jcloudcs.com/developer.jdcloud.com/529ccc9a-51e3-4841-ac1f-911aaa5d6c7b20210113122309.jpg) 上图展示了绝大多数协议消息的交互过程:对于DQL,服务器会返回FIELD_COUNT(列数)、FIELD(列的描述信息,如类型等)、ROW(每一行数据的值)、EOF(结束标志);而对于DML,只需要返回OK或者ERR,通知客户端命令执行成功或失败。 经过之前的分析,我们可以把范围缩小到OK_Packet这条响应消息上,这条消息由服务器发送给客户端,表示一条命令的成功执行。也就是说,在插入数据成功后,MySQL服务器会发送OK_Packet给Proxy,后者又会把这条消息发送给客户端。消息格式如下: ![](//img1.jcloudcs.com/developer.jdcloud.com/064752ec-cd10-4024-b966-1f8d499c710620210113122338.jpg) 这个表格看起来有点抽象,我们可以先用Wireshark抓个真实包看看,解析出来会直观一些: ![](//img1.jcloudcs.com/developer.jdcloud.com/dc53d91c-0f17-46d6-b8cb-bbb8ad6de67c20210113122405.jpg) 做协议开发永远离不开Wireshark,后面拓海会手把手教你使用Wireshark进行网络抓包。真实的数据看起来就简单明了一些了,有包长度、包序号、影响行数、最后插入id、服务器状态等信息。我们找一个字段,来说明其编解码方式。例如last_insert_id,它的类型是int<lenenc>,值为1083。 MySQL协议里存储数据的方式是小端字节序,这是什么意思呢?比如一个值0x1234,采用大端字节序的存储方式就是0x1234,而小端方式则是0x3412。MySQL采用这样的字节序很不友好,对于一个通信协议,应该使用网络字节序传输数据,而不是机器字节序。 ![](//img1.jcloudcs.com/developer.jdcloud.com/e39035af-9046-4040-93fc-e5c7e08d4cb920210113122432.jpg) 回到协议,int<lenenc>是变长编码的整数类型,不同范围的数值使用不同的长度来编码: ![](//img1.jcloudcs.com/developer.jdcloud.com/d1160530-f71a-4979-80b4-763e72d1c6c020210113122453.jpg) 由上可知,1083这个值应该是0xfc 加上2个字节来表示: ![](//img1.jcloudcs.com/developer.jdcloud.com/0505e154-5eeb-447b-af59-0def0491c53a20210113122519.jpg) 1083 = 0x043b,小端字节序就是0x3b04。有了这些基础,我们就可以通过抓包来分析产生bug的根本原因了。 # Bug最终定位 以下就是当时抓到的包,看起来非常的“正常”,插入一条数据,返回一条成功的响应,Wireshark也没有解析出错。看现象就是客户端停止了插入数据。 ![](//img1.jcloudcs.com/developer.jdcloud.com/501b5cd2-4640-4c0f-9a41-2fa6f42193ca20210113122541.jpg) 经过多次试验,最后一条INSERT总是卡在t_order_item这个表上,它与t_order表有什么本质区别呢?了解Sharding-Sphere-Example的同学对这两个表应该很熟悉,t_order使用的是雪花算法生成的分布式主键,值很大,而t_order_item使用的是MySQL自增主键。为什么主键值小的表会卡住? 又经过一系列调查,发现t_order_item表也不是每次都卡住,这个表有两个分片,在其中一个分片上永远没问题,而另一个分片上只要插入数据就会卡。这两个表又有什么区别呢?其实,由于分片的不均匀,一个表的数据量多,另一个数据量少,除此之外没什么不同。数据量多的表大概6万多条数据。 在拓海百思不得其解的时候,亮神和赵帅看出了些端倪: ![](//img1.jcloudcs.com/developer.jdcloud.com/1c4001c2-f279-4d0b-ab6d-602b535497d920210113122608.jpg) 最后一条OK_Packet的Last INSERT ID值太大了,一个表不可能有这么多数据。通过观察发现,表的数据量已经大于65535,应该使用3个字节存储 ![](//img1.jcloudcs.com/developer.jdcloud.com/b2e7be73-a599-4a27-9788-4f8df4942aa020210113122626.jpg) 然而程序里却使用了四个字节。四个字节的编码在协议里是不存在的,这必然导致客户端jdbc解析出错。至于为什么wireshark没有解析出错,那一定是因为它的解析逻辑错了。 # 使用Wireshark分析MySQL协议 Wireshark可以理解为是一个带图形界面的tcpdump,同时集成了非常多的协议解析插件,可以帮助我们方便的抓取和分析MySQL协议。需要注意的是,Wireshark只能抓网络协议包,但MySQL协议还有其他通信方式,如Shared memory,Named pipes,这些是抓不到的,最好不要开这些参数。 **Linux命令行环境下:**可以先用tcpdump抓包,然后将包发送到本地,使用Wireshark解析。命令如下: tcpdump -i any port 3307 -w test.pcap any: 表示所有网络接口。 port: tcp端口,Proxy的默认端口是3307,如果你关心MySQL的包,就改为3306。 pcap: 一种通用的数据流格式,Wireshark可以直接打开。 用Wireshark打开test.pcap,你会发现除了MySQL协议,还显示很多TCP协议的ACK消息: ![](//img1.jcloudcs.com/developer.jdcloud.com/60f296a9-b888-4d82-93c6-7813fd6ee58e20210113122653.jpg) 在过滤器中输入mysql,进行过滤即可: ![](//img1.jcloudcs.com/developer.jdcloud.com/7664d45a-6803-404b-901b-e87b1503a41c20210113122711.jpg) **Windows环境下**:直接使用Wireshark抓包,一边抓包一边解析。本地的测试环境往往会同时运行客户端、Proxy和MySQL服务器,它们的通信在同一台机器上是不走网卡的,然而windows系统又没有提供本地回环网络的接口,所以这种情况下是什么也抓不到的。 为了解决这个问题,我们需要把Wireshark在Windows系统上默认的抓包工具WinPcap替换为Npcap,在这里下载:[https://nmap.org/npcap/#download](https://nmap.org/npcap/#download) 安装前先在Wireshark的安装目录下卸载WinPcap。安装时勾选如下选项: ![](//img1.jcloudcs.com/developer.jdcloud.com/36e203c7-2680-4bbe-b9cc-ed6e79a96da620210113122736.jpg) 安装完成再次启动Wireshark时,就会看到在网络接口列表中,多了一项Npcap Loopback adapter,这个就是来抓本地回环包的网络接口了: ![](//img1.jcloudcs.com/developer.jdcloud.com/bce112ce-05f5-4a72-a1dc-dd00488113dd20210113122753.jpg)
原创文章,需联系作者,授权转载
上一篇:轻量级BDP知多少
下一篇:关系型数据库尚能饭否?NoSQL、NewSQL谁能接棒?
Apache ShardingSphere
文章数
96
阅读量
231327
作者其他文章
01
突破关系型数据库桎梏:云原生数据库中间件核心剖析
数据库技术的发展与变革方兴未艾,NewSQL的出现,只是将各种所需技术组合在一起,而这些技术组合在一起所实现的核心功能,推动着云原生数据库的发展。 NewSQL的三种分类中,新架构和云数据库涉及了太多与数据库相关的底层实现,为了保证本文的范围不至太过发散,我们重点介绍透明化分片数据库中间件的核心功能与实现原理,另外两种类型的NewSQL在核心功能上类似,但实现原理会有所差别。
01
Apache ShardingSphere数据脱敏全解决方案详解(上)
Apache ShardingSphere针对新业务上线、旧业务改造分别提供了相应的全套脱敏解决方案。
01
Shardingsphere整合Narayana对XA分布式事务的支持(4)
ShardingSphere对于XA方案,提供了一套SPI解决方案,对Narayana进行了整合,Narayana初始化流程,开始事务流程,获取连接流程,提交事务流程,回滚事务流程。
01
从中间件到分布式数据库生态,ShardingSphere 5.x革新变旧
5.x 是 Apache ShardingSphere从分库分表中间件向分布式数据库生态转化的里程碑,从 4.x 版本后期开始打磨的可插拔架构在 5.x 版本已逐渐成型,项目的设计理念和 API 都进行了大幅提升。欢迎大家测试使用!
最新回复
丨
点赞排行
共0条评论
Apache ShardingSphere
文章数
96
阅读量
231327
作者其他文章
01
突破关系型数据库桎梏:云原生数据库中间件核心剖析
01
Apache ShardingSphere数据脱敏全解决方案详解(上)
01
Shardingsphere整合Narayana对XA分布式事务的支持(4)
01
从中间件到分布式数据库生态,ShardingSphere 5.x革新变旧
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号