一:背景
1. 讲故事
nolock 关键词,即使当时不知道也会在踩过若干阻塞坑
之后果断的加上 nolock
,但这玩意有什么注意事项呢?这就需要了解它的底层原理了。
二:nolock 的原理
1. sql 阻塞还原
6 条记录,参考代码如下:
CREATE TABLE post(id INT IDENTITY,content char(4000
GO
INSERT 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 TRAN
UPDATE post SET content='xxxxx' WHERE id=3
- session2
SELECT * FROM post WHERE id=4
sys.dm_tran_locks,参考代码如下:
SELECT t.request_session_id,
CASE
WHEN t.resource_type = 'OBJECT' THEN
OBJECT_NAME(t.resource_associated_entity_id
WHEN t.resource_associated_entity_id = 0 THEN
'/'
ELSE
OBJECT_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 status
FROM sys.dm_tran_locks AS t
LEFT JOIN sys.partitions AS p
ON p.hobt_id = t.resource_associated_entity_id
WHERE t.resource_database_id = DB_ID(
1:489:0 这个槽位指向的记录上附加 S 锁时被阻塞,因为 1:489:0
已经被附加了 X 锁,很显然这个 X 锁是 update 给的。
sys.dm_tran_locks 显示的一致,有了这些基础后接下来观察下如果加上 with (nolock
会怎么样?
SELECT * FROM post(NOLOCK WHERE id=4
你会发现结果是可以出来的,那为什么可以出来呢?继续观察下 profile 即可。
with(nolock 会对 post 表附加 Sch-S
架构稳定锁,以及分区中的 堆或BTree
附加S锁,而不再对 PAGE 附加任何锁了,所以就不存在阻塞的情况,但肯定会引起脏读。
3. nolock 真的无视阻塞吗
从 sqlprofile 观察锁的走势图来看,nolock 只是在上限为 page 页级别上做到无视,但在 page
之上就无法做到了,比如你看到的 Sch-S
,可能有些朋友要问了,为什么要加上 Sch-S
锁呢? 其实很简单,在 query 的过程中一定要保持架构稳定嘛,不能在 query 的过程中,post 表突然被删了,这样大家多尴尬。
----- session 1
BEGIN TRAN
TRUNCATE TABLE post;
----- session 2
SELECT * FROM post(NOLOCK WHERE id=4
Sch-S 锁,因为 TRUNCATE
已经给 post 附加了 Sch-M
架构修改锁,那有没有数据支撑呢? 继续用动态视图 sys.dm_tran_locks
观察便可。
三:总结
系统视图 和 动态视图
获取,完全可以基于它们做一个完善的 APM 监控。