使用MySQL-Proxy读写分离时的注意事项(二)
UND_ROWS ..
SELECT FOUND_ROWS()
这种查询是有状态的,应该保证在同一个后端处理,查看rw-splitting.lua脚本可以看到MySQL-Proxy实际上已经对这样的查询进行了 判断,但在实际应用中发现还是存在问题。估计是脚本写得不咋地,实际应用中,建议大家不要使用这样的查询,一来没有可移植性,而来效率也不见得好。
另一个可能会产生问题的查询是:
www.2cto.com
INSERT ... (AUTO_INCREMENT)
SELECT LAST_INSERT_ID()
当系统执行完INSERT后,再执行SELECT时,可能已经被分发到了不同的后端服务器,如果你使用的编程语言是PHP的话,此时应该通过 mysql_insert_id()来得到最新插入的id,每次INSERT结束后,其实对应的autoincrement值就已经计算好返回给PHP 了,你无需再发出一次独立的查询,直接用mysql_insert_id()就可以了。不过很多PHP程序使用的都是SELECT LAST_INSERT_ID()的方式,如AdbDB,CakePHP等等,如果你正在使用它们的话需多加小心。(当使用bigint 时,mysql_insert_id()存在问题,详情见手册,不过对于大多数人而言,bigint基本不会遇到,所以你可以无视这个问题)
注:对于这两个问题,官方BUG库里有人给出了相应的补丁。
脚本问题
MySQL-Proxy读写分离的功能是通过lua脚本(rw-splitting.lua)实现的,但是这个脚本年久失修,问题多多,比如说使用时可能会出现:
ERROR 1105: can't change DB to on slave
出现这个问题的原因在于当客户端发出查询时,MySQL-Proxy会比较当前客户端所处数据库和服务器所处
数据库是否一致,如果不一致则会在服务端尝试执行一个"USE 数据库"的操作,一个可能性是主从服务器的数据库结构不同,在USE一个不存在的数据库的时候自然会出错,还有一个原因有些查询操作并没有所处数据库这个上下文,比如说SHOW DATABASES这个查询,并不需要事先“USE 数据库”,只要连上服务器就可以执行,这时候如果还尝试同步客户端和服务端所处的数据库,出错就是无法避免的事了。
rw-splitting.lua恰恰没有屏蔽后者所描述的情况,修复方法如下,在合适的位置加入粗体代码,
276 if cmd.type ~= proxy.COM_INIT_DB and
277 c.default_db and c.default_db ~= "" and c.default_db ~= s.default_db then
if is_debug
278 print(" server default db: " .. s.default_db)
279 print(" client default db: " .. c.default_db)
280 print(" syncronizing")
end
281 proxy.queries:prepend(2, string.char(proxy.COM_INIT_DB) .. c.default_db) www.2cto.com
282 end
在lua中,~=是不等于的意思,另外,lua里空字符串""用在if里被认为是true,所以单靠c.default_db不够。
顺手加上is_debug的判断,不然即使不是debug状态,服务器的命令行里也会偶尔冒出一些调试信息。
此外,新版MySQL-Proxy里,虽然源代码包里有rw-splitting.lua脚本,但缺省并没有安装,你需要手动拷贝,而且数据结构发生了变化,脚本需要按照数据结构的变化做出适当的修改,可以参考作者的描述操作,或者参考官方Bug管理或者直接
下载。值得期待的是现在有了专门的MySQL-Proxy-Lua-Scripts项目,希望开发进度能跟上来。
作者 root_zhang