2011-03-27 43 views
4

在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

在将更改提交到实际表之前,事务已经锁定了表。 – 2011-03-27 06:58:50

回答

4

您可以更新添加到SELECT语句锁定所有穿过它自带的行。

但这还不够 - 你基本上需要锁定整个表(因为你要更新整个表),并且事务不会为你做。 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会话事务隔离级别REPEATABLE READ;你连接到数据库之后,而不是由每个交易确保包括在这种情况下,SESSION关键字)

如果我错了,或者在这里错过了一些东西 - 或者即使有更好的方法,我也会很高兴知道它,因为我正在处理这种情况。

相关问题