2010-03-06 74 views
2

,这是为什么,与SQL Server(所以事务隔离级别=提交读)默认设置,这个测试:解释在SQL Server的锁定行为

CREATE TABLE test2 (
ID bigint, 
name varchar(20) 
) 

然后在一个SSMS运行此标签:

begin transaction SH 
insert into test2(ID,name) values(1,'11') 
waitfor delay '00:00:30' 
commit transaction SH 

,这一次同时在另一个标签:

select * from test2 

需要第二个选择等待第一个完成之前返回??

我们也尝试了这些用于第二查询:

select * from test2 NOLOCK WHERE ID = 1 

,并试图在第一个查询插入一个ID和在所述第二选择不同的ID。

这是页面锁定的结果吗?当运行2个查询,我也跑了这一点:

select object_name(P.object_id) as TableName, resource_type, resource_description 
from 
sys.dm_tran_locks L join sys.partitions P on L.resource_associated_entity_id = p.hobt_id 

,并得到这个结果集:

test2的RID 1:12186:5
test2的RID 1:12186:5
TEST2 PAGE 1:12186
TEST2 PAGE 1:12186

+0

您使用的是什么版本的SQL Server?从2000年起,SQL Server默认使用行级锁 - 而不是页锁。 – 2010-03-06 21:25:14

回答

4

要求第二次选择等待 第一次完成之前 返回

读COMMITED防止脏读,并通过阻止你将得到一个一致的结果,快照隔离来解决这个,但你会得到表现略差,因为现在的SQL Server将持有旧值交易的持续时间(更你有一个很好的驱动器上的tempdb)

BTW,尝试从

select * from test2 

更改查询到

select * from test2 where id <> 1 

假设你在表中有超过1行,这将是在一个页面,插入几千行

+0

+1,这是一个**功能**,而不是一个错误,跳过“脏”的插入,直到他们已经承诺! :-) – 2010-03-06 21:14:07

3
与节点锁定

目录遍历由“捕蟹”来完成:

  • 你有一个锁定当前节点
  • 你抢锁定下一个节点
  • 您做出下一个节点当前
  • 放开前一个节点上的锁(前者电流)

这种技术在所有列表遍历算法中都很常见,并且意味着在遍历期间保持稳定性:您永远不会在没有锁定的情况下进行“跳跃”。它通常与攀岩者使用的技术相比较。

SELECT ... FROM table;这样的声明是对整个表格的扫描。因此,它可以与列表遍历进行比较,并且执行表扫描遍历的线程将对行进行“crabb”,就像执行列表遍历将在节点上执行一样。这样的列表遍历是保证,它将试图最终锁定列表中的每个单个节点,并且表扫描将类似地尝试锁定表中的每一行。因此,另一笔交易在行上持有的冲突锁将阻止扫描,100%保证。你观察到的其他一切(页面锁,意图锁等)都是实现细节,与基本问题无关。

此问题的正确解决方案是优化他们不扫描表端到端的查询。之后只有可以实现,您可以将注意力转移到消除所有剩余争用:部署基于row-level versionning的快照隔离。换句话说,启用数据库上的读取提交快照。