php数据库读写分离需确保主从架构就绪、sql路由精准、强一致读走主库、延迟与故障有兜底策略。

PHP 应用做数据库读写分离,不是加个配置就能生效的——核心在于写操作必须走主库、读操作要能可靠路由到从库,且主从延迟时读不到最新数据是常态
MySQL 主从架构是否已就绪
读写分离的前提是底层已有可用的主从复制。没配好主从,上层怎么切都是空谈。
- 确认
SHOW SLAVE STATUS\G中Slave_IO_Running和Slave_SQL_Running均为Yes - 检查
Seconds_Behind_Master是否长期 > 0;若经常滞后数秒以上,说明从库压力大或网络/磁盘有瓶颈 - 主库
binlog_format必须为ROW(尤其用 Laravel/Eloquent 等 ORM 时,MIXED或STATEMENT可能导致从库执行异常) - 从库建议设置
read_only = ON,防止误写入(但需注意:super 用户仍可写,必要时加super_read_only = ON)
PHP 层如何控制读写路由
常见做法是封装一个数据库访问层,在执行前判断 SQL 类型,再选择连接。但不能只看 SQL 开头几个字,比如 SELECT COUNT(*) FROM ... 是读,而 SELECT ... FOR UPDATE 实质是写锁,必须走主库。
- 简单场景可用 PDO 封装:对
INSERT/UPDATE/DELETE/REPLACE/ALTER/DROP等显式写操作强制用主库连接;其余默认走从库 - Laravel 用户直接用内置读写分离:在
config/database.php的 MySQL 配置中填入'read'和'write'子数组,框架会自动分发 - ThinkPHP 6+ 同理,通过
['hostname' => [...]]配置多个节点,并设'deploy' => 1启用读写分离 - 手写连接池或使用 Swoole 协程 MySQL 客户端时,需自行维护两个连接对象(
$master/$slave),并在 query 方法里做 SQL 解析或由调用方显式指定连接(如db()->master()->table(...))
主从延迟导致脏读怎么办
这是读写分离最常被忽略的痛点。用户刚提交订单,刷新页面却查不到——不是代码错了,是数据还没同步到从库。
立即学习“PHP免费学习笔记(深入)”;
- 对强一致性要求的读(如订单详情、账户余额),一律走主库,哪怕只读。可加标记如
DB::connection('mysql:master')或自定义方法->useMaster() - 部分业务可接受“会话一致性”:同一用户请求,在写操作后几秒内所有读都走主库。实现方式是在 session 或 Redis 中记录该用户最近一次写的时间戳,读前比对是否超时
- 避免在事务中混用主从连接——PDO 默认不支持跨连接事务,
START TRANSACTION和COMMIT必须在同一连接上完成 - 不要依赖从库的
SELECT NOW()判断时间,主从系统时间差也可能造成逻辑错乱
连接数与故障转移怎么兜底
从库挂了,请求不能全崩;主库压力大,也不能让所有读挤在主库上。
- 从库连接失败时,应自动降级到主库读(需配置开关,避免雪崩)。Laravel 中可通过重写
MySqlConnection的select方法捕获PDOException并 fallback - 多从库时,用轮询或权重随机选一个;若某从库持续超时(如 5 次内 3 次 > 500ms),临时剔除它 30 秒(存内存或 Redis)
- 主库连接池大小建议设为从库总和的 1.2–1.5 倍,因为主库既要处理写,还要承担 fallback 的读流量
- 监控必须覆盖:各库的
Threads_connected、慢查询数量、主从延迟值;告警阈值建议设为延迟 > 3s 持续 30 秒
真正难的不是配通读写分离,而是想清楚哪些读能放从库、哪些必须钉死主库,以及当从库不可用时,你的业务是否还能正确响应。这些决策点藏在具体 SQL 和业务语义里,没法靠通用组件自动解决。











