MySQL Proxy
2010-09-14 09:24:07 阿炯

MySQL-Proxy是处在你的MySQL数据库客户和服务端之间的程序,它还支持嵌入性脚本语言Lua。这个代理可以用来分析、监控和变换(transform)通信数据,它支持非常广泛的使用场景:
* 负载平衡和故障转移处理
* 查询分析和日志
* SQL宏(SQL macros)
* 查询重写(query rewriting)
* 执行shell命令

MySQL Proxy更强大的一项功能是实现“读写分离(Read/Write Splitting)”。基本的原理是让主数据库处理事务性查询,而从数据库处理SELECT查询;数据库复制被用来把事务性查询导致的变更同步到集群中的从数据库。

MySQL Proxy处于客户端应用程序和MySQL服务器之间,通过截断、改变并转发客户端和后端数据库之间的通信来实现其功能,这和WinGate之类的网络代理服务器的基本思想是一样的。代理服务器是和TCP/IP协议打交道,而要理解MySQL Proxy的工作机制,同样要清楚MySQL客户端和服务器之间的通信协议,MySQL Protocol包括认证和查询两个基本过程:

认证过程包括:
1. 客户端向服务器发起连接请求
2. 服务器向客户端发送握手信息
3. 客户端向服务器发送认证请求
4. 服务器向客户端发送认证结果

如果认证通过,则进入查询过程:
1. 客户端向服务器发起查询请求
2. 服务器向客户端返回查询结果

当然,这只是一个粗略的描述,每个过程中发送的包都是有固定格式的。MySQL Proxy要做的,就是介入协议的各个过程。首先MySQL Proxy以服务器的身份接受客户端请求,根据配置对这些请求进行分析处理,然后以客户端的身份转发给相应的后端数据库服务器,再接受服务器的信息,返回给客户端。所以MySQL Proxy需要同时实现客户端和服务器的协议。由于要对客户端发送过来的SQL语句进行分析,还需要包含一个SQL解析器。可以说MySQL Proxy相当于一个轻量级的MySQL了,实际上MySQL Proxy的admin server是可以接受SQL来查询状态信息的。

MySQL Proxy通过lua脚本来控制连接转发的机制。主要的函数都是配合MySQL Protocol各个过程的,这一点从函数名上就能看出来:
* connect_server()
* read_handshake()
* read_auth()
* read_auth_result()
* read_query()
* read_query_result()

至于为什么采用lua脚本语言,我想这是因为MySQL Proxy中采用了wormhole存储引擎的关系。

Jan Kneschke在《MySQL Proxy learns R/W Splitting》中介绍了这种技巧,他还谈到了连接池的问题:为了实现读写分离我们需要连接池。我们仅在已打开了到一个后端的一条经过认证的连接的情况下,才切换到该后端。MySQL协议首先进行握手。当进入到查询/返回结果的阶段再认证新连接就太晚了。我们必须保证拥有足够的打开的连接才能保持运作正常。

实现读写分离的LUA脚本是简单明了的:
-- 读写分离
--
-- 发送所有的非事务性SELECT到一个从数据库
if is_in_transaction == 0 and
packet:byte() == proxy.COM_QUERY and
packet:sub(2, 7) == "SELECT" then
local max_conns = -1
local max_conns_ndx = 0

for i = 1, #proxy.servers do
local s = proxy.servers[i]

-- 选择一个拥有空闲连接的从数据库
if s.type == proxy.BACKEND_TYPE_RO and
s.idling_connections > 0 then
if max_conns == -1 or
s.connected_clients < max_conns then
max_conns = s.connected_clients
max_conns_ndx = i
end
end
end

-- 我们找到了一个拥有空闲连接的从数据库
if max_conns_ndx > 0 then
proxy.connection.backend_ndx = max_conns_ndx
end
else
-- 发送到主数据库
end

return proxy.PROXY_SEND_QUERY


项目主页:http://dev.mysql.com/downloads/mysql-proxy/