一:背景

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 ScanClustered 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 到底是怎样的无锁?的更多相关文章

  1. paip.提升性能----java 无锁结构(CAS, Atomic, Threadlocal, volatile, 函数式编码, 不变对象)

    paip.提升性能----java 无锁结构(CAS, Atomic, Threadlocal, volatile, 函数式编码, 不变对象) 1     锁的缺点 2     CAS(Compare ...

  2. java 多线程12 : 无锁 实现CAS原子性操作----原子类

    由于java 多线程11:volatile关键字该文讲道可以使用不带锁的情况也就是无锁使变量变成可见,这里就理解下如何在无锁的情况对线程变量进行CAS原子性及可见性操作 我们知道,在并发的环境下,要实 ...

  3. Erlang运行时中的无锁队列及其在异步线程中的应用

    本文首先介绍 Erlang 运行时中需要使用无锁队列的场合,然后介绍无锁队列的基本原理及会遇到的问题,接下来介绍 Erlang 运行时中如何通过“线程进度”机制解决无锁队列的问题,并介绍 Erlang ...

  4. (转载)java高并发:CAS无锁原理及广泛应用

    java高并发:CAS无锁原理及广泛应用   版权声明:本文为博主原创文章,未经博主允许不得转载,转载请注明出处. 博主博客地址是 http://blog.csdn.net/liubenlong007 ...

  5. 【DPDK】【ring】从DPDK的ring来看无锁队列的实现

    [前言] 队列是众多数据结构中最常见的一种之一.曾经有人和我说过这么一句话,叫做“程序等于数据结构+算法”.因此在设计模块.写代码时,队列常常作为一个很常见的结构出现在模块设计中.DPDK不仅是一个加 ...

  6. 如何在高并发环境下设计出无锁的数据库操作(Java版本)

    一个在线2k的游戏,每秒钟并发都吓死人.传统的hibernate直接插库基本上是不可行的.我就一步步推导出一个无锁的数据库操作. 1. 并发中如何无锁. 一个很简单的思路,把并发转化成为单线程.Jav ...

  7. 【实战Java高并发程序设计6】挑战无锁算法:无锁的Vector实现

    [实战Java高并发程序设计 1]Java中的指针:Unsafe类 [实战Java高并发程序设计 2]无锁的对象引用:AtomicReference [实战Java高并发程序设计 3]带有时间戳的对象 ...

  8. 线程安全的无锁RingBuffer的实现

    这里的线程安全,是指一个读线程和一个写线程,读写两个线程是安全的,而不是说多个读线程和多个写线程是安全的.. 在程序设计中,我们有时会遇到这样的情况,一个线程将数据写到一个buffer中,另外一个线程 ...

  9. [转]透过 Linux 内核看无锁编程

    非阻塞型同步 (Non-blocking Synchronization) 简介 如何正确有效的保护共享数据是编写并行程序必须面临的一个难题,通常的手段就是同步.同步可分为阻塞型同步(Blocking ...

  10. CAS原子锁 高效自旋无锁的正确用法

    "atomic_lock.h" #pragma once #ifndef _atomic_lock_h_include_ #define _atomic_lock_h_includ ...

随机推荐

  1. 虚拟机里网络连接的几种方式说明(桥接,NAT, 仅主机)

    虚拟机里网络连接类型的选择: 桥接:选择桥接模式的话虚拟机和宿主机在网络上就是平级的关系,相当于连接在同一交换机上. NAT:NAT模式就是虚拟机要联网得先通过宿主机才能和外面进行通信. 仅主机:虚拟 ...

  2. 第2-3-1章 文件存储服务系统-nginx/fastDFS/minio/阿里云oss/七牛云oss

    目录 文件存储服务 1. 需求背景 2. 核心功能 3. 存储策略 3.1 本地存储 3.2 FastDFS存储 3.3 云存储 3.4 minio 4. 技术设计 文件存储服务 全套代码及资料全部完 ...

  3. 嵌入式-C语言基础:理解形参和实参的区别

    #include<stdio.h> //实参:函数原型中声明函数后面带的参数 int test(int x)//函数原型 { //函数体 printf("test里面的x地址=% ...

  4. 基于python的数学建模---差分方程

    一.递推关系--酵母菌生长模型 代码: import matplotlib.pyplot as plt time = [i for i in range(0,19)] number = [9.6,18 ...

  5. 用最少的代码模拟gRPC四种消息交换模式

    我们知道,建立在HTTP2/3之上的gRPC具有四种基本的通信模式或者消息交换模式(MEP: Message Exchange Pattern),即Unary.Server Stream.Client ...

  6. layui的switch监听事件无用

    像layui的这种表单事件是属于表单,而不是表格,所以操作是layui.form.on监听事件,而不是用layui.table.on table = layui.table , form = layu ...

  7. dp状态设计

    迎接仪式 题目描述 LHX 教主要来 X 市指导 OI 学习工作了.为了迎接教主,在一条道路旁,一群"Orz 教主 er"穿着文化衫站在道路两旁迎接教主,每件文化衫上都印着大字.一 ...

  8. 在 win11 下搭建并使用 ubuntu 子系统(同时测试 win10)——(附带深度学习环境搭建)

    对于一个深度学习从事者来说,Windows训练模型有着诸多不便,还好现在Windows的Ubuntu子系统逐渐完善,近期由于工作需求,配置了Windows的工作站,为了方便起见,搭建了Ubuntu子系 ...

  9. 【SQL真题】SQL1: 各个视频的平均完播率 【AVG/SUM/IF/CASE】

    题目: https://www.nowcoder.com/practice/96263162f69a48df9d84a93c71045753?tpId=268&tqId=2285032& ...

  10. 多进程TCP服务端并发- 进程join方法 - IPC机制

    目录 同步与异步 阻塞与非阻塞 综合使用 创建进程的多种方式 进程间数据隔离 进程join方法 IPC机制 生产者消费者模型 进程对象的多种方法 僵尸进程与孤儿进程 守护进程 多线程实现TCP服务端并 ...