laravel读写分离必须在mysql连接下嵌套read/write数组,且启用sticky=true才能保证写后立即读;事务内所有查询强制走主库,eloquent默认不走从库,db::table()->on()无效。

config/database.php 里 mysql 连接必须嵌套 read/write,不能只加两个独立连接
所有查询仍走主库,从库压根不会被用到——这是最常被踩的坑。Laravel 的读写分离路由逻辑只认一个连接名(比如 mysql)下同时存在 read 和 write 键。如果你新增了 mysql_master 和 mysql_slave 两个连接,再在模型里写 User::on('mysql_slave')->get(),那只是手动切换,不触发框架的自动读写路由,事务、连接复用、故障转移全得自己兜底。
正确结构是:
'mysql' => [
'driver' => 'mysql',
'database' => env('DB_DATABASE'),
'username' => env('DB_USERNAME'),
'password' => env('DB_PASSWORD'),
'read' => [
['host' => env('DB_READ_HOST_1')],
['host' => env('DB_READ_HOST_2')],
],
'write' => [
'host' => env('DB_WRITE_HOST'),
],
'sticky' => true,
],
-
read必须是数组,哪怕只有一个从库也要写成数组形式 -
write必须是关联数组(不是索引数组),且只能有一个元素 - 共用字段(
database、username、password、charset等)必须提到外层,写在read或write内部会被忽略 -
driver必须显式声明在外层,否则连接初始化失败
事务内所有查询强制走主库,无法绕过
只要进了 DB::transaction() 或调用了 DB::beginTransaction(),后续所有查询——包括 DB::table('users')->where('id', 1)->first()——都会无视是否是 SELECT,一律发到 write 连接。这不是配置错误,而是 Laravel 为保证 ACID 强制做的行为。
PHP中文网提供Laravel 13.2.0版本下载,Laravel框架 是基于 PHP 8.3+ 的高性能框架,官方推荐通过 Composer 安装。它内置 AI SDK、JSON:API Resources 及原生向量搜索,支持属性驱动开发与队列路由,大幅提升开发效率。相比旧版,13.2.0 优化了缓存 TTL 管理与实时通信,无需 Redis 即可横向扩展。作为现代 Web 开发首选,它兼顾安全与极速体验,助您快速构建企业级应用。
- 试图在事务里执行
DB::connection('mysql')->table('logs')->get()依然走主库 - 想查从库?不行。只能把读操作提前到事务外,比如先查用户权限再开启事务
- 如果业务上只是“查配置+写日志”,就把查配置放到事务前,别为了省一次连接硬塞进去
- 事务中回滚后,
sticky标记仍保留——这是设计,不是 bug,但可能误伤后续读请求
sticky => true 是防主从延迟的关键开关,不是可选项
默认 sticky => false,开了读写分离却没开这个,用户刚提交表单就查不到自己刚填的数据,不是 bug,是预期行为:SELECT 落到了还没同步完的从库上。
- 设为
true后,只要当前请求中执行过insert()、update()、save()、delete()或DB::statement('INSERT ...'),后续所有Model::find()、DB::table()->get()都强制走主库 - 该标记仅限当前 HTTP 请求生命周期,不跨请求、不依赖 session/cache
- 队列任务里不用开
sticky:它是独立进程,且通常不需要「写后立刻读」 - 如果业务能接受几秒延迟(比如后台报表),可以关掉来压测从库吞吐,但前台交互建议始终开启
Eloquent 默认不走读库,DB::table() 的 on() 方法无效
User::all()、User::find(1) 这些看似是读操作,但 Eloquent 模型默认把整个连接当成可写连接处理,所以仍走主库。而 DB::table('logs')->on('mysql_read')->get() 会静默忽略或报错——因为 on() 只在 Eloquent 模型上有效,对 DB::table() 无效。
- 想让模型走读库,最稳妥的是在模型里加
protected $connection = 'mysql_read';,但这就绕过了 Laravel 原生读写路由,得自己管故障转移 - 更推荐用
User::on('mysql')->where(...)->get(),前提是你的mysql连接已正确定义read/write - 手动指定连接必须用
DB::connection('mysql_read'),而不是DB::table()->on('...') - 别信
User::on('mysql_slave')这种写法:mysql_slave不是一个合法连接名,它只是mysql.read的一部分
config/database.php 中那个看似简单的结构嵌套;最容易被忽略的不是语法,是 sticky 的作用域边界和事务的不可分割性。










