谁能告诉我django的延迟加载和事务?
Could anyone tell me about the lazy loading and transaction of django?
这样的背景:
data = User.objects.get(pk=1)
if data.age > 20:
with transaction.atomic():
data.age -=2
data.save()
我想知道,如果多个进程同时做代码,就像这样,每个进程都会在没有事务的情况下同时获取数据,例如,年龄是30。
然后,一个进程做下一个,使age-2=28并保存。
然后进行下一个过程,当它进行data.age -=2 时,数据逐个获取。
年龄,是18岁还是20岁?如果是20,是不是交易添加错了地方?或者这意味着,交易将不起作用,因为交易会添加到数据中 select 行,并且可以更改 save.but 交易添加 select 行?
第二个问题:
如果我这样做:
data = User.objects.get(pk=1)
with transaction.atomic():
if data.age > 20:
data.age -=2
data.save()
这个demo,在data.age > 20之前添加交易。
对于延迟加载,sql 行会在我使用它时执行,例如 data.age > 20。
但是当它准备好执行 sql 行时,事务已经添加了。
所以,我想知道,这个演示是否会在 sql 行添加交易?
非常感谢,好人。
这里有两个问题我们需要解决;事务和锁定以及延迟加载(您的代码似乎没有使用)。
您的所有示例中都存在竞争条件;获取同一用户年龄的多个请求将尝试更新数据库 table 以设置 18
如果他们在其中任何一个提交事务之前都获取了 20
。
如果列是在事务内部或外部获取的,这里并不重要。事务所保证的是,所有 writes 将一起成功,或者都将一起失败。读取的数据会保持一致(所以多次读取会产生相同的数据),但事务不会阻止其他事务根据读取的数据进行读取和更新。
那是因为原子事务只在写入数据时(短暂地)锁定行;事务中的所有更改都写在一起,作为一个单元。但这并不代表你写到数据库的内容是正确的,多个事务可以读取 20
作为年龄,并且在轮到他们获得锁时都会将 18
写入行并让他们的提交成功。
但是,要解决延迟加载问题,除非您明确 marked the age
column with defer()
您没有使用任何延迟加载。在执行 User.objects.get()
方法时,age
值将与所有其他 User
数据一起加载。这在这里并不重要,因为即使 user.age > 20
测试触发一个单独的语句来读取 age
列,您仍然会读取不一致的数据(您可以只读取 20
在另一个事务提交并写入 18
).
之前
那么您需要的是在读取前锁定该行,这样其他请求就无法读取到错误的值。如果先加锁,然后读,再提交,再解锁,其他请求就得等到锁释放了再读age
列。
您可以使用 select_for_update()
method 来锁定特定行,此时任何其他试图锁定同一行的请求都必须等到您完成锁定:
with transaction.atomic():
data = User.objects.select_for_update().get(pk=1)
if data.age > 20:
data.age -=2
data.save()
但是,您应该只将锁定用作最后的手段。锁定会造成性能瓶颈,因为现在请求必须相互等待。除非您的实际用例更复杂并且涵盖必须作为一个单元执行的多个读取和写入并且您只能使用 Python 代码来做出决定,否则您不需要求助于使用行锁定。
相反,如果您需要以原子方式更新列,您应该使用 update()
query with a filter on the age, at which point it is the database that determines if the age needs updating. Together with an F()
expression,然后将整个计算留给数据库,数据库以原子方式执行:
from django.db.models import F
rowcount = User.objects.filter(pk=1, age_gt=20).update(age=F('age') - 2)
对于更复杂的场景,您可以使用 conditional expression 来确定更新中的最终值。
使用带有适当过滤器和表达式的 UPDATE
语法将工作移至 数据库 ,以测试您的条件和值计算,它会这样做同时提交,因此当行被锁定时。这确保了尽可能少地持有锁,从而减少了瓶颈。
这样的背景:
data = User.objects.get(pk=1)
if data.age > 20:
with transaction.atomic():
data.age -=2
data.save()
我想知道,如果多个进程同时做代码,就像这样,每个进程都会在没有事务的情况下同时获取数据,例如,年龄是30。
然后,一个进程做下一个,使age-2=28并保存。
然后进行下一个过程,当它进行data.age -=2 时,数据逐个获取。
年龄,是18岁还是20岁?如果是20,是不是交易添加错了地方?或者这意味着,交易将不起作用,因为交易会添加到数据中 select 行,并且可以更改 save.but 交易添加 select 行?
第二个问题:
如果我这样做:
data = User.objects.get(pk=1)
with transaction.atomic():
if data.age > 20:
data.age -=2
data.save()
这个demo,在data.age > 20之前添加交易。 对于延迟加载,sql 行会在我使用它时执行,例如 data.age > 20。 但是当它准备好执行 sql 行时,事务已经添加了。 所以,我想知道,这个演示是否会在 sql 行添加交易?
非常感谢,好人。
这里有两个问题我们需要解决;事务和锁定以及延迟加载(您的代码似乎没有使用)。
您的所有示例中都存在竞争条件;获取同一用户年龄的多个请求将尝试更新数据库 table 以设置 18
如果他们在其中任何一个提交事务之前都获取了 20
。
如果列是在事务内部或外部获取的,这里并不重要。事务所保证的是,所有 writes 将一起成功,或者都将一起失败。读取的数据会保持一致(所以多次读取会产生相同的数据),但事务不会阻止其他事务根据读取的数据进行读取和更新。
那是因为原子事务只在写入数据时(短暂地)锁定行;事务中的所有更改都写在一起,作为一个单元。但这并不代表你写到数据库的内容是正确的,多个事务可以读取 20
作为年龄,并且在轮到他们获得锁时都会将 18
写入行并让他们的提交成功。
但是,要解决延迟加载问题,除非您明确 marked the age
column with defer()
您没有使用任何延迟加载。在执行 User.objects.get()
方法时,age
值将与所有其他 User
数据一起加载。这在这里并不重要,因为即使 user.age > 20
测试触发一个单独的语句来读取 age
列,您仍然会读取不一致的数据(您可以只读取 20
在另一个事务提交并写入 18
).
那么您需要的是在读取前锁定该行,这样其他请求就无法读取到错误的值。如果先加锁,然后读,再提交,再解锁,其他请求就得等到锁释放了再读age
列。
您可以使用 select_for_update()
method 来锁定特定行,此时任何其他试图锁定同一行的请求都必须等到您完成锁定:
with transaction.atomic():
data = User.objects.select_for_update().get(pk=1)
if data.age > 20:
data.age -=2
data.save()
但是,您应该只将锁定用作最后的手段。锁定会造成性能瓶颈,因为现在请求必须相互等待。除非您的实际用例更复杂并且涵盖必须作为一个单元执行的多个读取和写入并且您只能使用 Python 代码来做出决定,否则您不需要求助于使用行锁定。
相反,如果您需要以原子方式更新列,您应该使用 update()
query with a filter on the age, at which point it is the database that determines if the age needs updating. Together with an F()
expression,然后将整个计算留给数据库,数据库以原子方式执行:
from django.db.models import F
rowcount = User.objects.filter(pk=1, age_gt=20).update(age=F('age') - 2)
对于更复杂的场景,您可以使用 conditional expression 来确定更新中的最终值。
使用带有适当过滤器和表达式的 UPDATE
语法将工作移至 数据库 ,以测试您的条件和值计算,它会这样做同时提交,因此当行被锁定时。这确保了尽可能少地持有锁,从而减少了瓶颈。