ThinkPHP 6.0 实现读写分离需结合MySQL原生指标、应用层策略与运维联动构建闭环:用时间戳探针精准测延迟,通过Db::checkSlaveLag()获取毫秒级真实值;延迟超阈值时基于连接池标签自动熔断并fallback主库;配置健康检查与冷却重试机制防雪崩;借助Prometheus+Grafana实现监控告警与自动恢复。

ThinkPHP 6.0 实现读写分离后,从库延迟不可见、不可控,就等于把一致性风险交给运气。真正的高可用不是“配上了就行”,而是能感知延迟、快速响应、自动降级。TP6 本身不内置延迟监控和熔断能力,必须结合 MySQL 原生指标 + 应用层策略 + 运维联动,才能构建闭环。
从库延迟怎么实时知道?靠 Seconds_Behind_Master 不够准
MySQL 的 Seconds_Behind_Master 是最常用指标,但它有三大缺陷:值为 0 不代表无延迟(可能刚追上又积压)、主库停写时它恒为 0、大事务回放中它会“跳变”失真。真正要监控的,是业务可感知的延迟——比如用户刚下单,库存查询是否命中最新状态。
- 在从库执行
SHOW SLAVE STATUS\G,重点看Seconds_Behind_Master和Slave_SQL_Running_State(是否卡在某条 SQL) - 更可靠的方式:在主库写入一条带时间戳的探针记录(如
INSERT INTO health_check (ts) VALUES (NOW(6))),再从各从库查这条记录的ts与当前时间差,毫秒级真实延迟一目了然 - TP6 中可封装一个
Db::checkSlaveLag()方法,每次关键读前调用,返回延迟毫秒数,用于后续决策
延迟超阈值时,怎么自动切回主库?TP6 的 master(true) 要用对地方
TP6 的 master(true) 是手动强制走主库的开关,但不能靠“每个查询都加”,而应作为熔断后的统一路由策略。关键是触发时机精准、作用范围可控、恢复机制明确。
- 不要在模型方法里零散加
->master(true),应在中间件或服务层统一拦截:当检测到某从库延迟 > 500ms,对该请求后续所有同库表读操作自动透传主库 - 利用 TP6 的 数据库连接池标签,给从库连接打标(如
'slave_1_lag_320ms'),熔断时直接禁用该标签连接,新查询自动 fallback 到主库 - 避免全局开关(如全量开启
read_master => true),否则读写分离形同虚设;应按业务场景分级,例如“订单详情页”强一致,允许熔断;“商品列表页”弱一致,可容忍 200ms 延迟
从库挂了或持续高延迟,怎么不炸?TP6 连接池要配健康检测
TP6 默认从库连接池没有健康检查机制,一个从库宕机后,轮询仍会不断尝试连接,导致大量超时和请求堆积。必须补上心跳探测和自动剔除逻辑。
- 在
database.php的从库配置中,增加'health_check' => true(需自行扩展或使用社区适配包),每 30 秒执行一次SELECT 1探活 - 配合
'slave_balance' => 'random'(TP6.3+ 支持),让请求随机分发,避免单点压力集中;同时设置'max_retries' => 2,连续失败即临时下线该节点 - 下线不是永久删除,而是进入“冷却队列”,每隔 2 分钟重试一次,成功则重新加入可用池——这样既防雪崩,又保留弹性恢复能力
监控告警和自动恢复,不能只靠人盯
延迟问题往往凌晨爆发,靠人工登录服务器查 SHOW SLAVE STATUS 已经太慢。必须把指标采集、阈值判断、动作执行串成自动化流水线。
- 用 Prometheus + Exporter 定期拉取各从库的
Seconds_Behind_Master和自定义探针延迟,Grafana 做多维度看板(按实例、按业务线、按延迟分布) - 告警规则设两级:>500ms 持续 1 分钟 → 企业微信通知 DBA;>2000ms 持续 30 秒 → 自动触发熔断脚本(调用 TP6 提供的 API 关闭对应从库路由)
- 恢复信号来自延迟回归正常 + 连续 5 次探针成功,此时自动解除熔断,并记录本次事件完整链路(开始时间、峰值延迟、影响接口、恢复耗时)用于复盘


















