Django 2.2并发场景下select_for_update能否避免超量创建账户?
Django 2.2并发场景下控制Account创建上限的方案验证
结论
在@transaction.atomic装饰的方法中使用select_for_update()配合count查询,能够解决并发导致的账户超量创建问题,但需要确保完整逻辑都在事务内执行。
原问题根源
原来的代码缺乏并发控制:多个请求同时执行时,会同时读取到相同的unused_count,都判定有剩余名额,进而各自创建账户,最终导致总数量超出上限。
为什么select_for_update()能解决?
select_for_update()会对查询匹配到的所有行添加排他锁,在当前事务提交前,其他并发事务执行相同的查询会被阻塞。这就保证了同一时间只有一个事务能获取准确的剩余名额,避免了"读-改-写"过程中的并发冲突。
注意事项
完整逻辑必须包裹在原子事务内
不能只把count查询单独放在事务里,要把"查询剩余名额-创建账户"的整个流程都放在@transaction.atomic装饰的方法中,示例代码:from django.db import transaction @transaction.atomic def create_accounts(): # 加锁查询未使用账户数量 unused_count = Account.objects.select_for_update().filter(field1__isnull=True, field2__isnull=True).count() vacancies = UPPER_LIMIT - unused_count if vacancies > 0: # 批量创建账户 Account.objects.bulk_create([Account() for _ in range(vacancies)])性能优化建议
如果UPPER_LIMIT数值很大,符合条件的未使用账户数量较多,select_for_update()会锁定大量行,可能影响系统性能。这种情况下可以改用独立计数器表来控制总量,锁的粒度更小:# 先定义计数器模型 class AccountCounter(models.Model): total_created = models.IntegerField(default=0) # 创建逻辑 @transaction.atomic def create_accounts(): # 锁定计数器行 counter = AccountCounter.objects.select_for_update().first() if not counter: counter = AccountCounter(total_created=0) vacancies = UPPER_LIMIT - counter.total_created if vacancies > 0: Account.objects.bulk_create([Account() for _ in range(vacancies)]) counter.total_created += vacancies counter.save()
内容的提问来源于stack exchange,提问作者akash deep
相关产品推荐
相关产品推荐

