Django 5.1+Celery 5.4搭配SQLite时并发写入出现数据库锁定问题求助
Django 5.1+Celery 5.4搭配SQLite时并发写入出现数据库锁定问题求助
看起来你已经把SQLite的生产级配置拉满了,但还是碰到了数据库锁的坑,我来帮你拆解下问题根源和解决方向~
问题核心:事务里的耗时操作拖垮了并发能力
你当前的Celery任务流程是这样的:
transaction.atomic()包裹 → 查询本地数据 → 发起耗时的远程API调用 → 本地更新数据
结合你设置的 transaction_mode: 'IMMEDIATE',这里就出现了致命矛盾:
IMMEDIATE事务模式的特性是:事务一开启就立刻获取写锁,而不是等到真正执行写操作时才拿锁- 任务中间的远程API调用是纯IO耗时操作(可能几秒甚至更久),这就意味着写锁会被Celery进程长时间持有
- 哪怕你开了WAL模式,SQLite的写操作本来就是串行的,主进程的写请求会被这个长时间持有的锁堵住,等满
busy_timeout=5000(5秒)后就触发了"数据库锁定"错误
针对性解决思路
1. 把耗时操作移出事务(最推荐的治本方案)
事务的职责应该只包裹数据库相关操作,把远程API调用这种非DB操作放到事务外面,大幅缩短锁的持有时间:
原来的错误写法:
from django.db import transaction import requests def sync_remote_record(obj_id): with transaction.atomic(): # 事务内查询 obj = MyModel.objects.get(id=obj_id) # 耗时的API调用(锁被持续持有) requests.post("https://remote-api.com/update", data={"data": obj.serialize()}) # 本地更新 obj.status = "synced" obj.save()
修改后的写法:
from django.db import transaction import requests def sync_remote_record(obj_id): # 1. 先在事务外获取需要同步的数据 obj = MyModel.objects.get(id=obj_id) # 2. 执行耗时的远程API调用(此时无锁) response = requests.post("https://remote-api.com/update", data={"data": obj.serialize()}) if response.ok: # 3. 仅在需要更新本地数据时开启短事务 with transaction.atomic(): # 重新获取对象并加锁,避免期间主进程修改了数据 obj = MyModel.objects.select_for_update().get(id=obj_id) obj.status = "synced" obj.save()
2. 重新评估transaction_mode的必要性
你设置的IMMEDIATE模式适合需要确保后续写操作不会被阻塞的场景,但对于你的单用户+串行任务场景,改成默认的DEFERRED模式可能更合适:
DEFERRED模式下,事务只有在真正执行写操作时才会获取写锁,查询阶段不会持有锁- 这样哪怕任务里有API调用,只要还没到UPDATE步骤,主进程的写操作都不会被锁挡住
修改配置片段:
'OPTIONS': { # ... 其他配置不变 'transaction_mode': 'DEFERRED', # 替换原有的IMMEDIATE # ... }
3. 临时应急:延长busy_timeout(治标不治本)
如果暂时没法重构代码,可以把PRAGMA busy_timeout调得更长,比如改成10000(10秒)或15000(15秒),给主进程的写请求更多等待锁释放的时间。但这只是缓兵之计,核心问题还是锁被长时间持有。
4. 确认WAL模式是否真的生效
有时候init_command里的PRAGMA可能没正确执行,你可以在Django shell里验证:
from django.db import connection with connection.cursor() as cursor: cursor.execute("PRAGMA journal_mode;") print(cursor.fetchone()) # 正常应该返回('wal',)
最后再提个小细节
你的Celery是串行执行任务,所以任务之间不会互相锁,但Celery进程和主Django进程是两个独立的进程,这才是并发的来源——所以核心还是要尽量缩短写锁的持有时间,让两个进程的写操作能快速交替执行。
备注:内容来源于stack exchange,提问作者David Eyk
相关产品推荐
相关产品推荐

