编程 Laravel 13.36:长驻 worker 跑过一个写任务后,后面一万个任务的读都压在主库

2026-10-19 20:02:53

Laravel 13.36:长驻队列 worker 的读为什么一直压在主流上

laravel/framework v13.36.0 于 2026-10-13 发布,要求 PHP ^8.3。这个版本里有一个和队列 worker 相关的改动:允许在任务之间清掉粘性(sticky)的数据库连接。

现象:一个写任务,后面一万个读都走主库

读写分离的配置通常长这样:

'connections' => [
    'mysql' => [
        'read' => [
            'host' => ['192.168.1.1'],
        ],
        'write' => [
            'host' => ['196.168.1.2'],
        ],
        'sticky' => true,
        'driver' => 'mysql',
        // ...
    ],
],

打开 sticky 之后,写操作会把该连接实例的读钉在 write 连接上,避免刚写完立刻读到从库的旧数据。

问题出在队列 worker 的生命周期上。worker 是长驻进程,一个进程会连续 pop 成千上万个任务。只要其中有一个任务执行了写操作,这个进程持有的连接就一直是 sticky 状态,后续所有任务的读都落到主库——直到进程退出(下一次部署)才会重置。PR 描述里写得很直接:

当一个连接是 sticky 的,写入会把它上面的读钉在写连接上,所以对一个 worker 来说——只要它有一个任务写了,就可能意味着后面 10,000 个任务都在用写连接……或者直到我们下一次强制部署,而这实际上让我们失去了拥有从库的意义。

Web 请求里这个问题不明显:请求结束连接就销毁了。worker 不结束。

13.36 新增的开关

新增的是 Worker 上的一个静态属性,默认 false,需要手动打开:

// AppServiceProvider::boot()
use Illuminate\Queue\Worker;

public function boot(): void
{
    Worker::$flushStickyConnections = true;
}

打开后,worker 每执行完一个任务就刷新一次粘性连接,把读重新交还给从库。PR 里提到这个行为倾向在 14.x 变成默认,13.x 保持 opt-in 是为了不引入 BC 破坏:

我觉得这个在 14.x 应该是默认的,只是不想在这里做 b/c……(总会有人在某处依赖这个行为)

打开之后的代价

读回到从库,就意味着会碰到复制延迟。上一个任务刚写的数据,下一个任务读的时候可能读不到——因为新任务走的是从库,而从库还没追上主库。

如果两个任务之间本来就有先后依赖,正确做法是 chain 或 batch 把它们绑在一起,而不是指望同一个 worker 进程恰好接住下一个任务、恰好复用同一条 sticky 连接。PR 原话:

如果任务依赖其他任务,它们应该是 chain 或者 batch,而不是悄悄依赖哪个 worker 进程碰巧 pop 到了它们。

换句话说,打开这个开关会把「隐式依赖 worker 进程复用连接」的实现暴露出来。

怎么判断自己要不要开

按顺序核对几件事:

  1. 是否配了读写分离。read / write 两个 host 都配了、sticky => true,才有这个问题;单库配置下开关没有实际意义。
  2. worker 是否长驻。默认的 queue:work 就是长驻的,--memory 给得越大、重启越少,sticky 状态持续得越久。queue:listen 或 --stop-when-empty 影响较小。
  3. 观察主库负载。看主库 QPS、慢查询日志里是否有大量本应走从库的 SELECT,且来源是队列 worker 的连接。这类读通常不是业务必需的强一致读,只是被粘住了。

如果确实中了前两条、第三条也能对应上,打开开关;如果任务链条本身存在隐式顺序依赖,先把它们改成 chain / batch,再打开。

顺带一句:background 队列连接

同一版本里,框架自带的 config/queue.php 补上了 background 连接(#61917),应用骨架里早就有这个配置。在此之前,没有自己 queue 配置、或者用 php artisan config:publish queue 发布过旧版配置的应用,会报:

The [background] queue connection has not been configured

升级后如果需要这个连接,重新发布或手动补上对应配置即可。

复制全文 生成海报 Laravel PHP 队列 MySQL 读写分离

推荐文章

程序员茄子在线接单