一:背景

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. Java开发学习(三十九)----SpringBoot整合mybatis

    一.回顾Spring整合Mybatis Spring 整合 Mybatis 需要定义很多配置类 SpringConfig 配置类 导入 JdbcConfig 配置类 导入 MybatisConfig ...

  2. ahk_more

    ;20:47 2022/5/8 #NoEnv #Warn #SingleInstance Force ;设工作目录为桌面 SetWorkingDir %A_Desktop% ;托盘提示必须放在热键前面 ...

  3. selenium 添加特殊配置(如不完整 希望各位大神评论告诉我)

    options 常用配置 #添加特殊配置 options=webdriver.ChromeOptions() #设置默认编码为utf-8,也就是中文 options.add_argument('lan ...

  4. 详解pyautogui模块

    一.安装 pip install pyautogui 或者 pip install -i  https://pypi.tuna.tsinghua.edu.cn/simple pyautogui 二.全 ...

  5. AK/SK加密认证

    AK/SK认证的实现 AK/SK概述 1.什么是AKSK ak/sk是一种身份认证方式,常用于系统间接口调用时的身份验证,其中ak为Access Key ID,sk为Secret Access Key ...

  6. mindxdl--common--utils.go

    // Copyright (c) 2021. Huawei Technologies Co., Ltd. All rights reserved.// Package common define co ...

  7. AR路由器如何配置Portal认证(二层网络)

    规格 适用于所有版本.所有形态的AR路由器. 说明: 4GE-2S.4ES2G-S.4ES2GP-S和9ES2单板不支持NAC功能. 组网需求 如图所示,某公司接待室需要部署一套身份认证系统,对接入网 ...

  8. 原来用 MySQL 也可以做全文检索

    我是风筝,公众号「古时的风筝」,专注于 Java技术 及周边生态. 文章会收录在 JavaNewBee 中,更有 Java 后端知识图谱,从小白到大牛要走的路都在里面. 有朋友聊到他们的系统中要接入全 ...

  9. vue 3.0 常用API 的介绍

    vue3.0 生命周期 写法一 和vue2.x 一致 区别在于(beforeUnmount.unmount)名称不一样 写法二 在setup 中使用, 需要引用 如: import { onBefor ...

  10. std C++11 生成随机数组

    #include <algorithm> #include <array> #include <iostream> #include <iterator> ...