?一:背景1. 讲故事相信绝大部分用 SQLSERVER 作为底层存储的程序员都知道 nolock? 关键词,即使当时不知道也会在踩过若干阻塞坑?之后果断的加上 nolock,但这玩意有什么注意事项呢?这就需要了解它的底层原理了 。
二:nolock 的原理1. sql 阻塞还原为了方便讲述,先创建一个 post 表,插个 6 条记录,参考代码如下:
CREATE TABLE post(id INT IDENTITY,content char(4000))GOINSERT INTO dbo.post VALUES('aaa')INSERT INTO dbo.post VALUES('bbb')INSERT INTO dbo.post VALUES('ccc');INSERT INTO dbo.post VALUES('ddd');INSERT INTO dbo.post VALUES('eee');INSERT INTO dbo.post VALUES('fff');
这里为了简单我没有创建索引,所以会出现 Table Scan? 的情况,毕竟生产环境下的sql也避免不了 Table Scan? 和 Clustered Index Scan? 的存在,接下来还原下阻塞场景,开启两个 session 会话, session1 为正在运行的 update? 事务, session2 为一个简单的 select 操作,这种场景下会导致 session2 阻塞,参考代码如下:
- session1
BEGIN TRANUPDATE post SET content='xxxxx' WHERE id=3
- session2
SELECT * FROM post WHERE id=4
文章插图
从图中可以看到,这个 select 已经阻塞 9 分钟了,那为什么会被阻塞呢?可以观察 SQLSERVER 内部的统计信息,比如锁相关的动态视图 sys.dm_tran_locks ,参考代码如下:
SELECT t.request_session_id,CASEWHEN t.resource_type = 'OBJECT' THENOBJECT_NAME(t.resource_associated_entity_id)WHEN t.resource_associated_entity_id = 0 THEN'/'ELSEOBJECT_NAME(p.object_id)END AS resource_name,index_id,t.resource_type,t.resource_description AS description,t.request_mode AS mode,t.request_status AS statusFROM sys.dm_tran_locks AS tLEFT JOIN sys.partitions AS pON p.hobt_id = t.resource_associated_entity_idWHERE t.resource_database_id = DB_ID()
文章插图
从图中看,session55 准备在 1:489:0? 这个槽位指向的记录上附加 S 锁时被阻塞,因为 1:489:0 已经被附加了 X 锁,很显然这个 X 锁是 update 给的 。
上面给出的是一个 静态视图,为了方便显示动态视图,这里把 sql profile 开起来观察两个 session 给锁的过程,事件选择上如下所示:
文章插图
将 sqlprofile 开启后,重新运行下刚才的两个会话,观察 profile 的走势,截图如下:
文章插图
图中的注释已经说的非常清楚了,和 sys.dm_tran_locks? 显示的一致,有了这些基础后接下来观察下如果加上 with (nolock) 会怎么样?
SELECT * FROM post(NOLOCK) WHERE id=4
你会发现结果是可以出来的,那为什么可以出来呢?继续观察下 profile 即可 。文章插图
从 session 55 的 lock 输出来看,with(nolock)? 会对 post 表附加 Sch-S? 架构稳定锁,以及分区中的 堆或BTree 附加S锁, 而不再对 PAGE 附加任何锁了,所以就不存在阻塞的情况,但肯定会引起脏读 。
到这里基本上就是 nolock 的底层玩法了吧,不过也有一个注意点,nolock 真的不会引发阻塞吗? 接下来我们好好聊一聊 。
3. nolock 真的无视阻塞吗从 sqlprofile 观察锁的走势图来看,nolock 只是在上限为 page 页级别上做到无视,但在 page? 之上就无法做到了,比如你看到的 Sch-S?,可能有些朋友要问了,为什么要加上 Sch-S 锁呢?其实很简单,在 query 的过程中一定要保持架构稳定嘛,不能在 query 的过程中,post 表突然被删了,这样大家多尴尬 。
接下来也可以做个简单的测试 。
----- session 1BEGIN TRANTRUNCATE TABLE post;----- session 2SELECT * FROM post(NOLOCK) WHERE id=4
文章插图
可以发现 nolock 查询也被阻塞了,原因就在于拿不到 post 表的 Sch-S? 锁,因为 TRUNCATE? 已经给 post 附加了 Sch-M? 架构修改锁,那有没有数据支撑呢?继续用动态视图 sys.dm_tran_locks 观察便可 。
推荐阅读
- 关于 history 命令的几个使用技巧
- Linux的用户为何常年上不去?
- MySQL的ibtmp1文件怎么这么大?
- 八个常用的网络命令ping、nbtstat、tracert、Telnet等详细方法介绍,值得学习收藏!
- cpa广告是什么?聊一聊关于cpa广告的那些事!
- 学习Python,需要掌握的20个命令
- 《财富》封面:全球爆红的ChatGPT是如何诞生的?
- 香港乐坛三皇五帝的名字 三黄五帝夏商周,春秋战国乱悠悠,始皇统一国号秦,二世残暴西汉兴,汉有分为西与东,下一句?
- 制作人偶的少年歌词;羊毛戳针人偶的制作?
- 走的笔顺笔画顺序 奏字的笔顺