MySQL触发器无法实时标记主从延迟,因不感知复制状态;应改用外部探针查SHOW SLAVE STATUS并写入replica_health表,MyBatis在Interceptor中强制主库查询该表判断路由,PostgreSQL需通过LSN差值计算延迟并缓存结果,且自动切换前必须验证复制线程状态和延迟为0。

MySQL 触发器无法实时标记主从延迟状态
触发器在 MySQL 里只响应 DML(INSERT/UPDATE/DELETE),不感知复制线程、不读取 Seconds_Behind_Master,更没法主动写入延迟标记表——这是最常被误用的点。很多方案试图在从库建触发器监听 mysql.slave_relay_log_info,但该表只在 relay log 切换时更新,且从库默认禁用触发器(sql_log_bin=OFF 下不生效)。
真正可行的路径是:绕过触发器,用外部探针周期性查 SHOW SLAVE STATUS,把结果写进一张轻量状态表(如 replica_health),再让业务层读它做路由判断。
- 状态表只需两个字段:
server_id(INT)、delay_seconds(INT),主键为server_id - 探针脚本用
mysql -e "SHOW SLAVE STATUS\G" | grep Seconds_Behind_Master提取值,超 5 秒就写INSERT ... ON DUPLICATE KEY UPDATE - 务必给这张表加
READ COMMITTED隔离级别,避免业务读到半更新的延迟值
MyBatis 动态路由读库前必须检查 delay_seconds
很多人以为在 DataSource 层配个 AbstractRoutingDataSource 就完事,但没意识到:路由决策必须基于最新延迟值,而数据库连接池里的连接可能复用旧连接,导致 SELECT 还发到已延迟的从库。
正确做法是在每次执行 SELECT 前,强制查一次 replica_health 表,且必须走主库查(否则陷入“查从库判断是否该读从库”的死循环)。
- 在 MyBatis 的
Interceptor中拦截Executor.query,提取 SQL 类型和 mapper 方法名 - 若为读操作,先通过主数据源执行
SELECT delay_seconds FROM replica_health WHERE server_id = ? - 如果
delay_seconds > 3,则强制改用主库;否则走默认从库 - 注意缓存这个判断结果最多 1 秒(用
System.nanoTime()控制),避免频繁查状态表拖慢吞吐
PostgreSQL 的 pg_stat_replication 不适合直接轮询
PostgreSQL 没有 Seconds_Behind_Master 这种直白字段,得靠 pg_stat_replication 里的 write_lag、flush_lag 推算延迟。但问题在于:这些字段是事务级快照,不是实时值;且默认只对超级用户可见,普通应用账号查不到。
硬轮询会放大主库负载,尤其当连接数多时,pg_stat_replication 查询本身就成了瓶颈。
- 改用
pg_replication_slots查active_pid和confirmed_flush_lsn,结合主库当前pg_current_wal_lsn()算差值,更稳定 - 把计算逻辑封装成只读视图(如
v_replica_delay),并授权给应用账号 - 不要每条 SELECT 都查——在连接初始化时查一次,后续同连接内复用,直到连接关闭或超时(建议 30 秒重检)
自动切换后写操作失败的隐性原因
最隐蔽的问题不是读切错了,而是写操作因主从角色漂移失败。比如 MHA 切换后新主库还没加载完 relay log,但应用已连上去执行 INSERT,报错 ERROR 1236 (HY000): Could not find first log file name in binary log index file。
这说明:自动切换不能只看延迟,还得确认复制链路是否真正就绪。
- 切换前必须验证新主库的
Slave_IO_Running和Slave_SQL_Running都为Yes,且Seconds_Behind_Master = 0 - 应用侧应捕获
ER_MASTER_FATAL_ERROR_READING_BINLOG和ER_SLAVE_HEARTBEAT_FAILURE这类错误,触发本地熔断(如暂停写 5 秒再重试) - 所有写操作必须带
/*+ FORCE_MASTER */注释(MyBatis 可用<if test="hint=='master'">/*!+ FORCE_MASTER */</if>),防止中间件误路由
延迟判断只是开关,复制拓扑健康才是底座。漏掉角色确认步骤,自动切换反而比手动更危险。

















