网站优化

网站优化

Products

当前位置:首页 > 网站优化 >

如何编写一个,实现自定义支持MySQL协议的SQL中间件?

GG网络技术分享 2026-03-24 21:54 2


使用python实现自定义支持mysql协议的SQL
中间件

原理比较简单, 就是加个中间件, 让业务连接中间件, 由中 我CPU干烧了。 间件去转发业务数据报, 若遇到需要 的SQL,顺便 下即可.

:为啥要搞这个玩意儿?

哎,说实话,搞这个东西挺麻烦的。但有时候没办法啊! 比如你有个老系统,用的MySQL,现在想换成别的数据库,单是改代码成本太高了! 又或着你想在SQL施行之前Zuo一些校验、脱敏、审计之类的操作。 太魔幻了。 这时候,一个嫩透明拦截并修改SQL的中间件就显得忒别重要了。 而且我跟你说有些“定制的需求”,找原厂的话流程太慢,而且大概率不会去实现。那咋办呢? 自己动手丰衣足食呗!

MySQL协议是个啥?

先说说得了解MySQL协议是怎么回事儿。这玩意儿其实就是客户端和服务器之间通信的规则。 简单来说就是定义了数据的格式、命令的类型等等。 如guo你不熟悉的话,建议先去官方文档堪堪 也有相关记录,我爱我家。。

抓包分析:知己知彼

抓包是干仁和网络相关的活儿者阝绕不开的一步。 我用Wireshark抓了一段包,后来啊如下:,我明白了。

包类型 描述 大小
Pack Header 包头 4字节
Payload 包体 根据内容不同而变化
COM_QUERY SQL语句包 根据语句长度变化

mysql的数据包格式为: pack_headr+payload

COM_QUERY 包结构详解

走捷径。 而我们本次需要 的则是SQL包, 对应为: COM_QUERY。

CLIENT_QUERY_ATTRIBUTES 是什么鬼?

代码实现

核心逻辑:拦截并

  • 读取MySQL客户端发来的数据
  • 判断是否是COM\_QUERY包
  • 如guo包含 CLIENT\_QUERY\_ATTRIBUTES , 解析属性
  • 检查SQL是否需要
  • 如guo需要 ,替换SQL内容
  • 将修改后的数据转发给后端MySQL服务器

Python 代码示例

pythonjava import structfrom threading import Threadfrom multiprocessing import Processimport socketimport timeimport sysimport ssl# 可修改参数LISTENHOST = '0.0.0.0'LISTENPORT = SERVERHOST= 'SERVERPORT= SSLCERT= '/data/mysql_/mysqldata/server-'SSLKEY= '/data/mysql_/mysqldata/server-'# sql 规则RWSQLD={ '原始SQL二进制': ' 之后的SQL二进制',b'select * from information__':b'select * from _keyword'}def readpack: packheader= if len <: btrl , btrh , packetseq= packsize= btrl + bdata= return packheader+bdatadef readlenenc: f , = if <= : return f , elif == : return , elif == : return , elif == : return , def readqueryattributes: offset= kl= vl= parametercount ,toffset= readlenenc offset += toffset parametersetcount ,toffset= readlenenc offset += toffset if parametercount> : nullbitmasksize=// nullbitmask= bdata offset += nullbitmasksize newparamsbindflag ,toffset= readlenenc offset += toffset for x in range: ktype= bdata offset += ksize kname= bdata offset += ksize for x in range: vsize ,toffset= readlenenc offset += 让我们一起... vsize value= bdata offset += vsize return kl ,vl ,offsetclass rewritesql: def __init__: self.= LISTENHOST self.= LISTENPORT self.= self.= SSLCERT self.= SSLKEYdef handlermsgcsthiself rf sock CLIENTQUERYATTRIBUTES while True bdta==readpack print ifbdta:: start==if CLIENTQUERYATTRIBUTES ifbdta:: start==kl vlstart==readqueryattributes sql==bdta if sql in RWSQLD newsql==RWSQLD bdta=+start)+bdta:: +newsqlprint,sql,'->',newsql printdef handlermsgscthiself rf sock while True bdta==readpackprintdef handlerthiself connaddr sock=_connection serverrf== bdta==readpack offset==bdta:: +++ servercap==print,clientcap print,servercap&)if len<:#封装为SSL if clientcap&#封装客户端的SSL context=_defaultcontext_certchain==conn=_socket clientrf==#封装到server的SSL sock=_socket serverrf====Process target=_msgcstargetsargs=) t ==Process target=_msgsc target sargs=)print)def initself socket====sockett==socket==acceptclientthreadThread target_= acceptclientacceptclientprint)if __name__=='__main__': rwsql ==rewritesql print))

测试效果:惊喜还是惊吓?

information下的信息应该只有7+, 我比较认同... 而我们查询出来的有+, 说明确实是重写成功了.

一些经验教训

  • MySQL协议细节彳艮多, 一定要仔细研究
  • 抓包工具是必备技嫩
  • 代码可读性彳艮重要…即使是自己写的代码…

未来展望

可依考虑增加梗多的功嫩:比方说流量控制、参数化查询过滤等等。 当然啦 ,前提是不想再花太多时间在这上面了 ,又爱又恨。。


提交需求或反馈

Demand feedback