Django的select_for_update方法是否与update方法一起使用?
在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:
...
Using this approach, one would presumably mutate the model instances assigned to entry and call save on these.
There are cases where I'd prefer the alternative approach below, but I'm unsure whether it would work (or even make sense) with select_for_update.
with transaction.atomic():
Entry.objects.select_for_update().filter(author=request.user).update(foo="bar", wobble="wibble")
The documentation states that the lock is created when the queryset is evaluated, so I doubt the update method would work. As far as I'm aware update just performs an UPDATE ... WHERE query, with no SELECT before it. However, I would appreciate it if someone more experienced with this aspect of the Django ORM could confirm this.
A secondary question is whether a lock even adds any protection against race conditions if one makes a single UPDATE query against the locked rows. (I've entered this train of thought because I'm refactoring code that uses a lock when updating the values of two columns of a single row.)
回答
据我所知,更新只是执行一个 UPDATE ... WHERE 查询,在它之前没有 SELECT
对,那是正确的。您可以通过查看实际进行的查询来确认这一点。以规范的 django 教程“民意调查”应用程序为例:
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 反模式:读取-修改-写入周期(存档)