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

使用事务隔离级别锁定SQL Server中的数据,sql,sql-server,transactions,Sql,Sql Server,Transactions,我开始开发应该处理数据访问并发性问题的应用程序,但我在理解如何正确使用事务隔离级别方面遇到了困难 我有一个名为Folders的下表,其中包含一个树状文件夹结构: +-----------------------------------------------------------------+ | Id (int) | Name (varchar) | FullPath (varchar) | ParentId (int) | |----------+----------------+----

我开始开发应该处理数据访问并发性问题的应用程序,但我在理解如何正确使用事务隔离级别方面遇到了困难

我有一个名为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:从ID为2的文件夹中选择* 读取ID为4的文件夹称之为folder4:从ID为4的文件夹中选择* 更新文件夹2的ParentId和FullPath:更新文件夹集ParentId=folder4.Id,FullPath=folder4.FullPath+folder2.Name+“/”其中Id=folder2.Id 读取folder2的所有子文件夹将其称为folder2的子文件夹:从folder2.FullPath+“%”等完整路径所在的文件夹中选择* 对于子文件夹Folder2中的每个子文件夹,将忽略更新完整路径列查询 提交事务 显然,在事务完成之前,我不希望任何其他事务写入甚至读取folder2及其子文件夹folder2

在阅读之后,我得到了这样的想法:在步骤1中将隔离级别设置为可序列化将帮助我实现这一点。但出于某种原因,这似乎没有发生。我尝试在步骤7之前停止打开事务,打开另一个SSMS实例并从文件夹中选择*,并且查询成功完成,我仍然可以看到第一个事务读取的数据


为什么会这样?如何防止其他人读取/写入folder2和folder2的子文件夹?我觉得我遗漏了一些关于事务如何实际锁定数据的重要信息。

如果您执行以下操作,它会完全阻止读卡器和写卡器查看行: 在第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-and-catch只能改善情况,但即使没有它,序列化也可以工作。您没有向我们显示您的所有代码。

当您使用Serializable时,它的作用是在事务完成之前,将已读取行上的SELECT上的共享锁保留在原位。但行上的共享锁并不阻止另一个事务读取同一行。。。。。。它只是阻止另一个事务获取该行上的排他锁,该行上的共享锁用于更新或删除

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


现在,如果此事务保持打开状态,则在该SELECT之前,其他事务无法读取该WHERE条件选择的任何行。。来自dbo。已提交或回滚带有XLOCK事务的文件夹。

更改序列化是我唯一的万不得已的选择。
在这种情况下,我会坚持使用单个事务来更新父ID,但会将更新文件夹路径的代码放在更新触发器中。

Serializable特别意味着全局无并发性。这就是你想要的吗?如果不是,您可能应该让修改代码首先使用updlock、holdlock选择所有相关的父节点。但是,这只会直接设置修改代码,不会阻止人们阅读您将要修改但尚未修改的子文件夹的路径。由于您只有在锁定并读取父项竞争条件后才能知道这些子文件夹的ID,因此每次编辑路径时,您似乎都希望在整个表上放置tablock、xlock、holdlock。如果我从代码中删除update语句,会话2将不会挂起。为什么会这样?我喜欢的那篇文章指出,数据库引擎保留对所选数据获取的读写锁,因此在会话1中由select读取的数据应该被锁定,对吗?在没有任何更新/插入/删除的事务中,SQL Server锁定行的设置是不可能的。SQL Server保证了由于更改而产生的各种级别的数据一致性。没有变化,那么什么是保证。您可能想探索快照级隔离,它可以保证数据的一致性,而不需要锁。每个人都有自己的数据版本。@benjaminmoskovits:真的吗?!?!?!XLOCK在行上放置一个排他锁,这会阻止从这些行中进行选择……我错了。即使没有更新,select上带有xlock的select也会阻止对行进行选择。marc_的解决方案是正确的。
SELECT *
FROM dbo.Folders WITH (XLOCK)
WHERE ....