MySQL客户端代码引发的思考

开发 开发工具
execSQL是一个非常重要的方法,所有SQL语句的最终都是交由这个代码来实现的;这个方法被一个connectionMutex锁(这个锁其实就是Connection对象自己,Mysql客户端通过使用了“反射”所以不能直接使用this)包裹着这就意味着——在同一时刻一个Mysql连接只能执行一条SQL语句。

一次偶然的机会debug代码瞅了一眼Mysql客户端的代码(Java版本),最引发我兴趣的是这一句:

execSQL是一个非常重要的方法,所有SQL语句的最终都是交由这个代码来实现的;这个方法被一个connectionMutex锁(这个锁其实就是Connection对象自己,Mysql客户端通过使用了“反射”所以不能直接使用this)包裹着这就意味着——在同一时刻一个Mysql连接只能执行一条SQL语句。

MySQL客户端的“锁”

带着疑问我走读了这段代码,画一幅图

MysqlIO是负责网络通讯的底层类,使用的是Java是BIO通讯属于“标准”的TCP客户端写法。它内部有三个重要的成员变量

  • mysqlConnection是一个Socket类型
  • mysqlOutput是一个BufferedOutputStream类型
  • InputStream是一个InputStream类型(Mysql做了一点封装理解成BufferedInputStream也没什么问题)

这三个成员变量的初始化是在MySqlIO的构造函数完成的,当执行SQL语句的时相当于:

  • 锁住Socket,此时只有当前连接可以使用这个Socket对象(实际的锁范围更大)
  • mysqlOutput写入数据包,发送Command(内部称客户端->服务器的请求为Command)
  • mysqlInput读取数据包,解析返回结果

如果有多条SQL语句递交给一个连接因为Socket被“锁”所以会变成串行执行。

这不是bug,是特性

带着疑问我翻看了Mysql的通讯协议,客户端->服务器的标准格式如下:

  • 3字节表示后面“payload”的长度(所以mysql单数据包***值是4M);
  • 1字节表示“sequence_id”;这个字段似乎没有什么用途
  • ***一个是变长的“payload”(长度由***部分决定)

服务器端->客户端(响应数据包)的标准格式是

 

  • 1字节表示“类型”
  • 根据不同的类型“payload”会有不同的变化,比如返回的错误信息、受影响的行数等

看到这里我明白原因了。

Mysql的数据包中没有办法区分出一个连接中“不同的数据包”。A、B两条SQL语句,可以通过一个Socket发送到服务器端;服务器端也会返回两个执行结果。问题是——客户端如何区分出哪个是A的执行结果哪个是B的执行结果呢?

仔细观察上面的数据包唯一的方式是sequence_id,A分配一个id,B分配一个;服务器端在返回的时候把相应的sequence_id带回来表示这是某个SQL语句的执行结果。遗憾的是——sequence_id根本没用到,服务器端不会返回sequence_id信息。(看Mysql的响应数据包)

正是因为这个原因导致了所有的Mysql客户端Connection对象都不是线程安全的。如果想要同时执行多条SQL语句就只能构造多个Connection。

有意思的是MongoDB的协议格式几乎和Mysql的一样,messageLength,requestID,opCode;但是响应数据包“修正”了这个bug,messageLength,responseID,opCode。这就意味着mongodb的conneciton是不需要阻塞的而且根本不需要“线程池”。(根据我观察connectionsPerHost似乎是没有用到,难道这个是为了给大家“安全感”?——我们带线程池,放心用吧)

更多思考

我想到了更多东西,HTTP 2.0的多路复用(Multiplexing)。在HTTP1.0中每个HTTP请求都是一个TCP请求,浏览器载入页面的时候会大量加载css、js、图片、html短时间内会发起大量的TCP请求。在HTTP2.0中同时向一台主机发起css、js、图片可以被承载在同一个TCP连接中。

AMQP中也有相似的设计,叫Channel;一个TCP连接可以被多个线程同时使用。比如用一个TCP连接可以同时实现“订阅”和“发布”消息。

微软RDP协议中也有相同的设计,区分图片、声音、鼠标、键盘操作(Citrix的ICA设计号称的32个通道就是这个意思)。

SSH协议中有一个叫Channel Mechanism的东西,它也是为了实现“多路复用”的(这意味着提高了ansible的效率)。

技术就是这么奇妙,很多东西都是你“借鉴我”,我“借鉴你”。相同的问题相同的解决办法,如果用心其实可以汇编成一本书,比如我们上面讲的或许就可以叫——“多路复用的协议设计模式”吧。

【本文是51CTO专栏作者邢森的原创文章,转载请联系作者本人获取授权】

戳这里,看该作者更多好文

责任编辑:武晓燕 来源: 写程序的康德
相关推荐

2022-08-01 08:04:58

MySQL客户端字符

2014-08-11 16:35:35

KafkaJava客户端

2021-09-22 15:46:29

虚拟桌面瘦客户端胖客户端

2010-04-21 12:57:33

RAC负载均衡配置

2010-03-18 16:49:43

Java Socket

2010-12-30 12:13:03

Skype宕机Windows客户端漏

2014-06-12 13:44:19

2011-03-21 14:53:36

Nagios监控Linux

2011-04-06 14:24:20

Nagios监控Linux

2011-08-17 10:10:59

2020-11-17 08:53:07

MySQL数据库技术

2010-06-02 10:27:56

MySQL客户端工具

2010-03-18 17:30:46

Java Socket

2010-05-31 10:11:32

瘦客户端

2011-03-02 14:36:24

Filezilla客户端

2011-03-24 13:00:31

配置nagios客户端

2010-12-21 11:03:15

获取客户端证书

2011-10-26 13:17:05

2010-03-18 17:47:07

Java 多客户端通信

2010-07-22 12:24:31

Telnet客户端
点赞
收藏

51CTO技术栈公众号