使用事务隔离级别锁定SQL Server中的数据

我开始开发应该处理数据访问并发问题的应用程序,而且我无法理解如何正确使用事务隔离级别.

我有下表名为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的新父级):

>开始交易
>读取ID = 2的文件夹(将其命名为folder2):SELECT * FROM Folders WHERE Id = 2
>读取ID = 4的文件夹(将其命名为folder4):SELECT * FROM Folders WHERE Id = 4
>更新folder2的ParentId和FullPath:UPDATE文件夹SET ParentId = folder4.Id,FullPath = folder4.FullPath folder2.Name’/’WHERE Id = folder2.Id
>读取folder2的所有子文件夹(称为subfoldersOfFolder2):SELECT * FROM Folders WHERE FullPath LIKE folder2.FullPath’%’
>对于subfoldersOfFolder2中的每个子文件夹,更新FullPath列(查询省略)
>提交交易

显然,在我的事务完成之前,我不希望任何其他事务写入(甚至读取)folder2和subfoldersOfFolder2.

在阅读this article on SQL Server transactions之后,我得到了在步骤#1中将隔离级别设置为Serializable的想法将帮助我实现这一目标.但出于某种原因,这似乎并没有发生.我尝试打开事务(在第7步之前停止),打开另一个SSMS实例并执行SELECT * FROM Folders,并且查询成功完成,我仍然可以看到第一个事务读取的数据.

为什么会这样?如何防止其他人读/写folder2和subfoldersOfFolder2?我觉得我错过了一些关于事务如何实际锁定数据的重要信息.

最佳答案 当您使用Serializable时,它的作用是保持您已读取的行上的共享锁(来自SELECT),直到事务完成.但是一行上的共享锁不会阻止另一个事务读取同一行……它只是阻止另一个事务获得该行(用于共享锁)的独占锁以进行更新或删除.

如果要阻止任何其他事务甚至在这些行上读取(SELECT),则需要在SELECT时强制执行独占锁定:

SELECT *
FROM dbo.Folders WITH (XLOCK)
WHERE ....

现在,如果此事务“保持打开”,则没有其他事务可以读取该WHERE条件选择的任何行 – 直到SELECT .. FROM dbo.Folders WITH(XLOCK)事务已提交或回滚.

点赞