为什么我们需要在 SELECT 期间锁定 mysql table 当开始事务导致任何未决事务被提交时?
Why do we need to lock a mysql table during SELECT when beginning a transaction causes any pending transaction to be committed?
在 this 页面上显示:
Beginning a transaction causes any pending transaction to be
committed.
所以本质上开始交易就像一把锁。但随后在 this 页面上显示:
If you query data and then insert or update related data within the
same transaction, the regular SELECT statement does not give enough
protection. Other transactions can update or delete the same rows you
just queried.
这些说法对我来说似乎是矛盾的,我该如何调和它们?举个例子,假设我正在实现一个 Like 按钮。有一个 table 以下列 (user, post, is_liked)
和另一个存储 (post, total_likes)
的 table。所以要处理类似的请求,我想:
- 读取
is_liked
的值,这是给定 user
和 post
的布尔值。
- 然后切换
is_liked
并像 count 一样更新
所有这些都需要自动完成。根据第一个参考资料,我不需要在读取时锁定,因为开始事务会导致提交任何未决事务。如果其他一些事务试图同时更新行,它将被我的挂起事务阻止。但第二个参考文献另有说明。那么谁是正确的,为什么?
问题 2: 如果我确实需要锁定(我怀疑我确实需要),我是使用 LOCK IN SHARE MODE
还是 FOR UPDATE
?
启动事务不像锁。那是误会。
InnoDB 默认实现 "optimistic locking"。启动事务不会获取任何锁。当您执行锁定 SQL 语句时,将根据需要获取锁。
在您的情况下,您应该选择 SELECT ... FOR UPDATE
,因为这就是您准备做的事情 — 读取记录后更新记录。
回复您的评论:
如果您使用 SELECT 锁定共享模式,可能会导致死锁。
SESSION 1 SESSION 2
SELECT ... LOCK IN SHARE MODE
ok
SELECT ... LOCK IN SHARE MODE
ok
UPDATE
waits
UPDATE
waits
在上面的序列中,您最终会遇到两个相互等待的会话,这是一个死锁。没有人会放弃,所以 MySQL 必须杀死一个或另一个。
您将隐藏触发器中的增加或减少。所以不需要交易
但是你可以锁定table,也可以在交易中看到。
If you query data and then insert or update related data within the same transaction, the regular SELECT statement does not give enough protection. Other transactions can update or delete the same rows you just queried. InnoDB supports two types of locking reads that offer extra safety:
https://dev.mysql.com/doc/refman/8.0/en/innodb-locking-reads.html
MySQL 文档中的声明:
不正确,是问题混淆的原因。如果开始一个事务导致任何未决事务被提交,那么所有事务都被有效地序列化。正确的说法可能是:开始一个事务会导致在同一会话(连接)内提交任何待处理的事务
见https://forums.mysql.com/read.php?10,698642,698642#msg-698642
在 this 页面上显示:
Beginning a transaction causes any pending transaction to be committed.
所以本质上开始交易就像一把锁。但随后在 this 页面上显示:
If you query data and then insert or update related data within the same transaction, the regular SELECT statement does not give enough protection. Other transactions can update or delete the same rows you just queried.
这些说法对我来说似乎是矛盾的,我该如何调和它们?举个例子,假设我正在实现一个 Like 按钮。有一个 table 以下列 (user, post, is_liked)
和另一个存储 (post, total_likes)
的 table。所以要处理类似的请求,我想:
- 读取
is_liked
的值,这是给定user
和post
的布尔值。 - 然后切换
is_liked
并像 count 一样更新
所有这些都需要自动完成。根据第一个参考资料,我不需要在读取时锁定,因为开始事务会导致提交任何未决事务。如果其他一些事务试图同时更新行,它将被我的挂起事务阻止。但第二个参考文献另有说明。那么谁是正确的,为什么?
问题 2: 如果我确实需要锁定(我怀疑我确实需要),我是使用 LOCK IN SHARE MODE
还是 FOR UPDATE
?
启动事务不像锁。那是误会。
InnoDB 默认实现 "optimistic locking"。启动事务不会获取任何锁。当您执行锁定 SQL 语句时,将根据需要获取锁。
在您的情况下,您应该选择 SELECT ... FOR UPDATE
,因为这就是您准备做的事情 — 读取记录后更新记录。
回复您的评论:
如果您使用 SELECT 锁定共享模式,可能会导致死锁。
SESSION 1 SESSION 2
SELECT ... LOCK IN SHARE MODE
ok
SELECT ... LOCK IN SHARE MODE
ok
UPDATE
waits
UPDATE
waits
在上面的序列中,您最终会遇到两个相互等待的会话,这是一个死锁。没有人会放弃,所以 MySQL 必须杀死一个或另一个。
您将隐藏触发器中的增加或减少。所以不需要交易
但是你可以锁定table,也可以在交易中看到。
If you query data and then insert or update related data within the same transaction, the regular SELECT statement does not give enough protection. Other transactions can update or delete the same rows you just queried. InnoDB supports two types of locking reads that offer extra safety:
https://dev.mysql.com/doc/refman/8.0/en/innodb-locking-reads.html
MySQL 文档中的声明:
不正确,是问题混淆的原因。如果开始一个事务导致任何未决事务被提交,那么所有事务都被有效地序列化。正确的说法可能是:开始一个事务会导致在同一会话(连接)内提交任何待处理的事务
见https://forums.mysql.com/read.php?10,698642,698642#msg-698642