当前位置: 首页 > 科技观察

MySQL客户端代码引起的思考

时间:2023-03-15 18:43:39 科技观察

偶然调试代码看了一眼Mysql客户端代码(Java版),最引起我兴趣的是这句话:execSQL是一个很重要的方法,所有SQL语句的最终结果是全部由这段代码实现;这个方法被一个connectionMutex锁包裹(这个锁其实就是Connection对象本身,Mysql客户端使用“反射”所以不能直接使用这个)也就是说——在同一个Mysql连接中任何时候只能执行一条SQL语句.MySQL客户端的“锁”我带着疑惑把这段代码走一遍,画了一张图。MysqlIO是负责网络通信的底层类,使用Java,是BIO通信的“标准”TCP客户端写法。它里面有三个重要的成员变量,mysqlConnection,是一个Socket类型,mysqlOutput,是一个BufferedOutputStream类型,InputStream,是一个InputStream类型。是的,执行SQL语句时,相当于:锁定Socket,此时只有当前连接可以使用这个Socket对象(实际锁定范围更大)mysqlOutput写入数据包,发送Command(内部调用client->server)请求为Command)mysqlInput读取数据包,解析返回结果。如果一个连接提交了多条SQL语句,由于Socket被“锁定”,会被串行执行。这不是错误,这是一个功能。带着疑惑,我查看了Mysql的通信协议。client->server的标准格式如下:3个字节表示后面的“payload”的长度(所以一个mysql单个数据包的最大值为4M);1个字节代表“sequence_id”;这个字段好像没有什么用途***一个是变长的“payload”(长度由***部分决定)server->client(响应数据包)标准格式是1个字节来表示“type”根据不同类型的“payload”,会有不同的变化,比如返回的错误信息,受影响的行数等等。看到这里,我明白了原因。Mysql数据包中没有办法区分一个连接中的“不同数据包”。可以通过Socket向服务器发送两条SQL语句A和B;服务器也会返回两个执行结果。问题是——客户端如何区分哪个是A的执行结果,哪个是B的执行结果?仔细观察上面的数据包只有sequence_id,A分配了一个id,B分配了一个;当服务器返回时带回对应的sequence_id,表示这是某条SQL语句的执行结果。不幸的是-sequence_id根本没有被使用,服务器不会返回sequence_id信息。(见Mysql的响应包)正是由于这个原因,所有的Mysql客户端Connection对象都不是线程安全的。如果要同时执行多条SQL语句,只能构造多个Connection。有意思的是MongoDB的协议格式和Mysql几乎一样,messageLength、requestID、opCode;但是响应数据包“修复”了这个错误,messageLength,responseID,opCode。这意味着mongodb的conneciton不需要阻塞,根本不需要“线程池”。(根据我的观察,connectionsPerHost好像没有用到,这是给大家“安全感”吗?-我们有线程池,随便用)HTTP2.0(多路复用)。在HTTP1.0中,每个HTTP请求都是一个TCP请求。浏览器在加载页面时,会加载大量的css、js、图片、html,并在短时间内发起大量的TCP请求。在HTTP2.0中,css、js、图片可以同时发送给一台主机,并且可以承载在同一个TCP连接中。AMQP中也有类似的设计,叫做Channel;一个TCP连接可以同时被多个线程使用。例如,TCP连接可用于同时“订阅”和“发布”消息。微软的RDP协议也有相同的设计,区分了图片、声音、鼠标、键盘操作(Citrix的ICA设计的32通道就是这个意思)。SSH协议中有一个叫做ChannelMechanism的东西,也是为了“多路复用”(也就是提高ansible的效率)。技术真是太好了,很多东西你“学我”,我“学你”。同样的问题,同样的解决办法,只要用心去做,其实是可以编成书的。比如我们上面讲的可能叫做“MultiplexingProtocolDesignPatterns”。【本文为专栏作家邢森原创文章,转载请联系作者获得授权】点此阅读更多该作者好文

猜你喜欢