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

标签 sql sql-server transactions

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

我有下表,名为 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 的新父文件夹):

  1. 开始交易
  2. 读取ID=2的文件夹(将其称为folder2):SELECT * FROM Folders WHERE Id=2
  3. 读取ID=4的文件夹(称之为folder4):SELECT * FROM Folders WHERE Id=4
  4. 更新ParentIdFullPathfolder2 :UPDATE Folders SET ParentId=folder4.Id, FullPath=folder4.FullPath+folder2.Name+'/' WHERE Id = folder2.Id
  5. 读取 folder2 的所有子文件夹(称它们为subfoldersOfFolder2):SELECT * FROM Folders WHERE FullPath LIKE folder2.FullPath + '%'
  6. 对于每个subfoldersubfoldersOfFolder2更新FullPath列(省略查询)
  7. 提交交易

显然,我不希望任何其他事务写入(甚至读取)folder2subfoldersOfFolder2直到我的交易完成。

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

为什么会发生这种情况?我怎样才能阻止其他人读/写folder2subfoldersOfFolder2 ?我觉得我遗漏了一些关于事务如何实际锁定数据的重要信息。

最佳答案

当您使用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/

相关文章:

php - 将三个 SQL 语句合并为一个

mysql - SQL,自身连接表,按一定时间跨度内创建的顺序

hibernate - 第二个数据库操作卡住事务中的执行

java - 在事务mysql中创建另一个连接

sql - 如何转义 listQualify 函数中的逗号?

java - 数据库 INSERT 语句

sql-server - 这是否需要递归 CTE,只是创造性的窗口函数,一个循环?

c# - 我怎样才能做空?

c# - 涉及在进入范围之前创建的 DbTransaction 的 TransactionScope

php - 搜索并替换 MySQL : nothing was replaced