mysql主从架构下session一致性无法保障,因readwritesplit中间件默认将select路由至从库,而主从延迟导致刚写入数据尚未同步;maxscale默认不保该一致性,因其不感知业务会话,仅按sql类型或事务边界路由。

MySQL 主从架构下,Session 一致性(即用户会话期间读到刚写入的数据)无法靠复制机制本身保障——因为主从延迟天然存在,readwritesplit 类中间件默认把 SELECT 路由到从库,而刚写入的记录还没同步过去。这不是配置错误,而是设计使然;要解决它,得在路由策略上做针对性干预。
为什么 maxscale 的 readwritesplit 默认不保 Session 一致性
它的默认行为是:所有 SELECT 都走从库,除非显式标注事务或使用 /* master */ 提示。但普通 HTTP 请求中,写操作(如 INSERT)和后续读操作(如 SELECT)通常不在同一个数据库连接里,更不在同一个事务中,中间件无法感知它们属于同一业务会话。
常见现象:
- 用户注册后立即跳转到个人页,查不到刚插入的记录
- 订单创建成功,但“我的订单”列表为空
-
show slave status\G显示Seconds_Behind_Master: 2~5,延迟虽小但足以导致读空
根本原因:中间件不维护会话上下文,也不绑定连接生命周期与业务请求周期。
maxscale 中启用 transaction_replay 或强制主库读的条件
两种实用路径,按风险和复杂度排序:
- 对关键读操作加注释:
/* master */ SELECT * FROM users WHERE id = ?—— 最轻量,需代码配合,适合已知强依赖场景 - 启用
transaction_replay路由器(仅限 MaxScale 2.5+):将整个事务内所有语句发往主库,包括SELECT;但要求应用层显式开启事务(哪怕只读),且不能跨连接复用 - 配置
strict_max_slave_replication_lag=1+max_slave_replication_lag=0:让中间件拒绝向延迟 >0 秒的从库转发请求;代价是可能频繁切回主库,削弱读负载分担效果
注意:transaction_replay 不等于自动识别“同 session”,它只认 BEGIN 到 COMMIT 这个边界;如果应用用的是自动提交模式(autocommit=1),那它完全不生效。
mycat 中 session 级路由的硬限制
Mycat 本身不提供 Session 粘性或事务级读主能力。它的 schema.xml 里只有 loadBalance 和 writeType 配置,作用于 SQL 类型而非会话上下文。
实际能做的只有:
- 把特定表(如
user_session)强制分片到主库节点,通过<table> 的 <code>type="global"或绑定dataNode="dn1" - 在应用层写完后主动 sleep(100ms) 再读 —— 不推荐,掩盖问题而非解决
- 改用
ER分片逻辑,把用户 ID 和 session ID 绑定到同一分片,但主从延迟仍存在,只是缩小了影响范围 - 写操作后,直接返回新数据(而不是查库),避免读从库
- 对必须读库的场景,加一层缓存(如 Redis),写库同时更新缓存,读优先取缓存
- 用
GTID+WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS主动等待从库追上(仅限 MySQL 5.7+),但会阻塞请求,慎用于高并发接口
换句话说:Mycat 的读写分离是“SQL 级”的,不是“Session 级”的;你不能指望它记住“刚才那个连接写过什么”。
真正可控的 Session 一致性方案:应用层兜底
中间件再怎么配,也绕不开主从延迟这个物理事实。最可靠的做法是把一致性判断下沉到业务逻辑:
容易被忽略的一点:readonly=ON 在从库上必须设死,否则任何误写的 UPDATE 都会让主从彻底失联——这比延迟更致命,而且无法自动修复。











