Mysql-Proxy 读写分离的各类坑,特别是复制延迟时

延迟问题
读写分离不能回避的问题之一就是延迟,能够考虑Google提供的SemiSyncReplicationDesign补丁。mysql

端口问题
MySQL-Proxy缺省使用的是4040端口,若是你想透明的把3306端口的请求转发给4040的话,那么能够:sql

iptables -t nat -I PREROUTING -s ! 127.0.0.1 -p tcp --dport 3306 -j REDIRECT --to-ports 4040数据库

查询乱码编程

链接上MySQL-Proxy后,执行查询时,随机出现乱码。出现此问题的缘由是当咱们使用MySQL-Proxy读写分离时,一般会有多个后端服务器,客户端发出查询请求时,通常会先发出一条相似"SET NAME gbk"的语句来声明客户端编码,而后再发出实际查询的SQL语句,但MySQL-Proxy可能会把这两条语句分发给不一样的后端服务器,因而就出现了乱码。后端

解决方法是强行指定后端服务器的字符编码:服务器

init-connect='SET NAME gbk'tcp

default-character-set=gbk
skip-character-set-client-handshake编程语言

若是使用init-connect,则须要注意操做用户不能有SUPER权限,不然此选项无效。编码

即使作好了以上的设置后,还有可能会出现乱码,好比说数据库是gbk的,当咱们用PHPMyAdmin链接MySQL-Proxy时,查询仍是会出现乱码,不过这是正常的!由于PHPMyAdmin使用的是utf8编码,它发出的“SET NAMES utf8”语句被skip-character-set-client-handshake屏蔽了,因此出现乱码。lua

有状态的查询

一些有状态的特殊的查询可能失效,好比说:
SELECT SQL_CALC_FOUND_ROWS ..
SELECT FOUND_ROWS()

这种查询是有状态的,应该保证在同一个后端处理,查看rw-splitting.lua脚本能够看到MySQL-Proxy实际上已经对这样的查询进行了 判断,但在实际应用中发现仍是存在问题。估计是脚本写得不咋地,实际应用中,建议你们不要使用这样的查询,一来没有可移植性,而来效率也不见得好。

另外一个可能会产生问题的查询是:
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偏偏没有屏蔽后者所描述的状况,修复方法以下,在合适的位置加入代码,
    if cmd.type ~= proxy.COM_INIT_DB and
        c.default_db and c.default_db ~= "" and c.default_db ~= s.default_db then
        if is_debug

                print("    server default db: " .. s.default_db)                  

                print("    client default db: " .. c.default_db)

                print("    syncronizing")
        end

    proxy.queries:prepend(2, string.char(proxy.COM_INIT_DB) .. c.default_db)

    end
在lua中,~=是不等于的意思,另外,lua里空字符串""用在if里被认为是true,因此单靠c.default_db不够。

顺手加上is_debug的判断,否则即便不是debug状态,服务器的命令行里也会偶尔冒出一些调试信息。

相关文章
相关标签/搜索