gpt4 book ai didi

python - Django select_for_update 可以用来获取读锁吗?

转载 作者:行者123 更新时间:2023-12-05 04:45:20 24 4
gpt4 key购买 nike

我正在做一个类似于电子商务的项目,在这个项目中,我有一些模型来表示存储中有一定数量的产品,用户可以购买一定数量的产品,只要它不超过存储的数量.我想避免在服务器收到多个购买同一产品的请求时发生竞争条件。

class Product(models.Model):
amount_in_storage = models.PositiveIntegerField() # for design reasons, this amount is unchangeable, it must be only evaluated once during initialization like constants in C++

@property
def amount_available_for_purchase(self):
return self.amount_in_storage - Purchase.objects.filter(product=self.id).aggregate(models.Sum('amount'))["sum__amount"]
class Purchase(models.Model):
product = models.ForeignKey(Product, ...)
amount = models.PositiveIntegerField()
payment_method = ...

我们假设这是负责创建购买的代码块。

@atomic_transaction
def func(product_id, amount_to_purchase):
product = Product.objects.get(...)
if product.amount_available_for_purchase > amount_to_purchase:
# do payment related stuff
Purchase.objects.create(product=product, amount=amount_to_purchase)
# do more stuff

我想限制对这个代码块的并发访问,理想情况下我想在 if 条件下获得一个 read 锁访问,这样如果多个线程尝试查看可用数量是否大于购买数量,其中一个线程必须等到交易完成,然后才会评估读取请求,所以我想利用 Django 的 select_for_update 和一个像这样的 version 字段:

class Product(models.Model):
amount_in_storage = models.PositiveIntegerField()
version = models.PositiveIntegerField() # we use this field just to write to it, no reading will take place

@property
def amount_available_for_purchase(self):
return self.amount_in_storage - Purchase.objects.filter(product=self.id).aggregate(models.Sum('amount'))["sum__amount"]

然后我使用这个字段来获取锁,如下所示:


@atomic_transaction
def func(product_id, amount_to_purchase):
product = Product.objects.select_for_update.get(...)

# acquiring a lock
product.version += 1
product.save()

if product.amount_available_for_purchase > amount_to_purchase:
# do payment related stuff
Purchase.objects.create(product=product, amount=amount_to_purchase)
# do more stuff

使用select_for_update 如果多个线程到达版本修改行,只有第一个会评估,其余的必须等到整个事务完成,因此获取行的读锁如果条件。换句话说,一次只有 1 个线程可以访问此代码块。

我的问题是:

  1. 我的方法是否符合我的预期?
  2. 这是一个干净的方法吗?如果不是,如何在不使代码库复杂化和进行重大重构的情况下实现这一点?

最佳答案

  1. 是的,您的方法非常正确,它将为第一个查询获取锁,其余的必须等到事务完成。

  2. 如果除了获取锁之外,您没有将版本字段用于其他用途,则可以通过使用 list() 强制评估 QuerySet 来强制获取锁。请参阅下面的代码。

    @atomic_transaction
    def func(product_id, amount_to_purchase):
    # Forcefully acquiring a lock
    product = list(Product.objects.select_for_update.filter(...))[0]

    if product.amount_available_for_purchase > amount_to_purchase:
    # do payment related stuff
    Purchase.objects.create(product=product, amount=amount_to_purchase)
    # do more stuff

在这里,您通过评估 QuerySet 来强制获取锁,就像在 docs 中提到的那样。 .

关于python - Django select_for_update 可以用来获取读锁吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69173209/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com