You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 7.x 原子锁失效排查:cache_locks表为空仍存竞态问题

Laravel 7.x 原子锁未生效问题排查与解决

问题描述

我尝试使用Laravel 7.x的Atomic Locks(原子锁)解决竞态条件问题,已配置database缓存驱动并完成cache和cache_locks表迁移,但cache表有数据而cache_locks始终为空。

锁逻辑代码如下:

$lock = Cache::lock("{$myUniqueId}_lock", 5);

try 
{
    $lock->block(5);

    // Perform some database operation
    \DB::statement("UPDATE my_table SET some_column = {$some_data}");
} 
catch (LockTimeoutException $e) 
{
    throw new \Exception('Error with atomic locks');
} 
finally 
{
    $lock->release();
}

压测(20虚拟用户)时出现以下问题:

  1. 触发MySQL错误:Integrity constraint violation: 1062 Duplicate entry '....' for key '....',锁未生效;
  2. cache_locks表始终无数据写入;
  3. LockTimeoutException从未抛出。

问题排查与修复步骤

1. 确认缓存驱动配置正确性

检查.env文件中CACHE_DRIVER是否设置为database,同时核对config/cache.php的database驱动配置:

'database' => [
    'driver' => 'database',
    'table' => 'cache_locks',
    'connection' => 'mysql', // 明确指定业务使用的数据库连接,避免默认连接不匹配
],

若connection为null,Laravel会使用默认数据库连接,若业务操作与缓存锁不在同一数据库,会导致锁表无法写入。

2. 正确引入LockTimeoutException命名空间

代码中捕获的LockTimeoutException未引入命名空间,导致无法识别异常类。需在文件顶部添加:

use Illuminate\Contracts\Cache\LockTimeoutException;

否则PHP会将其视为当前命名空间下的类,无法正确捕获超时异常。

3. 确保锁键的唯一性

确认$myUniqueId是标识竞态资源的唯一键(如用户ID、订单ID)。若不同请求使用不同的锁键,锁的互斥作用会失效,导致竞态问题。

4. 优化锁的释放逻辑

只有成功获取锁后才需释放锁,避免在未获取锁时调用release()。可修改为:

$lock = Cache::lock("{$myUniqueId}_lock", 5);
$isLocked = false;

try 
{
    $lock->block(5);
    $isLocked = true;

    // 业务操作
    \DB::statement("UPDATE my_table SET some_column = {$some_data}");
} 
catch (LockTimeoutException $e) 
{
    throw new \Exception('Error with atomic locks');
} 
finally 
{
    if ($isLocked) {
        $lock->release();
    }
}

或使用Laravel闭包式锁,自动处理锁的获取与释放:

Cache::lock("{$myUniqueId}_lock", 5)->block(5, function () use ($some_data) {
    \DB::statement("UPDATE my_table SET some_column = {$some_data}");
});

5. 调整锁与事务的顺序

若业务操作包含数据库事务,需先获取锁,再启动事务,避免锁释放时机与事务提交时机不一致:

// 先获取锁
$lock = Cache::lock("{$myUniqueId}_lock", 5);
$lock->block(5);

try {
    DB::beginTransaction();

    // 业务操作
    \DB::statement("UPDATE my_table SET some_column = {$some_data}");

    DB::commit();
} catch (\Exception $e) {
    DB::rollBack();
    throw $e;
} finally {
    $lock->release();
}

6. 验证cache_locks表结构

重新运行迁移确保表结构正确:

php artisan migrate

Laravel 7.x的cache_locks表需包含key、owner、expiration字段,且key为唯一索引,否则锁无法正常写入。


内容的提问来源于stack exchange,提问作者Linesofcode

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 18:43:27