在 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/