mysql-proxy脚本编程(三)

如“简介”中所述,mysql-proxy向用户提供了6个hook点,让用户实现Lua脚本来完成各种功能,这些hook点是以函数的形式提供的,用户可以实现这些函数,在不同事件、不同操作发生时,做我们期望的事情。
connect_server()
mysql-client向proxy发起连接时,proxy会调用这个函数。用户可以实现该函数,来做一些负载均衡的事情,例如选择将要连向那个mysql-server。假设有多个mysql-server后端,而用户又没有实现这个函数,proxy默认采用轮询(round-robin)策略。
read_handshake()
mysql-server向proxy返回“初始握手信息”时,proxy会调用这个函数。用户可以实现这个函数,来做更多的权限验证工作。
read_auth()
mysql-client向proxy发送认证报文(user_name, password, database)时,proxy会调用这个函数。
read_auth_result()
mysql-server向proxy返回认证结果时,proxy会调用这个函数。
read_query()
认证完成后,mysql-client每次经过proxy向mysql-server发送query报文时,proxy会调用这个函数。用户如果要拦截请求,就可以模拟mysql-server直接返回了,当然用户亦可以实现各种策略,修改请求,路由请求等各种不同的业务逻辑。
read_query_result()
认证完成后,mysql-server每次经过proxy向mysql-client返回query结果时,proxy会调用这个函数。需要注意,如果用户没有显示实现read_query()函数,则read_query_result()函数是不会被调用的。用户可以在此处实现各种合并策略,或者对结果集进行修改。

SHENJIAN@58.COM 画外音:下图是一个各HOOK函数的触发图(请注意请求方向):

继续 画外音:可以发现,最重要的两个函数其实是READ_QUERY()和READ_QUERY_RESULT(),各种SQL的改写与结果集的改写逻辑,都是在这两个函数中实现的,更细节的QUERY过程如下图:

案例一: sql时间统计分析
不妨设mysql-client提交的原sql为:SELECT * FROM City;
proxy可以在read_query()里将其改写为:SELECT NOW(); SELECT * FROM City; SELECT NOW();
这样在返回结果集时,就可以在应用层对sql时间进行记录,以方便统计分析。

案例二:sql性能统计分析
不妨设mysql-client提交的原sql为:SELECT * FROM City;
proxy可以在read_query()里将其改写为: SELECT * FROM City; EXPLAIN SELECT * FROM City;
这样在返回结果集时,就可以在应用层对sql性能进行记录,以方便统计分析。

需要强调的是,这两个案例,由于proxy在read_query()时对sql进行了改写,故在read_query_result()时,mysql-server其实返回了比原请求更多的信息,proxy一定要将多余的信息去掉,再返回mysql-client。多说一句,可以加入一个唯一ID,来对请求sql和返回结果进行配对。

demo
需求:在业务层统计sql日志
实现:tutorial-basic.lua
1 — 如果是COM_QUERY,就将内容打印出来
2 function read_query( packet )
3 if string.byte(packet) == proxy.COM_QUERY then
4 print(“we got a normal query: ” .. string.sub(packet, 2))
5 end
6 end

修改配置并重启proxy:
proxy-lua-script = tutorial-basic.lua

客户端使用黑黑的窗口连接4040端口的proxy,并进行一系列sql操作,操作序列如下:
mysql -h127.0.0.1 -uroot -P4040
show databases;
use im;
show tables;
select * from user;
quit

通过tutorial-basic.lua,会将上述操作都记录到日志中,日志序列如下:
we got a normal query: select @@version_comment limit 1
we got a normal query: show databases
we got a normal query: SELECT DATABASE()
we got a normal query: show tables
we got a normal query: select * from user
SHENJIAN@58.COM 画外音:咦,通过这个日志我才知道,连上数据库会默认发一个SELECT @@VERSION_COMMENT LIMIT 1的请求呢。USE IM;这个请求,为啥变成SELECT DATABASE()了呢?

评论关闭。