“select_for_update()”可以与 Django 中的 update 方法一起使用吗?

问题描述 投票:0回答:2

我正在使用的 Django 2.2 文档给出了

select_for_update
的以下示例用法:

from django.db import transaction

entries = Entry.objects.select_for_update().filter(author=request.user)
with transaction.atomic():
    for entry in entries:
        ...

使用这种方法,人们可能会改变分配给

entry
的模型实例并在这些实例上调用
save

在某些情况下,我更喜欢下面的替代方法,但我不确定它是否适用于(甚至有意义)

select_for_update

with transaction.atomic():
    Entry.objects.select_for_update().filter(author=request.user).update(foo="bar", wobble="wibble")

文档指出,在评估查询集时创建锁,所以我怀疑

update
方法是否有效。据我所知,
update
只是执行
UPDATE ... WHERE
查询,前面没有
SELECT
。然而,如果对 Django ORM 这方面更有经验的人能够证实这一点,我将不胜感激。

第二个问题是,如果对锁定的行进行单个

UPDATE
查询,锁是否会增加针对竞争条件的任何保护。 (我进入这个思路是因为我正在重构在更新单行两列的值时使用锁的代码。)

python django postgresql locking django-orm
2个回答
13
投票

据我所知,更新只是执行 UPDATE ... WHERE 查询,之前没有 SELECT

是的,没错。您可以通过查看实际的查询来确认这一点。使用规范的 django 教程“polls”应用程序作为示例:

with transaction.atomic():
    qs = polls.models.Question.objects.select_for_update().all()
    qs.update(question_text='test')

print(connection.queries)
# {'sql': 'UPDATE "polls_question" SET "question_text" = \'test\'', 'time': '0.008'}

所以,如您所料,没有

SELECT

不过,确保获取锁就像执行任何操作来导致查询集被评估一样简单。

with transaction.atomic():
    qs = polls.models.Question.objects.select_for_update().all()
    list(qs) # cause evaluation, locking the selected rows
    qs.update(question_text='test')

print(connection.queries)
#[...
# {'sql': 'SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date" FROM "polls_question" FOR UPDATE', 'time': '0.003'},
# {'sql': 'UPDATE "polls_question" SET "question_text" = \'test\'', 'time': '0.001'}
#]

第二个问题是,如果对锁定的行进行单个 UPDATE 查询,锁是否会增加任何针对竞争条件的保护

总的来说,是的。在特定情况下是否有必要取决于您担心哪种竞争条件。例如,锁将防止另一个事务可能尝试更新同一行的竞争条件。

根据更新/竞争条件的性质,也可以在没有锁的情况下避免竞争条件。有时交易就足够了,有时则不然。您还可以使用在数据库上的服务器端评估的表达式来防止竞争条件(例如使用 Django 的

F()
表达式)。

还有其他注意事项,例如数据库方言、隔离级别等。

有关竞争条件思想的其他参考:PostgreSQL 反模式:读-修改-写周期存档


1
投票

要运行

SELECT FOR UPDATE
,您需要分别使用select_for_update()update(),然后将
print(qs)
放在它们之间,如下所示。 *您也可以用
bool(qs)
len(qs)
list(qs)
代替
print(qs)
:

qs = Entry.objects.select_for_update().filter(author=request.user)
print(qs) # bool(qs), len(qs) or list(qs) is also fine
qs.update(foo="bar", wobble="wibble")

你可以看到我的问题和答案在Django中解释更多关于

select_for_update()
的内容:

© www.soinside.com 2019 - 2024. All rights reserved.