Laravel读写分离权重靠read数组中节点重复次数模拟,如A库写3次、B库写1次即实现3:1比例;SQL含INSERT/UPDATE等关键词或事务内查询强制走主库;sticky仅对主键WHERE查询生效且不改变权重逻辑。

MySQL读写分离时,read和write连接池怎么配权重
Laravel本身不直接支持“按百分比分配从库流量”,它只做简单的轮询或随机选从库。所谓“负载比例”,本质是靠配置多个read节点 + 控制它们在配置中的出现频次来模拟加权——比如想让从库A承担70%读请求,就把它在read数组里写3次,从库B写1次,凑成4个地址,自然概率就是3:1。
实操建议:
-
config/database.php中'mysql'配置下,'read'必须是数组,哪怕只有一个从库也要写成['host' => '...'],不能是单个字符串 - 所有
read节点的host、port、database等字段必须显式一致,Laravel不会自动继承write的其他参数 - 如果某从库响应慢,它会在连接池里被临时剔除(取决于
sticky和重试逻辑),但不会动态调整权重——权重完全静态依赖配置顺序和重复次数
DB::connection('mysql')->select()到底走读还是写连接
绝大多数查询都会走read连接池,但Laravel有明确的“写操作判定规则”:只要SQL里含INSERT、UPDATE、DELETE、REPLACE、CREATE、DROP、ALTER、TRUNCATE这些关键词,就会强制走write连接,哪怕你用的是DB::select()。
常见错误现象:
- 用
DB::select("SELECT * FROM users WHERE id = ? FOR UPDATE")——FOR UPDATE虽是读语句,但会加锁,Laravel识别不到,仍走从库,导致死锁或主从不一致 - 调用
DB::transaction()后所有查询都走主库,哪怕里面只有SELECT——事务默认绑定到写连接 - 使用
DB::table('users')->where(...)->get()时,如果模型启用了$timestamps且执行了save(),后续链式select()可能因上下文残留走错连接
sticky设为true时,读操作还会分发到从库吗
会,但仅限当前请求生命周期内“刚写过的那条数据”的后续读取——sticky不是全局读写分流开关,而是“主库写入后,立刻读同一行时避免主从延迟”的补救机制。
关键细节:
-
sticky只对DB::table()->where()->first()这类带WHERE主键/唯一索引的查询生效;DB::select("SELECT * FROM users LIMIT 1")这种无条件查询永远走从库 - 它不改变连接池权重逻辑,只是在
read连接选出后,额外检查“最近一次写是否命中该记录”,再决定是否降级回主库 - 如果主库宕机,
sticky会失败并抛出PDOException,不会自动 fallback 到从库——这点常被忽略
如何验证某个查询实际连到了哪个数据库
最直接的办法是临时开启查询日志,看Connection实例的getPdo()底层host信息:
DB::connection()->getPdo()->getAttribute(PDO::ATTR_CONNECTION_STATUS)
但更实用的是打点调试:
- 在
AppServiceProvider::boot()里监听Illuminate\Database\Events\QueryExecuted事件,打印$event->connection->getConfig('host') - 用
DB::connection()->getPdo()->getAttribute(PDO::ATTR_SERVER_INFO)查当前连接的MySQL服务端地址(注意:PHP 8.1+才稳定支持) - 别依赖
DB::connection()->getName(),它返回的是配置名(如mysql),不是实际主机名
复杂点在于:Laravel的连接复用、PDO长连接、以及连接池中间件(如ProxySQL)会让host信息和真实路由不一致——真要压测权重,得在MySQL服务端用SHOW PROCESSLIST反查来源IP。










