Django批量update后无法遍历对象,求更优解决方案
问题与解决方案
问题
在cron定时任务中处理delivery_que对象时,为避免重复处理(防止当前任务未完成时新启动的cron任务抓取同批对象),需要先将对象状态从PENDING改为SENDING。但当前代码先查询PENDING对象,执行批量更新后,无法遍历原查询集,只能二次查询SENDING状态的对象,希望找到无需二次查库的优化方案。
原因
以常见ORM(如Django ORM)为例,查询集是惰性求值的:执行filter后并未立即查询数据库,只有在遍历、转列表等操作时才会触发SQL查询。当执行update修改了数据库中对象的状态后,再遍历原查询集,它会重新发送SQL到数据库,此时这批对象的状态已经是SENDING,自然查询不到结果。
优化方案
方案1:先将查询集转为内存列表,再批量更新
先把PENDING对象加载到内存列表中,再通过ID批量更新状态,之后直接遍历内存列表处理,无需二次查库:
# 先将PENDING对象加载到内存列表 pending_queues = list(DeliveryQue.objects.filter(status='PENDING')) # 通过ID批量更新状态为SENDING DeliveryQue.objects.filter(id__in=[q.id for q in pending_queues]).update(status='SENDING') # 遍历内存列表处理对象 for queue in pending_queues: process_delivery(queue)
方案2:事务+行锁+批量更新(适用于高并发场景)
如果存在多进程/多实例并发执行cron任务的情况,需要加行锁防止其他进程抢占同批对象,同时用事务保证原子性:
from django.db import transaction with transaction.atomic(): # 给查询到的PENDING对象加行锁,防止其他进程修改 pending_queues = list(DeliveryQue.objects.filter(status='PENDING').select_for_update()) # 批量修改内存中对象的状态 for queue in pending_queues: queue.status = 'SENDING' # 批量更新到数据库 DeliveryQue.objects.bulk_update(pending_queues, ['status']) # 遍历处理对象 for queue in pending_queues: process_delivery(queue)
这两种方案都避免了二次查询数据库,同时保证了不会出现重复处理的问题。
内容的提问来源于stack exchange,提问作者sprice2290
相关产品推荐
相关产品推荐

