在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语句之间没有任何变化?
提前感谢您的帮助
答案 0 :(得分:4)
您可以在select语句中添加FOR UPDATE以锁定它遇到的所有行。
但这还不够 - 你基本上需要锁定整个表(因为你要更新整个表),而且交易不会为你做。 innoDB具有行级锁定,只有所需的行被锁定,这将在这里严重失败,因为您只选择一行,但更新所有行。
选择整个表FOR UPDATE以锁定所有行可能会锁定所有行,但它最好是您运行的第一个SELECT。否则,您读取一行,并冻结您的视图(一致读取),然后另一个事务执行相同的操作。现在你们两个都有相同的视图 - 但是第二个事务实际上需要读取即将发生变化的数据! (第二个事务不会阻塞 - 您正在读取和锁定不同的行。系统不知道您计划更新整个表。)因此,如果您以这种方式锁定(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关键字。)
如果我错了,或者在这里遗漏了一些东西 - 或者即使有更好的方法,我也会非常感激,因为我正在处理同样的情况。