主从连接必须分离管理,使用独立pdo实例;读操作通过db_query_read自动路由至从库并支持降级,写操作强制走db_query_write且仅用主库;需检测从库延迟并熔断,严禁混用连接或在事务中误读。

主从连接配置要分开管理,不能共用一个 PDO 实例
主库写、从库读的分离前提是连接本身隔离。如果用同一个 PDO 实例来回切换 host,不仅无法保证读写分离,还可能因事务未结束导致从库返回过期数据或报错 SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active。
实操建议:
- 定义两个独立配置数组:
$master_config和$slave_config,分别含 host、dbname、user、pass - 初始化时创建两个
PDO对象:$pdo_master和$pdo_slave,各自保持长连接(加PDO::ATTR_PERSISTENT => true) - 不要在运行中调用
$pdo->exec("CHANGE MASTER TO ...")—— 这是复制控制命令,不是应用层切换手段
读操作自动路由到从库需封装查询入口
手动每次写 $pdo_slave->query(...) 容易遗漏,也难统一处理失败降级。必须把“读”这个行为抽象成可干预的调用点。
实操建议:
- 写一个
db_query_read($sql, $params = [])函数,内部优先用$pdo_slave执行;若抛出异常(如连接超时、拒绝连接),则 fallback 到$pdo_master - 避免在事务中调用该函数:从库不参与主库事务,
SELECT可能读不到刚INSERT的数据,除非显式要求一致性(此时应强制走主库) - 简单判断是否需强一致:检查 SQL 是否含
FOR UPDATE或出现在beginTransaction()后,就跳过从库直连主库
写操作必须明确指向主库,且不能被中间件误转
有些 ORM 或 DB 封装层会根据 SQL 关键字(如 INSERT)自动识别写操作,但不可靠——比如 INSERT INTO ... SELECT ... 实际触发了读,但仍是写语句;又或者存储过程里混用读写,会被错误路由。
实操建议:
- 所有写操作统一走
db_query_write($sql, $params),内部只用$pdo_master,不尝试 fallback - 禁止在
db_query_read()里执行INSERT/UPDATE/DELETE,哪怕语法合法——这会破坏主从职责边界 - 若使用
PDO::prepare(),注意预处理对象绑定的是哪个 PDO 实例;不要复用$pdo_slave->prepare()去执行写语句
从库延迟高时,读操作降级逻辑要带熔断和标记
单纯捕获连接异常不够。从库可能连得上但数据延迟几十秒,用户刚发帖就刷不出,这类问题不会抛异常,但体验极差。
实操建议:
- 在
db_query_read()中加入延迟探测:执行SELECT UNIX_TIMESTAMP() - UNIX_TIMESTAMP(utc_time())(需从库开启log_bin并有权限查SHOW SLAVE STATUS) - 设置阈值(如 > 3 秒),超过则记录日志并临时禁用该从库 60 秒(用内存缓存或 Redis 标记
slave_unavailable:192.168.1.10) - 不要依赖
Seconds_Behind_Master字段做实时判断——它在 IO 线程卡住时会归零,反而误导
主从切换不是改个 host 就完事,真正的难点在于读写意图识别、延迟感知、以及故障时的静默降级策略。这些逻辑一旦散落在各处,比数据库本身还容易出问题。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











