我开始开发应该处理数据访问并发问题的应用程序,而且我无法理解如何正确使用事务隔离级别。
我有一个名为Folders
的下表,其中包含一个树状的文件夹结构:
+-----------------------------------------------------------------+
| Id (int) | Name (varchar) | FullPath (varchar) | ParentId (int) |
|----------+----------------+--------------------+----------------|
| 1 | 'root1' | '/root1/' | NULL |
| 2 | 'c1' | '/root1/c1/' | 1 |
| 3 | 'c2' | '/root1/c1/c2/' | 2 |
| 4 | 'root2' | '/root2/' | NULL |
+----------+----------------+--------------------+----------------+
我正在尝试像这样实现“移动文件夹”工作流程(例如,我想将ID = 2的文件夹移动到ID = 4的新父级):
SELECT * FROM Folders WHERE Id=2
SELECT * FROM Folders WHERE Id=4
ParentId
的{{1}}和FullPath
:folder2
UPDATE Folders SET ParentId=folder4.Id, FullPath=folder4.FullPath+folder2.Name+'/' WHERE Id = folder2.Id
的所有子文件夹(称之为folder2
):subfoldersOfFolder2
SELECT * FROM Folders WHERE FullPath LIKE folder2.FullPath + '%'
更新subfolder
列中的每个subfoldersOfFolder2
(查询已省略)显然,在我的事务完成之前,我不希望任何其他事务写入(甚至读取)FullPath
和folder2
。
在阅读this article on SQL Server transactions之后,我认为在步骤#1将隔离级别设置为Serializable将帮助我实现这一目标。但出于某种原因,这似乎并没有发生。我尝试打开事务(在第7步之前停止),打开另一个SSMS实例并执行subfoldersOfFolder2
,并且查询成功完成,我仍然可以看到第一个事务读取的数据。
为什么会这样?如何阻止其他人阅读/撰写SELECT * FROM Folders
和folder2
?我觉得我错过了一些关于事务如何实际锁定数据的重要信息。
答案 0 :(得分:2)
当您使用Serializable
时,它的作用是保持共享锁(来自SELECT
)锁定您已读取的行,直到事务完成。但是一行上的共享锁定不阻止另一个事务读取同一行......它只是阻止另一个事务在该行上获得独占锁定(用于更新或删除的共享锁)。
如果您想阻止任何其他交易甚至在这些行上阅读(SELECT
),您需要在SELECT
时强制执行独占锁定:
SELECT *
FROM dbo.Folders WITH (XLOCK)
WHERE ....
现在,如果此事务“保持打开”,没有其他事务可以读取WHERE
条件选择的任何行 - 直到{ {1}}事务已提交或回滚。
答案 1 :(得分:0)
如果您执行以下操作,它会完全符合您的要求(阻止读者和作者查看行: 在第1场会议中:
create table dbo.test(i1 int, a1 varchar(25))
insert into dbo.test values (1,'NY'),(1,'NY'),(1,'NJ'),(2,'NY'),(2,'NY'),(2,'NJ')
set transaction isolation level serializable
begin transaction
select * from test where i1=1
update dbo.test set i1=3 where a1='NJ'
在第2节尝试
select * from dbo.test where i1=1
...挂起
添加begin try和catch只能改进一些东西,但即使没有它可序列化的工作。您没有向我们展示您的所有代码。
答案 2 :(得分:0)
改变序列化是我最不愿意做的事情。 在这种情况下,我坚持使用单个事务来更新父ID,但是将代码放在更新触发器中更新文件夹路径。