Django模型中的并发控制

tyg4sfes  于 2022-12-27  发布在  Go
关注(0)|答案(4)|浏览(192)

如何在Django模型中处理并发性?我不希望对记录的更改被读取同一记录的其他用户覆盖。

b4qexyjb

b4qexyjb1#

简短的回答是,这真的不是Django提出的问题。
并发控制通常被认为是一个技术问题,但在很多方面它是一个功能需求的问题。你希望/需要你的应用程序如何工作?在我们知道之前,很难给予任何Django特有的建议。
但是,我想漫无边际,所以这里去...
当遇到并发控制的需求时,我倾向于问自己两个问题:

  • 两个用户需要同时修改同一条记录的可能性有多大?
  • 如果用户对记录的修改丢失,对用户有何影响?

如果冲突的可能性相对较高,或者丢失修改的影响很严重,则您可能会看到某种形式的悲观锁定。在悲观方案中,每个用户在打开记录进行修改之前都必须获取逻辑锁。
悲观锁定带来了很多复杂性,必须同步对锁的访问,考虑容错、锁过期、超级用户能否覆盖锁、用户能否看到谁拥有锁等等。
在Django中,这可以通过一个单独的Lock模型或者在被锁定的记录上使用某种“lock user”外键来实现。使用锁表在存储获取锁的时间、用户、注解等方面给了你更多的灵活性。如果你需要一个通用的锁表,可以用来锁定任何类型的记录,那么可以看看django.contrib.contenttypes framework,但很快就会演变成抽象宇航员综合症。
如果冲突不太可能发生,或者丢失的修改可以轻易地重新创建,那么您可以使用乐观并发技术。这种技术简单且易于实现。本质上,您只需跟踪版本号或修改时间戳,并拒绝任何检测到不正常的修改。
从功能设计的Angular 来看,您只需考虑如何将这些并发修改错误呈现给您的用户。
在Django中,乐观并发控制可以通过覆盖模型类上的保存方法来实现...

def save(self, *args, **kwargs):
    if self.version != self.read_current_version():
        raise ConcurrentModificationError('Ooops!!!!')
    super(MyModel, self).save(*args, **kwargs)

当然,要使这两种并发机制中的任何一种都是健壮的,就必须考虑transactional control,如果不能保证事务的ACID属性,这两种模型都不能完全工作。

jtoj6r0c

jtoj6r0c2#

我不认为“保留版本号或时间戳”有效。
self.version == self.read_current_version()True时,仍有可能在您调用super().save()之前版本号被其他会话修改。

bvjxkvbb

bvjxkvbb3#

我同意乔·霍洛威的介绍性解释。
我想提供一个与他的回答的最后一部分相关的工作片段(“就Django而言,乐观并发控制可以通过覆盖模型类上的保存方法来实现...”)
可以使用以下类作为自己模型的祖先。
如果您在数据库事务内部(例如,通过在外部作用域中使用 transaction.atomic),则以下Python语句是安全且一致的
在实践中,通过一次触发,语句filter + update在记录上提供了一种test_and_set:它们验证版本并获取该行上的隐式数据库级锁。
因此,下面的“保存”操作能够更新记录的字段,确保它是在该模型示例上操作的唯一会话。
最后一次提交(例如,由transaction.atomic中的_exit_自动执行)释放该行上的隐式数据库级锁:

class ConcurrentModel(models.Model):
    _change = models.IntegerField(default=0)

    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        cls = self.__class__
        if self.pk:
            rows = cls.objects.filter(
                pk=self.pk, _change=self._change).update(
                _change=self._change + 1)
            if not rows:
                raise ConcurrentModificationError(cls.__name__, self.pk)
            self._change += 1
        super(ConcurrentModel, self).save(*args, **kwargs)

它取自https://bitbucket.org/depaolim/optlock/src/ced097dc35d3b190eb2ae19853c2348740bc7632/optimistic_lock/models.py?at=default

相关问题