首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >select_for_update是否看到另一个select_for_update事务在解除阻塞后添加的行?

select_for_update是否看到另一个select_for_update事务在解除阻塞后添加的行?
EN

Stack Overflow用户
提问于 2019-03-06 22:52:04
回答 2查看 80关注 0票数 0

我想要创建一个ID等于该模型当前最大ID的模型,再加上一个(比如自动增量)。我正在考虑使用select_for_update这样做,以确保当前最大ID不存在争用条件,如下所示:

代码语言:javascript
运行
复制
with transaction.atomic():
    greatest_id = MyModel.objects.select_for_update().order_by('id').last().id
    MyModel.objects.create(id=greatest_id + 1)

但是我想知道,如果两个进程试图同时运行,一旦第二个进程解除阻塞,它会看到第一个进程插入的新的最大ID,还是仍然看到旧的最大ID?

例如,假设当前最大ID为10,两个进程将创建一个新模型。第一个锁ID 10,然后第二个锁因为10被锁定。第一个是插入11,然后解锁10。然后,第二个打开块,现在它会看到第一个插入的11是最大的,还是仍然看到10,因为这是它阻塞的行?

在select_for_update 文档中,它说:

通常,如果另一个事务已经获得选定行之一的锁,则查询将被阻塞,直到锁被释放。

因此,对于我的例子,我认为这意味着第二个进程将重新运行对最大ID的查询,一旦它解除阻塞并得到11,但我不确定我是否正确地解释了这一点。

注意:我在数据库中使用MySQL。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-03-06 23:47:27

不,我不认为这会奏效。

首先,让我注意,您绝对应该检查您正在使用的数据库的文档,因为Django文档中没有捕获的数据库之间有许多细微的差异。

使用PostgreSQL文档作为指导,问题是,在默认的READ COMMITTED隔离级别上,阻塞的查询不会被重新运行。当第一个事务提交时,阻塞的事务将能够看到对该行的更改,但它将无法看到添加了新行。

更新命令可以看到不一致的快照:它可以看到并发更新命令对它试图更新的同一行的影响,但它看不到这些命令对数据库中其他行的影响。

因此,10是返回的内容。

票数 1
EN

Stack Overflow用户

发布于 2019-03-07 04:39:36

编辑:我对这个答案的理解是错误的,只是把它留给文档,以防我想要回到它。

经过一些调查后,我相信这将如预期的那样运作。

原因是这个电话:

代码语言:javascript
运行
复制
MyModel.objects.select_for_update().order_by('id').last().id

SQL Django生成和运行的数据库实际上是:

代码语言:javascript
运行
复制
SELECT ... FROM MyModel ORDER BY id ASC FOR UPDATE;

(对last()的调用只有在查询集已经评估之后才会发生。)

这意味着,查询在运行两次时都会对所有行进行扫描。这意味着它第二次运行时,它将拾取新行并相应地返回它。

我了解到,这种现象被称为“幻影读取”,这是可能的,因为我的db的隔离级别是REPEATABLE-READ

@KevinChristopherHenry“问题是,在释放锁之后,查询不会重新运行;行已经被选中”,您确定它是这样工作的吗?为什么读提交意味着锁释放后没有运行select?我认为隔离级别定义了查询运行时看到的数据快照,而不是~查询运行时看到的快照。在我看来,选择发生在释放锁之前还是之后,这与隔离级别是正交的。根据定义,被阻塞的查询不是在解除阻塞之后才选择行吗?

为了说明它的价值,我尝试通过在shell中打开到我的db的两个单独的连接并发出一些查询来测试它。在第一个过程中,我开始了一个事务,并获得了一个锁'select * from MyModel order by id for update‘。然后,在第二个过程中,我做了同样的操作,导致select块。然后回到第一个,我插入了一个新的行,并完成了事务。然后,在第二个查询中,解除阻塞,并返回新行。这让我觉得我的假设是正确的。

P.S.我终于读到了你读过的“不良结果”文档,我看到了你的观点--在那个例子中,它似乎忽略了没有预选的行,所以这将指向我的第二个查询不会选择新行的结论。但我测试了一个外壳它做到了。现在我不知道该怎么做了。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55033473

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档