【问题标题】:Explain locking behavior in SQL Server解释 SQL Server 中的锁定行为
【发布时间】:2010-03-06 20:22:45
【问题描述】:

为什么,使用 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 第 1 页:12186
test2 PAGE 1:12186

【问题讨论】:

  • 您使用的是什么版本的 SQL Server?从 2000 年起,SQL Server 默认使用行级锁,而不是页锁。

标签: sql-server select locking


【解决方案1】:

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

read commited 可防止脏读,通过阻塞您将获得一致的结果,快照隔离解决了这个问题,但您的性能会稍差,因为现在 sql server 将在事务期间保存旧值(最好让您的 tempdb在良好的驱动器上)

顺便说一句,尝试从

更改查询
select * from test2

select * from test2 where id <> 1 

假设您的表格中有超过 1 行并且超过一页,请插入几千行

【讨论】:

  • +1 并且它是一个功能,而不是错误,在它们被提交之前跳过“脏”插入! :-)
【解决方案2】:

带有节点锁定的列表遍历是通过'crabbing'完成的:

  • 你有一个锁定当前节点
  • 你抓住下一个节点的锁
  • 您将下一个节点设为当前节点
  • 你释放了前一个节点(前当前节点)上的锁

这种技术在所有列表遍历算法中都很常见,旨在在遍历时保持稳定性:如果没有将自己锚定在锁中,您永远不会进行“飞跃”。它经常与攀岩者使用的技术进行比较。

SELECT ... FROM table; 这样的语句是对整个表的扫描。因此,它可以与列表遍历进行比较,并且执行表扫描遍历的线程将“抓取”行,就像执行列表遍历的线程将遍历节点一样。这种列表遍历保证它最终会尝试锁定列表中的每个节点,并且表扫描将类似地尝试在某个时间或另一个时间锁定表中的每一行.因此,行上另一个事务持有的任何冲突锁都将阻止扫描,100% 保证。您观察到的所有其他内容(页面锁、意图锁等)都是实现细节,与基本问题无关。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-29
    • 1970-01-01
    • 2022-01-11
    • 1970-01-01
    相关资源
    最近更新 更多