我开始开发应该处理数据访问并发问题的应用程序,但我无法理解如何正确使用事务隔离级别。
我有下表,名为 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
- 更新
ParentId
和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
和subfoldersOfFolder2
直到我的交易完成。
阅读后this article on SQL Server transactions我的想法是,在第 1 步将隔离级别设置为可序列化将帮助我实现这一目标。但由于某种原因,这似乎并没有发生。我尝试让事务保持打开状态(在步骤 #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)
事务已提交或回滚。
关于sql - 使用事务隔离级别锁定 SQL Server 中的数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43941848/