sticky是Laravel读写分离中解决“写后即读不一致”问题的机制:启用后,当前请求中执行写操作则后续读操作自动路由至主库。需在database.php中配置'sticky' => true,仅对启用了读写分离的连接生效,且作用范围限于单次请求生命周期。

什么是 sticky,它解决什么问题?
当 Laravel 启用读写分离后,write 连接用于 INSERT/UPDATE/DELETE,read 连接用于 SELECT。但刚写入的数据立刻被 SELECT 查不到(因主从延迟),这就是典型的「写后即读不一致」问题。sticky 就是为此设计的:只要当前请求中执行过写操作,后续的读操作会自动路由到 write 连接(即主库),绕过从库延迟。
如何开启 sticky?
在 config/database.php 的 MySQL 配置里,把 sticky 设为 true 即可。它只对启用了 read/write 分离的连接生效:
'mysql' => [
'driver' => 'mysql',
'url' => env('DATABASE_URL'),
'host' => env('DB_HOST', '127.0.0.1'),
'read' => [
'host' => ['192.168.1.10', '192.168.1.11'],
],
'write' => [
'host' => ['192.168.1.5'],
],
'sticky' => true, // ← 关键开关
// 其他配置...
],
-
sticky默认为false,不开启时读永远走read数组里的从库(轮询或随机) - 开启后,Laravel 会在当前请求生命周期内记住「已写过」状态,所有后续
DB::table(...)->get()、Model::find()等读操作都会强制走write主库 - 该行为仅限当前请求(HTTP 或 CLI 单次运行),不会跨请求、不依赖 session 或 cache
sticky 不起作用的常见原因
开了 sticky => true 却仍读到旧数据?重点检查以下几点:
- 你是否真的触发了「写操作」?
insert()、update()、save()、delete()才算;DB::statement('INSERT ...')也生效,但DB::select()或原生PDO::exec()不会触发 sticky 标记 - 是否在事务中写入?Laravel 的
sticky在事务开始后第一次写就标记,但若事务回滚,标记仍保留——这可能导致本不该读主库的后续查询也走了主库 - 是否用了
DB::connection('mysql')->table(...)显式指定连接?这会绕过默认连接的sticky逻辑;应统一用DB::table(...)或Model::on('mysql') - 是否在队列任务中使用?队列是独立进程,
sticky无法跨进程传递,且队列里一般也不需要 sticky(无实时读需求)
sticky 的代价与适用边界
sticky 是用主库压力换一致性,不是银弹:
- 它会让单个请求的读流量全部落到主库,可能放大主库负载,尤其在高并发写+读场景下
- 对「写后非立即读」的场景(如写完跳转新页面再查),
sticky完全无效——它只影响当前请求生命周期 - 如果你的业务能接受秒级延迟(比如后台统计、日志归档),关闭
sticky+ 加缓存(如 Redis)往往比强依赖主库更健壮 - 真正需要
sticky的典型场景很窄:用户注册后立刻显示欢迎页(需读刚插入的 user 记录)、表单提交后重定向并展示结果(需读刚保存的 model)
别把它当成读写分离的必选项,而应看作一个有明确副作用的一致性补丁。主从延迟本身不可消除,sticky 只是在特定路径上帮你“临时切回主库”。









