锁定是 SQL Server 数据库引擎用来同步多个用户同时对同一个数据块的访问的一种机制。

  1. 基本概念
  2. 利用SQL Server Profiler观察锁
  3. 死锁产生的原因及避免
  4. 总结

基本概念

数据库引擎隔离级别

隔离级别 定义
未提交的读取 隔离事务的最低级别,只能保证不读取物理上损坏的数据。 在此级别上,允许脏读,因此一个事务可能看见其他事务所做的尚未提交的更改
已提交的读取 允许事务读取另一个事务以前读取(未修改)的数据,而不必等待第一个事务完成。 SQL Server 数据库引擎保留写锁(在所选数据上获取)直到事务结束,但是一执行 SELECT 操作就释放读锁。 这是SQL Server 数据库引擎默认级别
可重复的读取 SQL Server 数据库引擎保留在所选数据上获取的读锁和写锁,直到事务结束。 但是,因为不管理范围锁,可能发生虚拟读取
可序列化 隔离事务的最高级别,事务之间完全隔离。 SQL Server 数据库引擎保留在所选数据上获取的读锁和写锁,在事务结束时释放它们。 SELECT 操作使用分范围的 WHERE 子句时获取范围锁,主要为了避免虚拟读取
 

锁粒度

资源 说明
RID 用于锁定堆中的单个行的行标识符,也就是常说的行锁
KEY 索引中用于保护可序列化事务中的键范围的行锁
PAGE 数据库中的 8 KB 页,例如数据页或索引页,也就常说的业级锁
EXTENT 一组连续的八页,例如数据页或索引页
HoBT 堆或 B 树。 用于保护没有聚集索引的表中的 B 树(索引)或堆数据页的锁
TABLE 包括所有数据和索引的整个表
FILE 数据库文件
APPLICATION 应用程序专用的资源
METADATA 元数据锁
ALLOCATION_UNIT 分配单元
DATABASE 整个数据库
 

锁类型

说明
共享 (S) 用于不更改或不更新数据的读取操作,如 SELECT 语句
更新 (U) 用于可更新的资源中。 防止当多个会话在读取、锁定以及随后可能进行的资源更新时发生常见形式的死锁
排他 (X) 用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。 确保不会同时对同一资源进行多重更新
意向 (I) 用于建立锁的层次结构。 意向锁包含三种类型:意向共享 (IS)、意向排他 (IX) 和意向排他共享 (SIX)
架构 (Sch-) 在执行依赖于表架构的操作时使用。 架构锁包含两种类型:架构修改 (Sch-M) 和架构稳定性 (Sch-S)
大容量更新 (BU) 在将数据大容量复制到表中且指定了 TABLOCK 提示时使用
键范围 (Range) 当使用可序列化事务隔离级别时保护查询读取的行的范围。 确保再次运行查询时其他事务无法插入符合可序列化事务的查询的行
 

利用SQL Server Profiler观察锁

1. 准备数据10条数据

create table DataTable(Id int identity(,), [Name] varchar(), [Address] varchar(), CreateTime datetime2)

insert into DataTable
select 'Wilson','广东省广州市',GETDATE() union all
select 'Alice','北京市朝阳区',GETDATE() union all
select 'Miksovsky','吉林省松原市',GETDATE() union all
select 'Hines','甘肃省兰州市',GETDATE() union all
select 'Kane','辽宁省沈阳市',GETDATE() union all
select 'Gode','湖北省荆州市',GETDATE() union all
select 'Chen','湖南省岳阳市',GETDATE() union all
select 'Trenary','福建省厦门市',GETDATE() union all
select 'Achong','广西省玉林市',GETDATE() union all
select 'Nixon','江西省景德镇',GETDATE()

2. 打开SQL Server Profiler选中锁事件,勾选type和mode,建议取消不需要观察的列,然后用列筛选器过滤要观察的DB

3. 查询数据

可以看到在页面级别加上意向共享锁,因为我们数据只有一页

4. 更新一条数据

1. 表上加上意向排它锁(IX),可以用select OBJECT_NAME(581577110) 查看objectid代表的东西

2. 页级别加上意向更新锁(IU),告诉SQL Server引擎这里有更新锁

3. 获取第一行的更新锁(U),这里条件匹配

4. 页级别升级为意向排他锁(IX), 告诉SQL Server引擎这里有排他锁

5. 第一个行更新锁 升级为排它锁(X)

6. 释放锁

7. 随条扫描后面的记录,只是条件不符合,也就不会升级锁级别

可以看到是全表扫描,因为没聚集索引(堆表),我们也没做一个主键,下面将Id添加主键然后再更新试试

alter table DataTable add constraint PK_DataTable primary key(Id asc)

可以看出,直接在表,页级别加上意向排它锁(IX),然后在键上加上排它锁(X)

因为这里我们用主键更新,而且SQL Server主键默认是聚集索引,如果指定是非聚集索引主键,这里也会经历更新锁 到 排他锁,有兴趣的可以自行验证

5. 删除一条数据

这次我们没用主键删除,过程和更新的第一种情况差不多,就不列了。

因为加了聚集索引,索引定位器执行聚集索引Key的hash,要验证是否那条记录,可以在删除前加上%%lockres%%去查

死锁产生的原因及避免

死锁产生的原因

微软文档是这样说

在两个或多个任务中,如果每个任务锁定了其他任务试图锁定的资源,此时会造成这些任务永久阻塞,从而出现死锁

我理解就是有2个事务循环依赖对方的资源导致产生死锁。

例如

1. 事务A 获取 Row1 资源

2. 事务B 获取 Row2 资源

3. 事务A获取Row2资源,由于这时Row2是被事务B占有,所以必须等事务B完成

4. 事务B获取Row1资源,由于这时Row1是被事务A占有,所以必须等事务A完成

SQL Server处理死锁策略

1. 定期检查陷入死锁的任务

2. 若检查到循环依赖

3. 选择其中一个作为牺牲品,然后终止事务,然另外一个得以完成

模拟死锁

分别在两个不同的会话执行下面语句

begin tran;

update DataTable set Address = '上海市' where Id = 2;
--延迟5秒执行
WAITFOR DELAY '00:00:05';
update DataTable set Address = '上海市' where Id = 3; commit;
begin tran;

update DataTable set Address = '上海市' where Id = 3;
--延迟5秒执行
WAITFOR DELAY '00:00:05';
update DataTable set Address = '上海市' where Id = 2; commit;

执行一段时间,其中一个会出现下面错误

SQL Server Profiler 捕获死锁分析

打开Locks事件的死锁图形

重新执行上面语句,模拟死锁,Profiler捕获到死锁

可以看出

1. 进程56 请求的Key 的排它锁  被进程 54 占有

2. 进程54 请求的Key 的排他锁 被进程 56 占有

3. 形成了循环依赖

我们这里的Sql比较简单,而且没有用参数化执行,所以我们指定是哪一行被锁,线上的通常不能直接看到哪一行被锁

我们可以通过xml查看等待的资源,在xml里面有process-list 下面有多个process,process节点上面有个waitresource属性,这个指出每个进程等待的资源

锁类型:db_id : hobt_id : (hashvalue)

KEY: 6:72057594043760640 (61a06abd401c)

通过%%lockres%% 查到被锁资源

select %%lockres%%,* from DataTable where %%lockres%% = '(98ec012aa510)'

锁类型不一样,得到的会不一样,根据各自的格式用db_name / object_name  / dbcc去查到当前被锁的资源,有时候需要利用DBCC查询Page存储页面,可以参考上一篇文章【SQL SERVER】数据内部存储结构简单探索

避免死锁

首先需要说明死锁不能完全避免,但遵守特定的编码惯例可以将发生死锁的机会降至最低

1. 按同一顺序访问对象,一个获取锁,另外一个就必须等待

2. 避免事务中的用户交互 ,这样导致事务时间过长,容易造成死锁

3. 保持事务简短并处于一个批处理中,道理和2一样,尽量让事务运行时间短。

4. 使用较低的隔离级别,这个看能不能接受脏读,幻读等副作用

总结

1. 锁机制保证并发情况下的数据访问。

2. 开发中应该尽量利用索引检索数据,特别是UPDATE/DELETE这种需要排它锁,应该利用唯一聚集索引字段更新(通常是主键)

3. 规范使用事务能减少死锁发生

转发请标明出处:https://www.cnblogs.com/WilsonPan/p/12618849.html

【SQL SERVER】锁机制的更多相关文章

  1. SQL Server 锁机制 悲观锁 乐观锁 实测解析

    先引入一些概念,直接Copy其他Blogs中的,我就不单独写了. 一.为什么会有锁 多个用户同时对数据库的并发操作时会带来以下数据不一致的问题: 1.丢失更新 A,B两个用户读同一数据并进行修改,其中 ...

  2. SQL Server 锁机制

    锁兼容性图: 一.锁的粒度: 比较需要注意的是RID/KEY.HoBT/PAGE这两对儿的区别,RID和HoBT是针对堆表的,即没有聚集索引的表. 二.锁的模式: 1.关于其中的S.U.X锁: 共享锁 ...

  3. 简单理解SQL Server锁机制

    多个用户同时对数据库的并发操作时,可能会遇到下面几种情况,导致数据前后不一致: 1,A.B事务同时对同一个数据进行修改,后提交的人的修改结果会破坏先提交的(丢失更新): 2,事务A修改某一条数据还未提 ...

  4. Sql server锁机制

    如何查看锁 了解SQL Server在某一时间点上的加锁情况无疑是学习锁和诊断数据库死锁和性能的有效手段.我们最常用的查看数据库锁的手段不外乎两种: 使用sys.dm_tran_locks这个DMV ...

  5. [转]了解SQL Server锁争用:NOLOCK 和 ROWLOCK 的秘密_Mr_Indigo的空间

    了解SQL Server锁争用:NOLOCK 和 ROWLOCK 的秘密 关系型数据库,如SQL Server,使用锁来避免多用户修改数据时的并发冲突.当一组数据被某个用户锁定时,除非第一个用户结束修 ...

  6. 了解SQL Server锁争用:NOLOCK 和 ROWLOCK 的秘密

    关系型数据库,如SQL Server,使用锁来避免多用户修改数据时的并发冲突.当一组数据被某个用户锁定时,除非第一个用户结束修改并释放锁,否则其他用户就无法修改该组数据. 有些数据库,包括SQL Se ...

  7. 转:sql server锁知识及锁应用

    sql server锁(lock)知识及锁应用 提示:这里所摘抄的关于锁的知识有的是不同sql server版本的,对应于特定版本时会有问题. 一 关于锁的基础知识 (一). 为什么要引入锁 当多个用 ...

  8. SQL SERVER锁(LOCK)知识及锁应用

    提示:这里所摘抄的关于锁的知识有的是不同sql server版本的,对应于特定版本时会有问题. 一 关于锁的基础知识 (一). 为什么要引入锁 当多个用户同时对数据库的并发操作时会带来以下数据不一致的 ...

  9. sql server 锁与事务拨云见日(中)

    一.事务的概述 上一章节里,重点讲到了锁,以及锁与事务的关系.离上篇发布时间好几天了,每天利用一点空闲时间还真是要坚持.听<明朝那些事儿>中讲到"人与人最小的差距是聪明,人与人最 ...

  10. sql server 锁与事务拨云见日(上)

    一.概述 讲到sql server锁管理时,感觉它是一个大话题,因为它不但重要而且涉及的知识点很多,重点在于要掌握高并发要先要掌握锁与事务,涉及的知识点多它包括各式各样的锁,锁的组合,锁的排斥,锁延伸 ...

随机推荐

  1. DataGirdView

    DataGridView知识点 简单示例 (1)代码 SqlDataAdapter da; DataSet ds; string sql ="select 列名 from 表名": ...

  2. C#开发BIMFACE系列36 服务端API之:回调机制

    系列目录     [已更新最新开发文章,点击查看详细] 在<C# 开发 BIMFACE 系列文章>中介绍了模型转换.模型对比接口.这2个功能接口比较特殊,发起请求后,逻辑处理是在BIMFA ...

  3. Docker实战之Kafka集群

    1. 概述 Apache Kafka 是一个快速.可扩展的.高吞吐.可容错的分布式发布订阅消息系统.其具有高吞吐量.内置分区.支持数据副本和容错的特性,适合在大规模消息处理场景中使用. 笔者之前在物联 ...

  4. MySQL占用CPU超过百分之100解决过程

    本文转载自: https://www.93bok.com 访问网页504 Gateway Time-out,登陆服务器查看,内存正常,CPU使用率达到了400%,因为是4核,所以到了400%,几乎全部 ...

  5. 7-11 jmu-python-分段函数&数学函数 (15 分)

    本题要求计算下列分段函数f(x)的值(x为从键盘输入的一个任意实数): 输入格式: 直接输入一个实数x 输出格式: 在一行中按“f(x)=result”的格式输出,其中x与result都保留三位小数. ...

  6. HBuilder-X 关闭eslint-vue 插件语法检查

    HBuilder-X  在写vue项目的时候发现,代码在保存的时候回自动检查eslint语法,会报一大堆的红色警告! 这时候就很烦人,看着不爽,看了下eslint 配置里面介绍了关闭语法检查的配置-- ...

  7. 自己查与写的批量比较bash

    前言:互测的时候一个一个输入感觉太麻烦,于是尝试写自己的对拍,又想到os刚学了bash命令行处理,于是想把两者结合一下减轻自己的工作量 分两步: 将所有人的工程导出成jar文件 放到linux下用ba ...

  8. centos7搭建ceph集群

    一.服务器规划 主机名 主机IP 磁盘配比 角色 node1 public-ip:10.0.0.130cluster-ip:192.168.2.130 sda,sdb,sdcsda是系统盘,另外两块数 ...

  9. safari坑之 回弹

    博客地址: https://www.seyana.life/post/20 今天在使用safari浏览博客的时候, 发现在拉至顶部并产生回弹之后,头部导航隐藏了, 除非在上拉的时候,刚好达到顶部而不超 ...

  10. IOS7下,alertView不能使用addSubview添加textField解决办法

    UIAlertView *alertView1 = [[UIAlertView alloc] initWithTitle:@"Enter Form Name" message:@& ...