我已经TABLOCK
编辑了一个表,这样我就可以运行批量插入。当它仍处于锁定状态时我如何读取它?
在正常情况下,我认为这显然是不可能的。幸运的是,有一个关键因素导致情况变得不正常。在这种情况下,我根本不关心 ACID。我唯一关心的是不要取消已TABLOCK
编辑表的查询。
我已经尝试过WITH (NOLOCK)
,但迹象表明它不起作用。我是在要求不可能的事情吗?
只是为了说明这一点,假设我正在跑步
INSERT [dbo].[Table1] WITH (TABLOCK)
SELECT * FROM [A_Lot_Of_Rows]
Table1
我的目标是在运行时进行查询。
我正在进行批量插入,并且在它们运行时,我决定要检查一些细节。我不在乎从哪一行得到所述详细信息。
中的每一行A_Lot_Of_Rows
都有一个日期。A_Lot_Of_Rows
被我的存储过程截断,并在再次截断之前以基于日期的批次重新填充。通过读取表中的任意行,我可以知道我所在的批次。
有两种可能:
1. 事务内部截断
如果您要截断表然后在事务中批量加载,则这适用。
截断表需要模式修改 (Sch-M) 锁,这是限制性最强的锁类型。即使在读未提交隔离级别(误导性别名“nolock”)或 RCSI 下进行读取也需要架构稳定性 (Sch-S) 锁,该锁与 Sch-M 不兼容。
或提示通常不会阻止在读未提交
TABLOCK
隔离下读取数据(但请参阅可能性#2)。TABLOCKX
要么在数据加载事务之外截断表,要么单独记录进度并进行查询。
不幸的是,截断表需要 Sch-M 锁,而等效的无限制则
DELETE
不需要,但这就是它的实现方式。2.表是聚簇的
如果表具有聚集索引,则在语句上使用表锁
INSERT
(或在批量加载时指定表锁的任何其他方法)时存在重要区别:RowsetBulk
,这本质上是在幕后构建的离线索引。这需要一个 Sch-M 锁。在这种情况下不可能并发读取。FastLoadContext
机制。这需要目标表上的独占锁(“X”),而不是 Sch-M。在这种情况下,读未提交隔离或 RCSI下的并发读取是可能的。该
FastLoadContext
路径记录的日志量要多得多,RowsetBulk
并且速度可能明显慢一些,但从技术上讲,它仍然是最低限度记录的,并且比完全记录的操作更有效。这一切都假设您的数据库和配置符合最低限度的日志记录要求。还有另一个怪癖,
FastLoadContext
即 SQL Server 执行的计算(查看是否应应用最小日志记录)不会考虑当前批处理(在您的情况下是存储过程)中表基数的任何更改。这意味着 SQL Server 可能决定不进行最小化日志记录,因为它没有注意到表已被
TRUNCATE TABLE
同一过程中的语句清空。据我所知,除了在单独的批次中执行截断之外,没有特别的解决方法。如果您能够捕获插入的实际执行计划,请检查Clustered Index Insert运算符是否将DMLRequestSort属性设置为 true。缓存计划重用也可能是这里的一个因素,提示
OPTION (RECOMPILE)
可以帮助避免这种情况。另请记住,使用写入的第一页数据
FastLoadContext
始终会被完全记录。更多详细信息请参阅我上面链接的文章。批量加载堆时也可能会看到不同的阻塞行为,但这取决于您使用的 SQL Server 版本。请参阅《数据加载性能指南》中标题为“批量加载、NOLOCK 查询和读提交快照隔离”的部分(某些详细信息不适用于当前版本)。
有两种表锁。DML 表锁是对象上的 X 锁,就像您添加了 TABLOCKX 表提示一样,以及排他模式锁 SCH-M,它是通过更改表上的截断或分区切换而获得的。
READ UNCOMMITTED/NOLOCK 将读取 TABLOCKX,但不会读取 SCH-M 锁。