第八节:数据库层次的锁机制详解和事务隔离级别

一. 基本概念

1.共享锁:(holdlock)

 (1). select的时候会自动加上共享锁,该条语句执行完,共享锁立即释放,与事务是否提交没有关系。

 (2). 显式通过添加(holdlock)来显式添加共享锁(比如给select语句显式添加共享锁),当在事务里的时候,需要事务结束,该共享锁才能释放。

 (3). 同一资源,共享锁和排它锁不能共存,意味着update之前必须等资源上的共享锁释放后才能进行。

 (4). 共享锁和共享锁可以共存在一个资源上,意味着同一个资源允许多个线程同时进行select。

2. 排它锁:(xlock)

 (1). update(或 insert 或 delete)的时候加自动加上排它锁,该条语句执行完,排它锁立即释放,如果有事务的话,需要事务提交,该排它锁才能释放。

 (2). 显式的通过添加(xlock)来显式的添加排它锁(比如给select语句显式添加排它锁),如果有事务的话,需要事务提交,该排它锁才能释放。

 (2). 同一资源,共享锁和排它锁不能共存,意味着update之前必须等资源上的共享锁释放后才能进行。

3. 更新锁:(updlock)

 (1). 更新锁只能显式的通过(updlock)来添加,当在事务里的时候,需要事务结束,该更新锁才能释放。

 (2). 共享锁和更新锁可以同时在同一个资源上,即加了更新锁,其他线程仍然可以进行select。

 (3). 更新锁和更新锁不能共存(同一时间同一资源上不能存在两个更新锁)。

 (4). 更新锁和排它锁不兼容

 (5). 利用更新锁来解决死锁问题,要比xlock性能高一些,因为加了updlock后,其他线程是可以进行select的。

4. 意向锁

 意向锁分为三种:意向共享 (IS)、意向排他 (IX) 和意向排他共享 (SIX)。 意向锁可以提高性能,因为数据库引擎仅在表级检查意向锁来确定事务是否可以安全地获取该表上的锁,而不需要检查表中的每行或每页上的锁以确定事务是否可以锁定整个表.

 T1:select * from table (xlock) where id=10

 T2:select * from table (tablock) 

分析:T1线程执行该语句时,会对该表id=10的这一行加排他锁,同时会对整个表加上意向排它锁(IX),当T2执行的时候,不需要逐条去检查资源,只需要看到该表已经存在【意向排它锁】,就直接等待。

PS: update table set xx=xx where id=1, 不光会对id=1的这条记录加排它锁,还会对整张表加意向排它锁。

5. 计划锁(Schema Locks)

 用jdbc向数据库发送了一条新的sql语句,数据库要先对之进行编译,在编译期间,也会加锁,称之为:计划锁。

 编译这条语句过程中,其它线程可以对表做任何操作(update、delete、加排他锁等等),但不能做DDL(比如alter table)操作。

6. 锁的颗粒:行锁、页锁、表锁

 (1). rowlock:行锁---对每一行加锁,然后释放。(对某行加共享锁)

 (2). paglock:页锁---1执行时,会先对第一页加锁,读完第一页后,释放锁,再对第二页加锁,依此类推。(对某页加共享锁)

  假设前10行记录恰好是一页(当然,一般不可能一页只有10行记录),那么T1执行到第一页查询时,并不会阻塞T2的更新。

 (3). tablock:表锁---对整个表加锁,然后释放。 (对整张表加共享锁)

注:

  1. 以上三种锁执行完该语句后即可释放,无须等待事务的提交,与事务是否提交没有关系。

  2. 以上三种锁划分的角度不同,都是共享锁,所以他们相互之间是可以共存的。

7. rowlock、paglock、tablock 和 holdlock的区别

 二者无非是划分的角度不同,其实都是共享锁,但在释放上有所不同

 tablock(rowlock、paglock):对表、行、页加共享锁,只要语句执行完,就释放,与事务是否提交没关系。

 holdlock:对表加共享锁,必须等着事务执行完,才能释放。

8. tablockx对表加排它锁,在有事务和没事务的时候的区别

 (1). 无事务的时候:其他线程无法对该表进行读和更新,除非加tablockx的语句执行完,才能进行。

 (2). 有事务的时候:必须整个事务执行了commit或rollback后才会释放该排他锁。

 xlock还可这么用:select * from table(xlock tablock) 效果等同于select * from table(tablockx)

9.各种锁的兼容关系

  

二. 实战测试

1. 测试共享锁和共享锁可以共存

 View Code

结论:

  默认加 或者 显式(holdlock)的方式加,都能共存。

2. 测试排它锁和排它锁不能共存

 View Code

结论:

  1. 关于排它锁,无论是显式(xlock)模式添加还是update默认加的模式,如果在事务里都需要事务提交才能释放。

  2. 默认与默认、显式与显式、默认与显式 这三种组合关系都不能共存,所以证明排它锁和排它锁之间不能共存。

  3. 注意:这里加排他锁,会在表层次上加上意向排它锁,与操作那条数据无关。

3. 测试共享锁和排它锁不能共存(显式和隐式) (两个结论未完成)

 View Code

结论:

  1.默认select共享锁先执行,未提交事务的情况下,默认update排它锁 和 显式排它锁(xlock)都能正常执行。,

  证明:默认共享锁语句执行完立即释放,与事务是否提交没有关系

  2.显式共享锁(holdlock),未提交事务的情况下,默认update排它锁 和 显式排它锁(xlock)都 不能 正常执行,

  证明:显式共享锁(holdlock)需要事务提交才能释放,同时也证明共享锁和排它锁不能共存。

  3.默认update排它锁先执行,未提交事务的情况下,默认select共享锁能执行,显式共享锁(holdlock)不能执行。

  证明:

  4.显式排它锁(xlock)先执行,未提交事务的情况下,默认select共享锁能执行,显式共享锁(holdlock)不能执行。

  证明:

4. 测试共享锁、更新锁、排它锁间的关系

 View Code

结论:

  1. 更新锁需要事务提交才能释放。

  2. 更新锁和更新锁不能共存。

  3. 更新锁和排它锁不能共存。

  4. 更新锁和共享锁可以共存。

5. 测试表锁和排它锁不能共存

 View Code

 结论:

  1. 先执行表锁,事务未提交的情况下,排它锁能正常进行。

  证明:表锁只要执行完该语句立即释放,与事务是否提交没有关系。

  2. 先执行默认排它锁,事务未提交的情况想,表锁不能运行。

  证明:默认的排它锁必须等待事务提交完才能释放,同时证明排它锁和表锁不能共存 (表锁在这里的特点和共享锁一样,实质表锁也就是个共享锁,只是划分的角度不同)

6. 测试行锁和排它锁不能共存

 View Code

结论:

  1. 先执行行锁,事务未提交的情况下,排它锁能正常进行。

  证明:行锁只要执行完该语句立即释放,与事务是否提交没有关系。

  2. 先执行默认排它锁,事务未提交的情况想,行锁不能运行。

  证明:默认的排它锁必须等待事务提交完才能释放,同时证明排它锁和行锁不能共存 (行锁在这里的特点和共享锁一样,实质表锁也就是个共享锁,只是划分的角度不同)。

7. 测试页锁和排它锁不能共存(与表锁、行锁类似,不单独测试)

 

三. 事务隔离级别

1. 四种错误

 (1). 脏读:第一个事务读取第二个事务正在更新的数据,如果第二个事务还没有更新完成,那么第一个事务读取的数据将是一半为更新过的,一半还没更新过的数据,这样的数据毫无意义。

 (2). 幻读:第一个事务读取一个结果集后,第二个事务,对这个结果集进行“增删”操作,然而第一个事务中再次对这个结果集进行查询时,数据发现丢失或新增。

 (3). 更新丢失:多个用户同时对一个数据资源进行更新,必定会产生被覆盖的数据,造成数据读写异常。

 (4). 不可重复读:如果一个用户在一个事务中多次读取一条数据,而另外一个用户则同时更新啦这条数据,造成第一个用户多次读取数据不一致。

2. 死锁

(1). 定义:相互等待对方释放资源,造成资源读写拥挤堵塞的情况,就被称为死锁现象,也叫做阻塞。如下面的例子:

1 begin tran
2     select * from OrderInfor(holdlock) where id='333'
3     waitfor  delay '0:0:8' --等待8秒执行下面的语句
4     update OrderInfor set userName='ypf1' where id='333'
5 commit tran

分析:线程T1 和 线程T2 同时执行该事务,假设线程T1先执行完select,线程T2随后执行完select,线程T1要执行update语句的时候,根据数据库策略需要将【共享锁】提升为【排它锁】才能执行,所以必须等线程T2上的【共享锁】释放,而线程T2需要事务提交完才能释放锁,同时T1的【共享锁】不释放导致T2要一直等待,这样造成了T1和T2相互等待的局面,就是死锁现象。

(2). 数据库的默认处理思路的逻辑:

  数据库并不会出现无限等待的情况,是因为数据库搜索引擎会定期检测这种状况,一旦发现有情况,立马【随机】选择一个事务作为牺牲品。牺牲的事务,将会回滚数据。有点像两个人在过独木桥,两个无脑的人都走在啦独木桥中间,如果不落水,必定要有一个人给退回来。这种相互等待的过程,是一种耗时耗资源的现象,所以能避则避。

(3). 手动控制锁级别:

 语法:set deadlock_priority <级别>

 死锁处理的优先级别为 low<normal<high,不指定的情况下默认为normal,牺牲品为随机。如果指定,牺牲品为级别低的。

 还可以使用数字来处理标识级别:-10到-5为low,-5为normal,-5到10为high,数越小,级别越低,越先牺牲,越先回滚。

(4). 案例测试

  事先准备: 使用【LockDemoDB】中的OrderInfor表进行测试, 事先插入一条测试数据,之后都使用该数据进行测试。

1 insert into OrderInfor values('333','ypf','去青岛','lmr','1')

  在两个窗口里(即两个线程)执行下面一段代码:

复制代码

 1 -- 线程1执行下面语句2 begin tran3 begin try4     set deadlock_priority  -95     select * from OrderInfor(holdlock) where id='333'6     waitfor  delay '0:0:8' --等待8秒执行下面的语句7     update OrderInfor set userName='ypf1' where id='333'8     commit tran9 end try
10 begin catch
11     rollback tran
12 end catch

复制代码

复制代码

 1 -- 线程2测试(下面语句单独开一个窗口进行测试)2 begin tran3 begin try4     set deadlock_priority  -85     select * from OrderInfor(holdlock) where id='333'6     waitfor  delay '0:0:8' --等待8秒执行下面的语句7     update OrderInfor set userName='ypf2' where id='333'8     commit tran9 end try
10 begin catch
11     rollback tran
12 end catch

复制代码

分析:线程1和线程2分别执行下面语句,产生死锁,由于线程1设置的级别  -9 < -8,所以线程1牺牲且回滚,最后是线程2执行的结果,userName为ypf2  .

 (5). 扩展补充

 A. 查看锁活动情况

1 select * from sys.dm_tran_locks

B. 查看事务活动情况

1 dbcc opentran

C.  设置锁的超时时间

1 set lock_timeout 4000

PS:

   发生死锁的时候,数据库引擎会自动检测死锁,解决问题,然而这样子是很被动,只能在发生死锁后,等待处理。然而我们也可以主动出击,设置锁超时时间,一旦资源被锁定阻塞,超过设置的锁定时间,阻塞语句自动取消,释放资源,报1222错误。

  好东西一般都具有两面性,调优的同时,也有他的不足之处,那就是一旦超过时间,语句取消,释放资源,但是当前报错事务,不会回滚,会造成数据错误,你需要在程序中捕获1222错误,用程序处理当前事务的逻辑,使数据正确。为0时,即为一旦发现资源锁定,立即报错,不在等待,当前事务不回滚,设置时间需谨慎处理后事啊,你hold不住的。
拓展杀死锁和进程

 View Code

 3. 事务隔离级别

  read uncommitted:这个隔离级别最低啦,可以读取到一个事务正在处理的数据,但事务还未提交,这种级别的读取叫做脏读。

  read committed:这个级别是默认选项,不能脏读,不能读取事务正在处理没有提交的数据,但能修改。

  repeatable read:不能读取事务正在处理的数据,也不能修改事务处理数据前的数据。

  snapshot:指定事务在开始的时候,就获得了已经提交数据的快照,因此当前事务只能看到事务开始之前对数据所做的修改。

  serializable:最高事务隔离级别,只能看到事务处理之前的数据。 

 事先准备: 使用【LockDemoDB】中的OrderInfor表进行测试, 事先插入一条测试数据,之后都使用该数据进行测试。

线程1执行下面代码:

1  begin tran 
2    update OrderInfor set userName='ypf1' where id='333'
3    waitfor  delay '0:0:8' --等待8秒执行下面的语句
4  rollback tran

 线程1执行后,开启一个新线程(在一个新窗口)马上执行下面代码:

情况1

 

1 --1. 设置允许脏读,能马上读出来数据
2 set tran isolation level read uncommitted
3 select * from OrderInfor where id='333' --读取的数据为正在修改的数据 ,即为脏读
4 
5 --8秒之后数据已经回滚,查出来的数据是回滚后的数据 ypf
6 waitfor  delay '0:0:8'  
7 select * from OrderInfor where id='333'  

 

情况2

 

1 --2. 设置不允许脏读,不能马上读出来数据(数据库默认就是这种模式)
2 set tran isolation level read committed
3 select * from OrderInfor where id='333' 
4 
5 
6 --可以修改(但也得等线程1执行完事务后),8s后显示的是 ypf2,而不是原回滚后的数据ypf
7 update OrderInfor set userName='ypf2' where id='333'   
8 waitfor  delay '0:0:8'  
9 select * from OrderInfor where id='333' 

 

其它三种暂不测试了,与此同样道理进行测试。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/438972.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第九节:基于MVC5+AutoFac+EF+Log4Net的基础结构搭建

一. 前言 从本节开始&#xff0c;将陆续的介绍几种框架搭建组合形式&#xff0c;分析每种搭建形式的优势和弊端&#xff0c;剖析搭建过程中涉及到的一些思想和技巧。 (一). 技术选型 1. DotNet框架&#xff1a;4.6 2. 数据库访问&#xff1a;EF 6.2 (CodeFrist模式) 3. IOC框架…

第十节:基于MVC5+Unity+EF+Log4Net的基础结构搭建

一. 前言 从本节开始&#xff0c;将陆续的介绍几种框架搭建组合形式&#xff0c;分析每种搭建形式的优势和弊端&#xff0c;剖析搭建过程中涉及到的一些思想和技巧。 (一). 技术选型 1. DotNet框架&#xff1a;4.6 2. 数据库访问&#xff1a;EF 6.2 (CodeFrist模式) 3. IOC框架…

a*算法的优缺点_K-近邻算法以及案例

什么是K-近邻算法(KNN)根据邻居判断类型。如果一个样本在特征空间中有K个最相似&#xff08;距离相近&#xff09;的样本大多数属于一个类别&#xff0c;则该样品也属于这个类别。如何求距离呢?非常简单,如图。a1,a2,a3代表样本a的特征值 b1,b2,b3代表b的样本值&#xff0c;根…

android 预约挂号代码_告别排队!用Python定时自动挂号和快捷查询化验报告

作者 | 阿文来源 | 程序人生(ID: coder_life)我什么要做这个事情去年单位体检查出问题来&#xff0c;经过穿刺手术确诊是个慢性肾脏病2期&#xff0c; IGA 肾病三期&#xff0c;可能大家对于这个病并不是很了解&#xff0c;但是另外一个词可能大家都听过&#xff0c;叫"尿…

灵动思绪EF(Entity FrameWork)

很久之前就想写这篇文章了&#xff0c;但是由于种种原因&#xff0c;没有将自己学习的EF知识整理成一片文章。今天我就用CodeFirst和ModelFirst两种方式的简单案例将自己学习的EF知识做个总结。 在讲解EF之前&#xff0c;我们先来看下ORM ORM全称&#xff1a;(Object-Relatio…

json qbytearray 串 转_JSON数据采集网关,json转Modbus RTU串IO口RS485转4~20mA边缘计算智能终端...

JSON数据采集网关边缘计算智能终端是一种能够将各种传感器仪表仪器设备的数据采集后按照JSON格式上传服务器的网关&#xff0c;可以实现云边协同。JSON(JavaScript Object Notation)是java中的数据格式。例如{“Energy”:”100”, “time”:”22:22:15”}这样的格式&#xff0c…

ABP入门系列(3)——领域层定义仓储并实现

一、先来介绍下仓储 仓储&#xff08;Repository&#xff09;&#xff1a; 仓储用来操作数据库进行数据存取。仓储接口在领域层定义&#xff0c;而仓储的实现类应该写在基础设施层。 在ABP中&#xff0c;仓储类要实现IRepository接口&#xff0c;接口定义了常用的增删改查以及…

XCIE-HUAWEI-PBR-MQC-引入形成的路由环路

XCIE-HUAWEI-PBR-MQC-引入形成的路由环路 首先来个测试 给你们选&#xff0c;答案选啥呢? 正确答案在结尾公布 正确答案是C 为什么呢&#xff1f; 首先&#xff0c;虽然ACL有一个齐总是拒绝的&#xff0c;但是呢&#xff0c;他两都是同一条路由 但是呢&#xff01;&#x…

ABP入门系列(5)——展现层实现增删改查

ABP入门系列目录——学习Abp框架之实操演练源码路径&#xff1a;Github-LearningMpaAbp 这一章节将通过完善Controller、View、ViewModel&#xff0c;来实现展现层的增删改查。最终实现效果如下图&#xff1a; 展现层最终效果 一、定义Controller ABP对ASP.NET MVC Controlle…

限制会话id服务端不共享_不懂 Zookeeper?看完不懂你打我

高并发分布式开发技术体系已然非常的庞大&#xff0c;从国内互联网企业使用情况&#xff0c;可发现RPC、Dubbo、ZK是最基础的技能要求。关于Zookeeper你是不是还停留在Dubbo注册中心的印象中呢&#xff1f;还有它的工作原理呢&#xff1f;经典应用场景呢&#xff1f;对前面三个…

防抖与节流方案_前端ajax优化解决方案

伴随着前端ajax的应用场景越来越多&#xff0c;那就免不了一个整合的ajax优化解决方案了&#xff0c;自己优化太麻烦&#xff1f;没事&#xff0c;有它帮你解决&#xff1a;hajax 与当下比较热门的请求库 axios 和原生的 fetch相比&#xff0c;hajax有什么一些什么内容和特点呢…

ABP入门系列(6)——定义导航菜单

ABP入门系列目录——学习Abp框架之实操演练源码路径&#xff1a;Github-LearningMpaAbp 完成了增删改查以及页面展示&#xff0c;这一节我们来为任务清单添加【导航菜单】。 在以往的项目中&#xff0c;大家可能会手动在layout页面中添加一个a标签来新增导航菜单&#xff0c;这…

ABP入门系列(7)——分页实现

ABP入门系列目录——学习Abp框架之实操演练源码路径&#xff1a;Github-LearningMpaAbp 完成了任务清单的增删改查&#xff0c;咱们来讲一讲必不可少的的分页功能。 首先很庆幸ABP已经帮我们封装了分页实现&#xff0c;实在是贴心啊。 来来来&#xff0c;这一节咱们就来捋一捋如…

下载matlab安装包太慢_Matlab2017a软件安装包以及安装教程

安装步骤&#xff1a;1.如图所示&#xff0c;完整的安装包应该有13个压缩包&#xff0c;必须要全部下载完成才能解压。鼠标右击“thMWoMaR17a.part01.rar”压缩包&#xff0c;选择“解压到thMWoMaR17a”&#xff0c;然后等待解压完成2.打开“thMWoMaR17a”文件夹&#xff0c;解…

【转】ORM系列之Entity FrameWork详解

一. 谈情怀 从第一次接触开发到现在&#xff08;2018年&#xff09;&#xff0c;大约有六年时间了&#xff0c;最初阶段连接数据库&#xff0c;使用的是【SQL语句ADO.NET】&#xff0c;那时候&#xff0c;什么存储过程、什么事务 统统不理解&#xff0c;生硬的将SQL语句传入SQL…

springcloud 微服务鉴权_Java微服务框架spring cloud

Spring Cloud是什么Spring Boot 让我们从繁琐的配置文件中解脱了出来&#xff0c;而 Spring Cloud&#xff0c;它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发&#xff0c;如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等&#xff0c;…

ABP入门系列(9)——权限管理

1.引言 完成了简单的增删改查和分页功能&#xff0c;是不是觉得少了点什么&#xff1f; 是的&#xff0c;少了权限管理。既然涉及到了权限&#xff0c;那我们就细化下任务清单的功能点&#xff1a; 登录的用户才能查看任务清单用户可以无限创建任务并分配给自己&#xff0c;但…

c#quartz触发_SpringBoot集成Quartz实现定时任务

1 需求在我的前后端分离的实验室管理项目中&#xff0c;有一个功能是学生状态统计。我的设计是按天统计每种状态的比例。为了便于计算&#xff0c;在每天0点&#xff0c;系统需要将学生的状态重置&#xff0c;并插入一条数据作为一天的开始状态。另外&#xff0c;考虑到学生的请…

ABP入门系列(10)——扩展AbpSession

一、AbpSession是Session吗&#xff1f; 1、首先来看看它们分别对应的类型是什么&#xff1f; 查看源码发现Session是定义在Controller中的类型为HttpSessionStateBase的属性。 public HttpSessionStateBase Session { get; set; } 再来看看AbpSession是何须类也&#xff0c…