Laravel worker 处理作业批次时锁定等待超时
Lock wait timeout when Laravel worker processes job batches
我在日志中看到以下错误。
SQLSTATE[HY000]: General error: 1205 Lock wait timeout exceeded; try
restarting transaction (SQL: select * from job_batches
where id
=
xxx limit 1 for update)
我发现这个查询是在 Laravel 更新数据库中处理的作业数之前进行的。但是不知道为什么会超时(代码在事务内部,所以应该立即释放锁)。
P.S。我使用 Amazon SQS 驱动我的队列
P.S.S.我建议有一些作业没有提交嵌套 Laravel 事务。但是我试着重现了这样的场景,看来这个建议是错误的。
简而言之,是因为一个批次的工作量大。解决方案是将作业以较小的块添加到批处理中(使用 add()
方法)。
出现这个问题是因为在一个交易中发生了两件事(见下面的代码片段):
- 数据库中更新的职位总数
- 作业被发送到队列(在我的例子中是 Amazon SQS)
关键是作业多的话,第二个语句会花很多时间。并且只要执行了事务就无法提交(并且第一条语句中受影响的行保持锁定状态)。
但是 一旦第一个作业被推送到队列,它就会被工作人员处理。在处理作业工作者尝试更新数据库中的行后,它执行 SELECT ... FOR UPDATE
。等到超时发生
这是Laravel代码的对应片段:
$this->repository->transaction(function () use ($jobs, $count) {
// Statement 1 (the affected row stays locked until transaction is committed)
$this->repository->incrementTotalJobs($this->id, $count);
// Statement 2 (can take a lot of time)
$this->queue->connection($this->options['connection'] ?? null)->bulk(
$jobs->all(),
$data = '',
$this->options['queue'] ?? null
);
});
我在日志中看到以下错误。
SQLSTATE[HY000]: General error: 1205 Lock wait timeout exceeded; try restarting transaction (SQL: select * from
job_batches
whereid
= xxx limit 1 for update)
我发现这个查询是在 Laravel 更新数据库中处理的作业数之前进行的。但是不知道为什么会超时(代码在事务内部,所以应该立即释放锁)。
P.S。我使用 Amazon SQS 驱动我的队列
P.S.S.我建议有一些作业没有提交嵌套 Laravel 事务。但是我试着重现了这样的场景,看来这个建议是错误的。
简而言之,是因为一个批次的工作量大。解决方案是将作业以较小的块添加到批处理中(使用 add()
方法)。
出现这个问题是因为在一个交易中发生了两件事(见下面的代码片段):
- 数据库中更新的职位总数
- 作业被发送到队列(在我的例子中是 Amazon SQS)
关键是作业多的话,第二个语句会花很多时间。并且只要执行了事务就无法提交(并且第一条语句中受影响的行保持锁定状态)。
但是 一旦第一个作业被推送到队列,它就会被工作人员处理。在处理作业工作者尝试更新数据库中的行后,它执行 SELECT ... FOR UPDATE
。等到超时发生
这是Laravel代码的对应片段:
$this->repository->transaction(function () use ($jobs, $count) {
// Statement 1 (the affected row stays locked until transaction is committed)
$this->repository->incrementTotalJobs($this->id, $count);
// Statement 2 (can take a lot of time)
$this->queue->connection($this->options['connection'] ?? null)->bulk(
$jobs->all(),
$data = '',
$this->options['queue'] ?? null
);
});