使用 PostgreSQL 在 Django 中获取下一个工作项
Get next work-item in Django with PostgreSQL
假设您有一个简单的 table 工作项:
|ID |OWNER|...
+---+-----+---
|123| |...
|456| |...
|789| |...
我们想提供一个 http API 来获取下一个还没有所有者的工作项。
我们使用 PostgreSQL。
我们使用 Django-ORM 访问 table。
如果 API 被许多用户同时访问,我想会有几个竞争条件。
我如何确保使用给定的工具(PostgreSQL、Django)解决所有竞争条件(如果将一个工作项提供给两个或多个用户,这是一个主要错误)。
与select_for_update
:
with transaction.atomic():
work_item = WorkItem.objects.select_for_update().filter(owner__isnull=True).first()
work_item.owner = request.user
work_item.save(update_fields=['owner'])
# process work_item
https://docs.djangoproject.com/en/1.11/ref/models/querysets/#select-for-update
select_for_update
将确保只有一个连接可以更新匹配的行,直到事务结束。
使用 Django 1.11,select_for_update 开始支持 skip_locked
。这意味着您可以节省 save()
次通话,因为您不必立即将其分配给所有者。
例如,建立在@user73657 的回答之上:
with transaction.atomic():
work_item = WorkItem.objects.select_for_update().filter(owner__isnull=True).first()
work_item.owner = request.user
work_item.save(update_fields=['owner'])
# process work_item
你可以做到:
with transaction.atomic():
work_item = WorkItem.objects.select_for_update(skip_locked=True).filter(owner__isnull=True).first()
work_item.owner = request.user
# process work_item, edit other fields
work_item.save()
对于skip_locked=True
,事务跳过锁定的行,因此是非阻塞的。作为奖励,您只需要保存到数据库 一次。
假设您有一个简单的 table 工作项:
|ID |OWNER|...
+---+-----+---
|123| |...
|456| |...
|789| |...
我们想提供一个 http API 来获取下一个还没有所有者的工作项。
我们使用 PostgreSQL。
我们使用 Django-ORM 访问 table。
如果 API 被许多用户同时访问,我想会有几个竞争条件。
我如何确保使用给定的工具(PostgreSQL、Django)解决所有竞争条件(如果将一个工作项提供给两个或多个用户,这是一个主要错误)。
与select_for_update
:
with transaction.atomic():
work_item = WorkItem.objects.select_for_update().filter(owner__isnull=True).first()
work_item.owner = request.user
work_item.save(update_fields=['owner'])
# process work_item
https://docs.djangoproject.com/en/1.11/ref/models/querysets/#select-for-update
select_for_update
将确保只有一个连接可以更新匹配的行,直到事务结束。
使用 Django 1.11,select_for_update 开始支持 skip_locked
。这意味着您可以节省 save()
次通话,因为您不必立即将其分配给所有者。
例如,建立在@user73657 的回答之上:
with transaction.atomic():
work_item = WorkItem.objects.select_for_update().filter(owner__isnull=True).first()
work_item.owner = request.user
work_item.save(update_fields=['owner'])
# process work_item
你可以做到:
with transaction.atomic():
work_item = WorkItem.objects.select_for_update(skip_locked=True).filter(owner__isnull=True).first()
work_item.owner = request.user
# process work_item, edit other fields
work_item.save()
对于skip_locked=True
,事务跳过锁定的行,因此是非阻塞的。作为奖励,您只需要保存到数据库 一次。