MySQL事务 vs 锁

3
我正在开发一个应用程序,用户可以提交帖子,其他人可以回复该帖子。当用户发布帖子时,他可以选择哪些用户可以查看该帖子。
这是帖子表:
| id | poster-id | title | message | ... |

这是“posts-visible”表格。它表示帖子和成员之间的个体关系(这意味着他们可以查看和回复帖子):
| id | post-id | user-id |

这是回复表格:
| id | posts-id | poster-id | title | message | ... |

现在,当有人添加回复时,我会在一个事务中运行以下操作:
try
{
    $db->beginTransaction();
    // first check if the user is part of the posts-visible group
    $stmt = $db->prepare('SELECT id FROM `posts-visible` WHERE posts-id=:pid AND poster-id=:uid LOCK IN SHARE MODE');
    // ... bind and execute

    if($stmt->rowCount() <= 0) return false; // not in visible group

    $stmt = $db->prepare('INSERT INTO `replies` ... ');
    // binds and execute

    $db->commit();
}
catch(Exception $e)
{
    $db->rollback();
}

我的问题是:是否需要使用LOCK IN SHARE MODE? 我设想了可能出现的竞争条件,即原始发布者在用户发布回复时删除了对该帖子的访问权限,在这种情况下,尽管发布者禁止他访问,但他仍然可以发布回复,因此我使用了LOCK IN SHARE MODE,以便其他会话可以读取但不能修改它。 然而,事务不已经通过ACID中的隔离保证了同步访问,以防止发生这种情况吗? 如果是这样,那么什么情况下会使用LOCK IN SHARE MODE? 为什么在事务中使用它?
第二个问题是,我还有一些关于帖子信息(例如帖子内容、标题等)的代码。 代码类似,除了没有INSERT之外,第一个SELECT之后还有另一个SELECT来检查访问权限。既然没有写入任何东西,那么是否需要事务? 这与第一个答案有何不同?
最后一个问题。如果我有一个事务,在插入之前检查行是否存在,那么事务是否会防止另一个事务在行检查和插入之间插入一行?
谢谢!

你可以添加一个列,该列引用 posts-visible.id - zerkms
从我所了解的来看,锁定/事务更适合这种情况,逻辑上,我没有看到权限和帖子之间有明显的联系。此外,我希望用户之前创建的帖子仍然存在;只是在他的权限被撤销后,他不能再发布帖子了。 - untitled
1
“LOCK IN SHARE MODE” 不是允许多个会话共享锁并仅防止写入,同时允许更新吗?因此,如果两个事务都尝试写入,它们仍然能够插入,因为实际的 INSERT 操作不会影响已锁定的行,对吗? - untitled
更新是一种写操作吗?或者我在这里漏掉了什么东西.... - Namphibian
在插入、更新或选择期间,总是会创建一个锁。更重要的是要理解其他操作如何尊重该锁。 - Namphibian
显示剩余6条评论
1个回答

1
我的问题是是否因为事务而需要锁定。
简短的答案是,是的,你需要。
所有的事务只会给你提供回滚的手段,如果在之后的某个时刻发生了失败。没有锁定,就无法知道在你的两个查询之间,基础表是否被修改。
请记住,假设您使用的是InnoDB,您的“SELECT ... LOCK IN SHARE MODE”只会锁定所选的行,而不是整个表如此记录在这里。我强烈建议阅读这份文档,特别是两种方法之间的区别以及何时使用每种方法。
这个概念也适用于你的第二个问题,并且在这个答案中很好地解释

最终,问题归结为以下几点:

锁定可以防止其他人干扰你正在处理的任何数据库记录。事务可以防止“后面”的错误干扰你之前完成的操作。单独使用这两个方法都不能保证最终结果是正确的,但是一起使用就可以。

明天的课程:死锁的乐趣。


谢谢您的回复!如果事务存在以允许一组All-or-Nothing查询,那么它如何满足ACID中的隔离性?如果是这样,那么是否意味着仅用于SELECT的事务没有意义,因为LOCK正在确保没有数据更改,而事务仅在修改时有用?此外,链接页面已损坏 :( - untitled
@untitled 我已更新链接,由于某种原因SO自行添加了www.在开头。是的,在您的情况下,可能不需要事务。这更多是为了当您想要回滚所做的更改时使用,如果出现问题,例如进行了一些更改并且它们全部成功或全部失败(您可以ROLLBACK)。 - Alex.Ritna

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接