公司最近需要一个扩展性良好的Mysql主从分离方案,老大要求对业务层的代码改动要小。
业务层代码改动小,意味着要在连接层直接把读/写请求分发,而不是到业务层通过”SQL识别”的来分离。
脑子里有两套方案, Mysqlnd_ms 和 Mysql-Proxy。两个方案各有优/缺点
Mysqlnd_ms
优点:
1. PHP官方推荐的扩展,PECL安装方便
- 基于PHP扩展对SQL进行解析,进而分离请求,对业务层代码改动小,性能损失也小
- 扩展本身已经在社区打滚有些年头了,较稳定,口碑好
- 入门级文档清晰,易配置易上手
- 有连接池
- 功能强大,支持SQL Hint, 事务,最终/Session/强一致性等场景
缺点:
1. 基于扩展的封装,扩展的实现对上层透明。扩展本身缺乏debug工具, 所以真遇到问题可能比较难查
- Failover 策略较基础
- 缺乏中文文档
- 强一致性的场景会有些坑
Mysql-Proxy
优点:
1. 来自Mysql官方的解决方案
- 性能好,资源占用少
- 有连接池
- 配置简单/文档众多
- 应有广泛,口碑好
缺点:
1. 需要第三方脚本(Lua)支持
鉴于”对业务层代码改动要小”这个需求,以及笔者对Mysql-Proxy 使用较少,所以在简单比较之后,果断选择了Mysqlnd_ms。
安装
配置
1. 创建配置文件 /usr/local/etc/php/php.d/mysqlnd_ms.ini
2. 创建主从策略文件 /usr/local/etc/php/php.d/mysqlnd_ms.conf (位置由mysqlnd_ms.ini指定)
Demo 主从写成了同一个库
/etc/init.d/php-fpm restart #重启fpm生效
3. 一些基本的参数说明
参数名称 | 描述 |
---|---|
master | 主库配置 |
host | ip or hostname |
port | 端口 |
socket | 连接套接字 |
db | 库名字 |
user | 用户名 |
password | 用户密码 |
connect_flags | 连接参数 |
slave | 从库配置 |
global_transaction_id_injection | GTID的配置 详见 http://cn2.php.net/manual/zh/mysqlnd-ms.plugin-ini-json.php |
filter | 选择从库的策略 random/roundrobin/user/user_multi/node_groups 策略详情详见http://cn2.php.net/manual/zh/mysqlnd-ms.plugin-ini-json.php |
failover | 故障转移 disabled (默认), master(读从失败就连主库),loop_before_master(读从失败轮寻其他从库再读主库) |
lazy_connections | 被动连接 |
server_charset | 服务端字符集 |
trx_stickiness | 事务控制策略 |
使用
- 通常情况下,使用mysqlnd_ms 不需要改动业务端的代码,只需要在连接数据库时,使用配置中的host,即可实现主从分离,如下:
- 采取主从分离,就一定存在着同步延迟的问题。 在一个繁忙的服务中,主/从同步会存在延迟。mysqlnd_ms 将这种场景细分为:
- 最终一致性:用户可能无法立即看到自己写入的数据
- session一致性:用户自己可以立即看到自己写入的数据
- 强一致性:所有的用户都可以立即看到其他用户的写入的数据
mysqlnd_ms 针对不同的场景,提供不同的策略。如果对延迟比较敏感(如:用户提交表单后,页面立即跳转展示用户提交的数据),在编码的过程中还需要参考下述文档
- SQL Hints: http://cn2.php.net/manual/zh/mysqlnd-ms.quickstart.sqlhints.php
- 事务: http://cn2.php.net/manual/zh/mysqlnd-ms.quickstart.transactions.php
- 服务级别和一致性: http://cn2.php.net/manual/zh/mysqlnd-ms.quickstart.qos-consistency.php
- Global transaction IDs (GTID):http://cn2.php.net/manual/zh/mysqlnd-ms.quickstart.gtid.php
- Cache integration: http://cn2.php.net/manual/zh/mysqlnd-ms.quickstart.cache.php
一些陷阱
- Mysqlnd_ms 的行为是这样的。 所有的Select 操作会访问从库, 除Select 其他的所有操作都访问主库,包括(SHOW, SET等)。
所以习惯使用mysql变量来编程的同学要注意了,如:上面的第二条语句会因为在从库找不到id这个变量而报错, 这种情况要SQL Hint 给Mysql一些提示
- Mysqlnd_ms 并不支持 mysqli 的multi_statements。 如使用mysqli_multi_query() 来批量执行SQL
上面的第一条SQL会被识别为访问从库,Mysqlnd_ms 无法拆分一个批量操作,所以第二条Insert 也会进从库而造成数据不一致。
- 事务的默认行为也和上述第二点一样,无法拆分一个事务,所以”读/写”请求尽量不要写进同一个事务中。如果一定要写,要使用SQL Hint 给Mysqlnd_ms 一些提示。