读写分离的核心风险
读写分离把查询压力分散到从库,代价是延迟读:刚写完立刻查从库可能读到旧数据,这类问题在用户侧表现为「提交了但没显示」。常见套路有三种:写后短窗强制读主库、用注释或代理规则给关键查询打主库标记、会话粘连保证同一会话读一致。选哪种取决于业务对延迟的容忍度。
基本信息
| 适用 | 一主多从 + 中间件(ProxySQL、MyCat)或应用层数据源路由 |
|---|---|
| 收益 | 读扩展、降低主库压力、报表查询隔离 |
| 风险 | 延迟导致读到旧值,缓存双删类逻辑要一并重审 |
| 兜底 | 半同步复制可缩小延迟窗口,但仍不保证实时一致 |
常见方案与代价
| 方案 | 适用场景 | 代价 |
|---|---|---|
| 写后短窗读主库 | 下单、支付等写完立刻要读的场景 | 窗口内主库压力上升,需要统一封装 |
| 关键查询强制主库 | 对账、库存扣减校验等强一致查询 | 需在 SQL 或代理规则里显式标注,易漏 |
| 会话粘连 | 同一用户会话内读一致要求高 | 负载均衡变差,连接利用率下降 |
| 从库追平校验 | 可接受毫秒级补偿的场景 | 实现复杂,超时后仍要回主库 |
| 直连从库的报表查询 | 延迟容忍高的统计与导出 | 需明确标注延迟容忍并监控追平 |
代理层强制主库示例
代理层强制主库示例
-- ProxySQL:为特定语句打标走写组(示例,规则按业务调整)
INSERT INTO mysql_query_rules (rule_id, active, match_digest, destination_hostgroup, apply)
VALUES (10, 1, '^SELECT.*FOR UPDATE', 10, 1);
LOAD MYSQL QUERY RULES TO RUNTIME; # 规则装载到运行时
SAVE MYSQL QUERY RULES TO DISK; # 持久化,避免重启丢失
-- 应用侧另一种做法:在 SQL 注释里打标,由中间件识别
-- SELECT /* master */ balance FROM account WHERE id = 1;典型使用场景
- 读多写少的业务用从库分流,主库只承担写入
- 报表与导出走从库,避免大查询拖慢在线交易
- 活动高峰扩容只读节点,弹性提升查询吞吐
- 用户提交表单后立刻跳转详情页,用写后读主保证不出现空数据
- 跨机房读取就近路由,按延迟容忍度决定是否允许读从库
一致性要求高的报表与对账应直连从库并单独监控追平时间,而不是依赖业务默认读从库;默认策略一旦是「读从库」,少数强一致场景会被长期忽视。
延迟读会在缓存双删逻辑上放大问题:缓存删了、从库还没追上,回源又读到旧值并写回缓存,脏数据存活时间可能被显著拉长。