mysql - 在以下情况下是否需要表锁来保持数据完整性

标签 mysql sql locking nested-sets

在 MySQL 中,我有一个由嵌套集表示的树。嵌套集的操作需要修改表中的多行。例如,向树中添加一个节点将需要重新排序左右值...

SELECT @myLeft := lft FROM folders WHERE ID = ?;
UPDATE folders SET rgt = rgt + 2 WHERE rgt > @myLeft;
UPDATE folders SET lft = lft + 2 WHERE lft > @myLeft;

目前我正在将其包装在交易中。但是,我想知道......是否足以确保数据完整性的事务?

我担心因为有两个单独的 UPDATE 语句,在高容量环境中这可能会导致问题...我是否也应该锁定表,以确保这两个 UPDATE 语句之间没有任何变化?

预先感谢您的帮助

最佳答案

您可以将 FOR UPDATE 添加到 select 语句以锁定它遇到的所有行。

但这还不够——您基本上需要锁定整个表(因为您要更新整个表),事务不会为您做这件事。 innoDB 具有行级锁定,并且只锁定需要的行,这将在这里严重失败,因为您只选择了一行,但更新了所有内容。

SELECTing the entire table FOR UPDATE 以锁定所有行可能会锁定所有内容,但最好是您运行的第一个 SELECT。否则你读取一行,并卡住你的 View (一致读取),然后另一个事务做同样的事情。现在你们两个有相同的观点——但是第二个事务实际上需要读取即将改变的数据! (第二个事务不会阻塞 - 你正在读取和锁定不同的行。系统不知道你打算更新整个表。)所以如果你以这种方式锁定(FOR UPDATE),那条语句必须是一个用于“卡住”您对表格的看法。

经过一些研究,我决定唯一可能的解决方案是建议锁定。

(MySQL 中的常规 LOCK 命令不适用于事务。)

改用 GET_LOCK 函数 - 并在打开交易之前运行它,而不是之后。

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT GET_LOCK('folders_nested_set', <timeout>); /* remember to check to make sure the lock worked */
START TRANSACTION WITH CONSISTENT SNAPSHOT;

do work on folders table here

COMMIT;
DO RELEASE_LOCK('folders_nested_set');

确保使用嵌套集的所有函数都包含在事务中。不是为了写入,而是为了确保跨 SQL 语句的读取一致。

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION WITH CONSISTENT SNAPSHOT;

do reads here

COMMIT;

除非您知道您正在通过一条 SQL 语句读取所需的所有数据,否则您不需要这个。

(您可以在连接到数据库后立即执行 SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;而不是每个事务都执行。确保在这种情况下包含 SESSION 关键字。)

如果我错了,或者在这里遗漏了什么 - 或者即使有更好的方法,我也会非常感激知道它,因为我正在处理完全相同的情况。

关于mysql - 在以下情况下是否需要表锁来保持数据完整性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5447693/

相关文章:

c++ - 使用 WinAPI 的多个读取器/单个写入器锁定

go - 如何修复2个 channel 相互死锁

java - JPA NamedQuery 选择特定列并返回类类型

mysql - 如何获取postgresql中特定行的最近日期

sql - SQL Server 和 mysql 上的增量备份

c# - 另一个锁定问题

php - Yii2:如何从表单中的两个字段上传两个图像

mysql - 选择所有组合

sql - 如何在 SQL 查询中向 SQL Server 列添加加一 (+1)

sql - 如何使用通配符删除 PostgreSQL 中的多个表