主从延迟大时,SELECT
为什么还会读到旧数据?
根本原因不是没走从库,而是应用没做读写分离的路由控制,或者用了弱一致性策略但没处理好时序。MySQL 自身不自动分流
SELECT和
INSERT/UPDATE,所有流量默认打到主库——除非你在客户端或中间件层显式指定。
常见错误现象:
INSERT后立刻
SELECT,结果查不到刚插入的数据;或者查到了,但字段值还是旧的。这不是 MySQL bug,是主从复制存在天然延迟(尤其在高并发写入、大事务、网络抖动时)。 必须在业务代码或代理层(如
ShardingSphere-JDBC、
MyCat、
ProxySQL)中识别写操作上下文,并将后续关联读强制路由到主库(即“强一致性读”) 避免用
sleep(1)或重试轮询来等从库同步——不可靠且放大延迟感知 如果用
read_committed隔离级别 + 行级锁,仍不能解决跨会话读旧数据问题,因为从库回放是异步的,和事务隔离无关
max_allowed_packet
和 slave_parallel_workers
必须调大
高并发下主从同步卡顿,80% 是这两个参数没调。前者限制单个 binlog event 大小,后者决定从库并行回放线程数。默认值在千级 QPS 下极易成为瓶颈。
典型表现:
Seconds_Behind_Master持续增长,
SHOW SLAVE STATUS中
Slave_SQL_Running_State常停在 “Reading event from the relay log”;
Relay_Log_Space不断上涨。
max_allowed_packet建议设为
512M(主从两端都要改),否则大
UPDATE或
LOAD DATA会直接中断复制
slave_parallel_workers设为 CPU 核数 × 2(如 16 核设 32),同时开启
slave_parallel_type = LOGICAL_CLOCK,才能真正利用多核加速回放 注意
relay_log_recovery = ON必须开启,否则从库异常重启后可能丢 relay log,导致同步断裂
ProxySQL 的 mysql_query_rules
怎么写才不误杀写请求?
用 ProxySQL 做读写分离时,规则匹配顺序和正则精度决定成败。很多团队配置完发现
UPDATE被发到从库,报错
ERROR 1290 (HY000): The MySQL server is running with the --read-only option。
关键点在于:ProxySQL 默认按 rule_id 升序匹配,第一条命中即执行,不会继续往下找。所以写规则必须优先于泛化读规则。
写规则match_pattern推荐用
^INSERT[[:space:]]+INTO|^UPDATE[[:space:]]+.*SET|^DELETE[[:space:]]+FROM|^REPLACE[[:space:]]+INTO,加
^锚定开头,避免误匹配注释里的关键词 务必设置
apply = 1并把该 rule_id 设为最小(如 1),再配读规则(rule_id=2)匹配
^SELECT不要依赖
username或
schemaname做路由,权限粒度太粗,且无法区分同用户下的读写混杂语句
从库只读开关 read_only=1
为什么有时不起作用?
read_only=1确实能阻止普通用户写从库,但它对 SUPER 权限用户无效。而 MySQL 主从切换、监控探活、备份工具(如
mydumper)常以 root 或具有 SUPER 权限的账号连接,一不留神就往从库写了脏数据。
更隐蔽的问题是:某些 ORM(如 Laravel 的
DB::statement())或 DBA 临时排障脚本,可能绕过读写分离中间件直连从库执行
TRUNCATE或
DROP,导致主从数据彻底不一致。 生产环境必须配
super_read_only=1(MySQL 5.7.8+),它连 SUPER 用户都禁止写,且比
read_only更严格 定期检查从库的
show global status like 'Com_insert%',非零值说明有写入发生,要溯源 备份任务必须明确指定
--no-lock --single-transaction,避免在从库上加全局读锁影响复制回放 实际部署中最容易被忽略的是:主库故障切换后,原从库升主,但应用连接池里还缓存着旧的只读连接,导致新主库被当从库用。这需要配合服务发现(如 Consul)或带健康检测的连接池(如 HikariCP 的
connection-test-query)来主动剔除失效节点。
