在以下情况下是否需要使用表锁来维护数据完整性

时间:2011-03-27 06:49:24

标签: 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语句之间没有任何变化?

提前感谢您的帮助

1 个答案:

答案 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关键字。)

如果我错了,或者在这里遗漏了一些东西 - 或者即使有更好的方法,我也会非常感激,因为我正在处理同样的情况。