【问题标题】:Best approach to handle concurrency in Django for eauction toy-app在 Django 中为拍卖玩具应用处理并发的最佳方法
【发布时间】:2011-06-08 22:10:26
【问题描述】:

我正在 Django 中实现一个拍卖玩具应用程序,并且对如何在下面的代码中最好地处理并发感到困惑。我不确定我的哪个解决方案候选者(或任何其他)最适合 Django 的设计。我对 Django/python 还很陌生,而且我的 SQL 专业知识很生疏,所以如果这是一个明智的选择,我深表歉意。

要求:用户可以对产品出价。只有在同一产品的出价高于之前的出价时,才会接受出价。

这是模型的精简版:

class Product(models.Model):
    name = models.CharField(max_length=20)

class Bid(models.Model):
    amount = models.DecimalField(max_digits=5, decimal_places=2)
    product = models.ForeignKey(Product)

和出价视图。这是竞争条件发生的地方(参见 cmets):

def bid(request, product_id):
    p = get_object_or_404(Product, pk=product_id)
    form = BidForm(request.POST)
    if form.is_valid():
        amount = form.cleaned_data['amount']

        # the following code is subject to race conditions
        highest_bid_amount = Bid.objects.filter(product=product_id).aggregate(Max('amount')).get('amount__max')
        # race condition: a bid might have been inserted just now by another thread so highest_bid_amount is already out of date
        if (amount > highest_bid_amount):
            bid = Bid(amount=amount, product_id=product_id)
            # race condition: another user might have just bid on the same product with a higher amount so the save() below is incorrect
            b.save()
            return HttpResponseRedirect(reverse('views.successul_bid)'

目前我考虑过的候选解决方案:

  1. 我已阅读有关事务的 Django 文档,但我不知道如何将它们应用于我的问题。由于数据库不知道出价必须上升的要求,它不会导致 Django 抛出 IntegrityError。有没有办法在模型定义期间定义这个约束?还是对事务 API 有误解?
  2. 存储过程可以处理出价逻辑。在我看来,到目前为止,这似乎是“最佳”选择,但它将处理竞争条件转移到了底层数据库系统。不过,如果这是一个好方法,这个解决方案可能会与解决方案 1 结合使用吗?
  3. 我考虑使用 select_for_update 调用来锁定该产品的出价。但是,这似乎不是一个解决方案,因为据我了解,它不会影响正在创建的任何出价?

愿望清单:

  • 如果可能的话,我想避免锁定整个投标表,因为其他产品的投标无论如何都不会受到影响。
  • 如果在应用层面有好的解决方案,我希望代码独立于底层数据库系统。

非常感谢您的想法!

【问题讨论】:

    标签: django concurrency


    【解决方案1】:

    您是否可以将highest_bid 列添加到Products。如果我的逻辑没有关闭,那么您可以更新最高出价where product_id = x and highest < current_bid。如果此查询表明某行已更新,则将新记录添加到投标表中。这可能意味着您必须为highest_bid 列设置一个默认值。

    【讨论】:

    • 谢谢!这会成功的。它不仅将数据库锁限制在单行上,而且还允许我摆脱丑陋的聚合函数来计算上面示例代码中的当前“highest_bid”。
    【解决方案2】:

    您查看Celery 了吗?您可以异步处理查询,将查询排队,然后在结果或错误可用时将其返回。如果您想避免锁定,这似乎是一条可行的路径。

    否则,似乎确实需要进行一些锁定。

    【讨论】:

    • 我不知道 Celery。谢谢你!如果我的应用程序不是“玩具”(阅读:少量事务,..),我会考虑 Celery 确保实现可扩展性。
    猜你喜欢
    • 2022-11-17
    • 2021-03-18
    • 2010-11-15
    • 2010-12-19
    • 2010-12-21
    • 2012-11-09
    • 1970-01-01
    • 1970-01-01
    • 2013-03-27
    相关资源
    最近更新 更多