为什么带NOLOCK的查询语句还会造成阻塞
背景
客户反映HIS数据库在11点出现了长时间的阻塞,直到手动KILL掉阻塞的源头。请我们协助分析原因,最终定位到.NET程序中使用的SqlDataReader未正常关闭导致。
现象
登录SQL专家云,进入趋势分析,在活动会话中回溯11点一个小时内的运行情况,从11:28开始出现阻塞情况,一直持续到11:57。

转到活动会话原始数据,看到ID为418的会话是阻塞源头,阻塞了会话722,会话722又阻塞了其它大量的会话。

分析
首先分析阻塞的成因,会话418执行的是一个查询语句,在表上都加了NOLOCK查询提示,也就是说不加共享锁,不会阻塞其它会话对该表的INSERT、UPDATE、DELETE操作,但是还是要加架构共享锁的,而会话722执行的是TRUNCATE TABLE SYS_KSDMK,是要对表加架构排他锁的,产生互斥,从而造成阻塞。

接下来再分析为什么会造成这么长时间的阻塞,在不同时间点看会话418的运行情况,每个时间点的等待资源都是ASYNC_NETWORK_IO,CPU时间和逻辑读次数都非常小,而且一直不变。
ASYNC_NETWORK_IO从字面上有很强的迷惑性,第一反应是网络环境的问题,通过微软官方文档看到,客户端程序因为各种原因无法快速接收使用查询返回的数据也是很重要的原因。和客户进行确认,这个语句查询的都是字典表,数据量非常小,因此判断该查询语句在SQL Server端很快就执行完了,客户端程序在接收查询结果时出现异常。
根据推断,在客户端程序中果然找程序的错误日志。查看代码,程序用SqlDataReader逐行接收并处理查询结果,在数据类型转换时有报错。
为了更好的说明这个现象,特地写了一段演示代码。用SqlDataReader循环逐行接收查询返回的结果,当接收到ID>500的数据行时抛出异常。报错后程序无法运行到reader.Close()和connection.Close()来关闭SqlDataReader和SqlConnection。
1 private void Demo(string connectionString)
2 {
3 SqlConnection conn = new SqlConnection(connectionString);
4 conn.Open();
5 SqlCommand command = conn.CreateCommand();
6 command.CommandText = "SELECT id, a, b FROM [dbo].[Table_2] WITH(nolock) ORDER BY id";
7 SqlDataReader reader = command.ExecuteReader();
8 while (reader.Read())
9 {
10 int id = Convert.ToInt32(reader["id"]);
11 string a = reader["a"].ToString();
12 if (id > 500)
13 {
14 throw new Exception("故意抛出异常");
15 }
16 }
17 reader.Close();
18 conn.Close();
19 }
因为程序没有异常处理机制,该连接(会话)已经被泄露,所以该会话和请求会一直存在,状态如下图。其它会话对表进行TRUNCATE TABLE操作时就会被阻塞。

解决
要修改应用程序,增加程序的健壮性。可以在异常处理时检查并关闭SqlDataReader和SqlConnection,也可以利用using的特性自动关闭。
1 //利用using特性,即使有异常也可以关闭SqlDataReader和SqlConnection
2 private void Demo(string connectionString)
3 {
4 using (SqlConnection conn = new SqlConnection(connectionString))
5 {
6 conn.Open();
7 SqlCommand command = conn.CreateCommand();
8 command.CommandText = "SELECT id, a, b FROM [dbo].[Table_2] WITH(nolock) ORDER BY id";
9 using (SqlDataReader reader = command.ExecuteReader())
10 {
11 while (reader.Read())
12 {
13 int id = Convert.ToInt32(reader["id"]);
14 string a = reader["a"].ToString();
15 if (id > 500)
16 {
17 throw new Exception("故意抛出异常");
18 }
19 }
20 reader.Close();
21 }
22 conn.Close();
23 }
24 }
自动查杀会话
因为很多客户是购买软件厂商的产品,修改程序不容易实施。因此只能在数据库端进行补偿性的措施,例如配置一个定期运行的自动查杀会话的作业,根据这种会话的特征KILL掉。也可以在SQL专家云中启用自动查杀会话的功能。

北京格瑞趋势科技有限公司是聚焦于数据服务的高新技术企业,成立于2008年,创始团队及核心技术人员来自微软和雅虎。微软数据平台金牌合作伙伴,卫宁健康数据平台战略合作伙伴。通过产品+服务双轮驱动的业务模式,14年间累计服务4000+客户,覆盖互联网、市政、交通、电信、医疗、教育、电力、制造业等各个领域。
为什么带NOLOCK的查询语句还会造成阻塞的更多相关文章
- 深入学习MySQL 01 一条查询语句的执行过程
在学习SpringCloud的同时,也在深入学习MySq中,听着<mysql45讲>,看着<高性能MySQL>,本系列文章是本人学习过程的总结,水平有限,仅供参考,若有不对之处 ...
- EF5中 执行 sql语句使用Database.ExecuteSqlCommand 返回影响的行数 ; EF5执行sql查询语句 Database.SqlQuery 带返回值
一: 执行sql语句,返回受影响的行数 在mysql里面,如果没有影响,那么返回行数为 -1 ,sqlserver 里面 还没有测试过 using (var ctx = new MyDbConte ...
- 利用带关联子查询Update语句更新数据
Update是T-sql中再简单不过的语句了,update table set column=expression [where condition],我们都会用到.但update的用法不仅于此,真 ...
- 通过带参数的Sql语句来实现模糊查询(多条件查询)
#region 通过带参数的Sql语句来实现模糊查询(多条件查询) StringBuilder sb = new StringBuilder("select * from books&quo ...
- (转)经典SQL查询语句大全
(转)经典SQL查询语句大全 一.基础1.说明:创建数据库CREATE DATABASE database-name2.说明:删除数据库drop database dbname3.说明:备份sql s ...
- 经典SQL查询语句大全
一.基础1.说明:创建数据库CREATE DATABASE database-name2.说明:删除数据库drop database dbname3.说明:备份sql server--- 创建 备份数 ...
- T-SQL查询语句(二):嵌套查询
一个select...From...Where查询语句块可以嵌套在另一个select...From...Where查询块的Where子句中,称为嵌套查询.外层查询称为父查询,主查询.内层查询称为子查询 ...
- T_SQL查询语句(一): 单表查询
############################################ 查询语句--SELECT ########################################## ...
- ES 常用的查询语句介绍
elasticsearch定义了两种查询方式: 一.索引(index).type.document 相关语句 1.列出所有索引的状态 GET /_cat/indices?v health status ...
- mybatis查询语句的背后之参数解析
转载请注明出处... 一.前言 通过前面我们也知道,通过getMapper方式来进行查询,最后会通过mapperMehod类,对接口中传来的参数也会在这个类里面进行一个解析,随后就传到对应位置,与sq ...
随机推荐
- echarts设置暂无数据
场景描述 我们在项目中,很多时候都会使用echarts进行数据展示. 当没有数据的时候,echarts的展示就会特别的难看. 这个时候我们就会优化界面的显示,在echarts中展示暂无数据. 有很多中 ...
- SqlSugar的查询函数SqlFunc
用法 我们可以使用SqlFunc这个类调用Sql函数,用法如下: db.Queryable<Student>().Where(it => SqlFunc.ToLower(it.Nam ...
- 2.13 PE结构:实现PE代码段加密
代码加密功能的实现原理,首先通过创建一个新的.hack区段,并对该区段进行初始化,接着我们向此区段内写入一段具有动态解密功能的ShellCode汇编指令集,并将程序入口地址修正为ShellCode地址 ...
- C/C++ 类与构造析构等知识
简单定义类 #include <iostream> #include <string> using namespace std; class Student { public: ...
- Win32汇编:算数运算指令总结
整理复习汇编语言的知识点,以前在学习<Intel汇编语言程序设计 - 第五版>时没有很认真的整理笔记,主要因为当时是以学习理解为目的没有整理的很详细,这次是我第三次阅读此书,每一次阅读都会 ...
- Protobuf之proto基础语法
目录 1.说明 2.字段类型 3.字段规则 4.字段编号 5.注释 6.类型 6.1.message 6.2.service 7.枚举enum 8.保留字段 9.import 9.1.protoc指令 ...
- 2000元内最超值游戏处理器!锐龙5 7500F首发评测:轻松超频5.6GHz游戏追平i5-13600K
一.前言:首款不带核显的锐龙7000处理器 以往的桌面锐龙处理器,带核显型号的很少,而到了Zen4时代,此前已上市的锐龙7000系列处理器都集成了核显. 现在,AMD锐龙5 7500F来了,这是AMD ...
- Ubuntu+Minio对象存储+pm2进程管理
Minio是一个go编写的高性能对象存储服务,它兼容Amazon S3 API.无论是静态网站的托管,还是数据存储分析,亦或是数据的备份与恢复等多种场景下,都可以为我们提供解决方案. ubuntu安装 ...
- 记录一则因主机名讹误导致的RAC启动异常
1.故障现象 2.解决方案 环境:RHEL 7 + Oracle 19.5 RAC 1.故障现象 最近遇到客户的一套19c测试环境,在一次主机重启后发现集群无法启动,使用crsctl stat res ...
- Windows、MacOs上 gif 录像软件 LICEcap
LICEcap 官网:https://www.cockos.com/licecap/ 适用于 windows macos 我自己保持的一版,下载地址:https://files-cdn.cnblogs ...