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

Django+MySQL使用select_for_update时的死锁问题排查求助

多进程并发修改Django模型的死锁问题分析与解决

问题背景

在多进程并发场景下,实现端点逻辑:获取用户最新未完成的Play记录,存在则修改,不存在则创建,要求并发请求时一个创建、另一个阻塞等待后修改。使用Django的transaction.atomic()上下文结合select_for_update()实现,但压测后仍出现死锁。死锁日志显示:事务1持有finished索引锁,等待某Play主键锁;事务2持有该Play主键锁,等待finished索引锁,且两个Play属于不同用户。

死锁原因分析

1. 索引设计缺陷导致锁范围过大

当前仅依赖finished单字段索引,执行select_for_update().filter(user=X, game=X, finished=False, discard=False)时,InnoDB无法通过finished索引精准定位到当前用户的行:

  • 首先会通过finished索引筛选所有finished=False的行,然后回表校验user、game、discard条件;
  • 此过程中,InnoDB会对finished=False对应的整个索引范围(包括间隙)加锁,而非仅锁定当前用户的目标行。这意味着事务会锁定其他用户的Play记录对应的索引项,甚至未存在的行的间隙。

2. 锁获取顺序不一致引发循环等待

结合代码中拆分的事务逻辑(先查询锁索引,再在另一个事务中锁主键),可能出现以下死锁场景:

  • 事务A先执行filter查询,锁定了finished索引的大范围(包含事务B用户的Play索引项);
  • 事务B先创建了自己的Play记录,持有该记录的主键锁;
  • 事务A后续尝试修改某Play需要获取该主键锁,而事务B执行filter查询时需要获取finished索引锁,两者形成循环等待,触发死锁。

3. 重复加锁与事务拆分的问题

代码中在第一个查询已通过select_for_update()锁定Play行,后续又在独立的transaction.atomic()中再次执行select_for_update().get(pk=play.pk),这种重复加锁且拆分事务的操作,会打乱锁的获取顺序,增加死锁概率。

解决办法

1. 创建精准覆盖查询条件的联合索引

建立包含所有查询条件的联合索引,让InnoDB直接定位到目标行,缩小锁范围:

CREATE INDEX idx_user_game_finished_discard ON play (user_id, game_id, finished, discard);

(注:根据实际表名、字段名调整,确保索引顺序匹配查询条件的过滤优先级)

2. 将所有操作合并到同一事务中

把查询、判断、修改/创建逻辑放在同一个transaction.atomic()上下文内,避免锁的生命周期拆分,确保锁的获取顺序一致:

with transaction.atomic():
    # 精准锁定当前用户的未完成Play
    plays = list(Play.objects.select_for_update().filter(
        game=self.game,
        user=self.user,
        discard=False,
        finished=False,
    ))
    if plays:
        play = plays[0]
        # 执行Play的修改操作
        play.some_field = new_value
        play.save()
    else:
        # 不存在则创建新记录
        play = Play.objects.create(
            game=self.game,
            user=self.user,
            discard=False,
            finished=False,
            # 其他字段赋值
        )

3. 移除不必要的重复加锁

删除后续独立事务中对play.pk的二次select_for_update()操作,第一个查询已经完成了行锁定,无需重复加锁。

4. 优化锁的获取顺序(可选)

确保所有事务遵循相同的锁获取顺序,比如先通过用户维度锁定,再处理索引相关操作,避免循环等待的可能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 22:54:58