SQLSERVER 的 nolock 到底是怎样的无锁?
一:背景
1. 讲故事
相信绝大部分用 SQLSERVER 作为底层存储的程序员都知道 nolock
关键词,即使当时不知道也会在踩过若干阻塞坑
之后果断的加上 nolock
,但这玩意有什么注意事项呢?这就需要了解它的底层原理了。
二:nolock 的原理
1. sql 阻塞还原
为了方便讲述,先创建一个 post 表,插个 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
从图中可以看到,这个 select 已经阻塞 9 分钟了,那为什么会被阻塞呢? 可以观察 SQLSERVER 内部的统计信息,比如锁相关的动态视图 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()
从图中看,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 1
BEGIN TRAN
TRUNCATE TABLE post;
----- session 2
SELECT * FROM post(NOLOCK) WHERE id=4
可以发现 nolock 查询也被阻塞了,原因就在于拿不到 post 表的 Sch-S
锁,因为 TRUNCATE
已经给 post 附加了 Sch-M
架构修改锁,那有没有数据支撑呢? 继续用动态视图 sys.dm_tran_locks
观察便可。
三:总结
综上所述,nolock 也仅在 page 级别上畅通无阻,在某些情况下也会有阻塞情况的发生,由于无锁自然就会读到别的会话已修改但还未提交的记录,sqlserver 作为一个数据库应用程序,里面包含了大量的运行时统计信息,这些统计信息可以用 系统视图
和 动态视图
获取,完全可以基于它们做一个完善的 APM 监控。
SQLSERVER 的 nolock 到底是怎样的无锁?的更多相关文章
- paip.提升性能----java 无锁结构(CAS, Atomic, Threadlocal, volatile, 函数式编码, 不变对象)
paip.提升性能----java 无锁结构(CAS, Atomic, Threadlocal, volatile, 函数式编码, 不变对象) 1 锁的缺点 2 CAS(Compare ...
- java 多线程12 : 无锁 实现CAS原子性操作----原子类
由于java 多线程11:volatile关键字该文讲道可以使用不带锁的情况也就是无锁使变量变成可见,这里就理解下如何在无锁的情况对线程变量进行CAS原子性及可见性操作 我们知道,在并发的环境下,要实 ...
- Erlang运行时中的无锁队列及其在异步线程中的应用
本文首先介绍 Erlang 运行时中需要使用无锁队列的场合,然后介绍无锁队列的基本原理及会遇到的问题,接下来介绍 Erlang 运行时中如何通过“线程进度”机制解决无锁队列的问题,并介绍 Erlang ...
- (转载)java高并发:CAS无锁原理及广泛应用
java高并发:CAS无锁原理及广泛应用 版权声明:本文为博主原创文章,未经博主允许不得转载,转载请注明出处. 博主博客地址是 http://blog.csdn.net/liubenlong007 ...
- 【DPDK】【ring】从DPDK的ring来看无锁队列的实现
[前言] 队列是众多数据结构中最常见的一种之一.曾经有人和我说过这么一句话,叫做“程序等于数据结构+算法”.因此在设计模块.写代码时,队列常常作为一个很常见的结构出现在模块设计中.DPDK不仅是一个加 ...
- 如何在高并发环境下设计出无锁的数据库操作(Java版本)
一个在线2k的游戏,每秒钟并发都吓死人.传统的hibernate直接插库基本上是不可行的.我就一步步推导出一个无锁的数据库操作. 1. 并发中如何无锁. 一个很简单的思路,把并发转化成为单线程.Jav ...
- 【实战Java高并发程序设计6】挑战无锁算法:无锁的Vector实现
[实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...
- 线程安全的无锁RingBuffer的实现
这里的线程安全,是指一个读线程和一个写线程,读写两个线程是安全的,而不是说多个读线程和多个写线程是安全的.. 在程序设计中,我们有时会遇到这样的情况,一个线程将数据写到一个buffer中,另外一个线程 ...
- [转]透过 Linux 内核看无锁编程
非阻塞型同步 (Non-blocking Synchronization) 简介 如何正确有效的保护共享数据是编写并行程序必须面临的一个难题,通常的手段就是同步.同步可分为阻塞型同步(Blocking ...
- CAS原子锁 高效自旋无锁的正确用法
"atomic_lock.h" #pragma once #ifndef _atomic_lock_h_include_ #define _atomic_lock_h_includ ...
随机推荐
- 三、celery执行定时任务
三.Celery执行定时任务 设定时间让celery执行一个 定时任务,product_task.py from celery_task import send_email from datetime ...
- stm32h750移植lvgl
之前没做过ui,只用过lcd画几条线写点字,如果按键.菜单什么的全用线画也太麻烦了,所以需要一个ui库. 听说lvgl用的人很多,就打算裸机移植一下用用.本文移植的lvgl版本是lvgl6.2,也移植 ...
- springboot整合mybatis步骤以及错误集合
1.首先在springboot项目中的pomx文件引入官方的依赖 <groupId>org.mybatis.spring.boot</groupId> <artifact ...
- 开箱即用 yyg-cli(脚手架工具):快速创建 vue3 组件库和vue3 全家桶项目
1 yyg-cli 是什么 yyg-cli 是优雅哥开发的快速创建 vue3 项目的脚手架.在 npm 上发布了两个月,11月1日进行了大升级,发布 1.1.0 版本:支持创建 vue3 全家桶项目和 ...
- 前端常见loading动画
loading动画是前端页面加载时必不可少的元素,好看合适的加载动画会极大的提升用户体验与系统的交互效果.下面为大家提供几种简单的加载动画效果,如果帮助到你了请点赞评论. 1.无限循环的圆圈 < ...
- Sqlite 安装操作使用
一.什么是 SQLite 数据库 SQLite 是嵌入式SQL数据库引擎.与大多数其他 SQL 数据库不同,SQLite 没有单独的服务器进程.SQLite 直接读取和写入普通磁盘文件.具有多个表,索 ...
- Redis Cluster 数据分片
介绍 Redis Cluster Redis 集群是 Redis 提供的分布式数据库方案, 集群通过分片(sharding) 来进行数据共享, 并提供复制和故障转移功能. 节点 一个 Redis 集群 ...
- nacos的使用
一:下载nacos 打开github搜索nacos,选择历史版本,建议下载1.4版本的,较稳定 https://github.com/alibaba/nacos 二:下载完后解压文件,两种方式打开 1 ...
- 正则表达式之前戏、字符组、量词、特殊符号、贪婪与非贪婪匹配等,python正则模块之re
目录 正则表达式前戏 正则表达式之字符组 正则表达式之特殊符号 正则表达式之量词 贪婪匹配与非贪婪匹配 转义符 正则表达式实战建议 re模块 re模块补充说明 作业 正则表达式前戏 案例:京东注册手机 ...
- ST表优化区间gcd
ST表的使用需要所求区间答案具有可重复性(询问时需要用到两个区间重叠来覆盖询问区间) 此题要求gcd为x的区间个数 可以用ST表处理出所有区间的\(gcd\) \(O(nlogn)\) 将区间的左端点 ...