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

Django如何处理并发API请求,避免转账场景下database is locked报错

问题根因分析

你遇到的django.db.utils.OperationalError: database is locked报错核心是默认使用了SQLite数据库,它仅支持库级锁,只要有一个写事务在执行,其他所有写操作都会被直接阻塞报错,本身不适用于有并发写需求的生产场景。另外你当前的实现逻辑也有可优化空间。

可行解决方案(按优先级排序)

1. 更换支持行级锁的生产级数据库

直接换成MySQL/PostgreSQL这类支持行级锁的关系型数据库,是从根本上解决问题的方案:

  • 这类数据库的select_for_update默认是行级锁,只会锁定你查询到的对应用户的记录,不会锁整个库,其他用户的转账操作完全不受影响
  • 同一个用户的并发请求,第二个请求会自动等待第一个事务释放锁后再执行,不会抛出全库锁异常,余额计算也不会出错

2. 优化锁逻辑,无需while循环等待

如果你不想让请求等待锁,可以直接调整锁参数+异常捕获,从入口拦截并发请求:

  • 给select_for_update添加nowait=True参数,拿不到锁时会直接抛出异常,不需要你自己实现循环等待逻辑
  • 捕获拿锁失败的异常,直接返回「操作过于频繁,请稍后再试」的提示即可
  • 如果是多实例部署,可额外增加Redis分布式锁做第一层拦截,以用户ID为key设置1-2秒的短过期时间,拿不到锁直接返回,不需要请求到数据库层

3. 优化余额存储逻辑

你当前每次从转账流水表倒查最新余额的设计效率低,也容易出问题,建议拆分出独立的UserAccount表存储用户实时余额,转账时直接锁定对应用户的UserAccount行做扣减,流水表仅作为操作记录事后对账使用。

优化后的代码示例

import datetime
from django.db import transaction, DatabaseError

# 建议拆分后的UserAccount模型方法
@transaction.atomic
def withdraw(self, amount, phone, username):
    try:
        # 锁定当前用户的账户行,拿不到锁直接抛异常
        user_account = UserAccount.objects.select_for_update(nowait=True).get(username=username)
    except DatabaseError:
        # 捕获拿锁失败的异常,直接返回提示
        return {'result': 'error', 'message': '操作过于频繁,请稍后再试'}
    
    amount = int(amount)
    if user_account.balance >= amount:
        # 扣减余额
        user_account.balance -= amount
        user_account.save()
        # 记录转账流水
        transferLog.objects.create(
            username=username,
            _from=username,
            _to=phone,
            amount=amount,
            time=datetime.datetime.now()
        )
        return {'result': 'success', 'message': 'transfer complete', 'remaining': user_account.balance}
    else:
        return {'result': 'error', 'message': 'not enough money'}

若必须保留SQLite的临时方案

如果暂时无法更换数据库,可在Django的settings.py中调整SQLite的锁等待超时时间,缓解报错问题:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
        'OPTIONS': {
            'timeout': 20, # 单位为秒,调整锁等待最长时间
        }
    }
}

该方案仅为临时兼容,生产环境强烈建议更换为支持行级锁的数据库。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 10:24:04