当前位置: 首页 > 编程日记 > 正文

【转修正】sql server行版本控制的隔离级别

在SQL Server标准的已提交读(READ COMMITTED)隔离级别下,一个读操作会和一个写操作相互阻塞。未提交读(READ UNCOMMITTED)虽然不会有这种阻塞,但是读操作可能会读到脏数据,这是大部分用户不能接受的。有些关系型数据库(例如Oracle)使用的是另一种处理方式。在任何一个修改之前,先对修改前的版本做一个复制[WX1] ,后续的一切读操作都会去读这个复制的版本,修改将创建一个新的版本。在这种处理方式下,读、写操作不会相互阻塞。使用这种行版本控制机制的好处,是程序的并发性比较高,但是缺点是用户读到的虽然不是一个脏数据,但是可能是个正在被修改马上就要过期的数据值[WX2] 。如果根据这个过期的值做数据修改,会产生逻辑错误。


[WX1]复制的内容保存在tempdb当中。

[WX2]假如读跟写同时进行,读到的不是现在正被修改的值,如果是读到正被修改的值那就是脏读了。读到的是修改前的值。但是这个值随时会过期。等到修改完就过期了。

有些用户可能为了更高的并发性而不在乎这种缺点,所以更喜欢Oracle的那种处理方法。为了满足这部分用户的需求,SQL Server 2005也引入了这种机制,来实现类似的功能。所以选取行版本控制隔离级别也可以成为消除阻塞和死锁的一种手段。

SQL Server有两种行版本控制,使用行版本控制的已提交读隔离(READ_COMMITTED_SNAPSHOT)和直接使用SNAPSHOT事务隔离级别。

  • READ_COMMITTED_SNAPSHOT数据库选项为ON时,READ_COMMITTED事务通过使用行版本控制提供语句级读取一致性。
  • ALLOW_SNAPSHOT_ISOLATION数据库选项为ON时,SNAPSHOT事务通过使用行版本控制提供事务级读取一致性。

下列示例可以说明使用普通已提交读事务,行版本控制的快照隔离事务和行版本控制的已提交读事务的行为差异。

数据库结构

手工创建数据库名称 HumanResources,执行以下脚本

CREATE TABLE [dbo].[Employee]([EmployeeID] [int] NULL,[VacationHours] [int] NULL,[SickLeaveHours] [int] NULL
) ON [PRIMARY]
GO
INSERT [dbo].[Employee] ([EmployeeID], [VacationHours], [SickLeaveHours]) VALUES (4, 48, 48)

实验1:Read Committed Isolation level

query1:事务1

--step1:开启第一个事务
BEGIN TRAN tran1--step2:执行select操作,查看VacationHours,对查找的记录加S锁SELECT EmployeeID, VacationHoursFROM Employee WHERE EmployeeID = 4;--step6: 在第一个事务中重新运行查询语句,发现查询被阻塞--这是因为在VacationHours上面有排他锁,现在要查询VacationHours字段又必须获得S锁,但是X锁与S锁冲突--所以不能执行查询,被阻塞.SELECT EmployeeID, VacationHoursFROM EmployeeWHERE EmployeeID = 4;--step8:因为会话2已经提交了事务,不再阻塞当前查询,因此返回会话2修改好的新数据:40--step9:回滚或者提交事务
ROLLBACK TRANSACTION;
commit tran tran1
GO

query2:事务2

--step3:开启第二个事务
BEGIN TRAN tran2;--step4:修改VacationHours,需要获得更新锁U,在VacationHours上有S锁,US不冲突,因此可以进行修改.--在修改VacationHours以后,更新锁U变成了排他锁XUPDATE Employee SET VacationHours = VacationHours - 8  WHERE EmployeeID = 4;-- step5:在当前事务中查询VacationHours,发现只有40小时SELECT VacationHoursFROM EmployeeWHERE EmployeeID = 4;--step7:回滚事务
rollback tran tran2
--commit tran tran2

总结:

  1. 事务1中的读操作没有阻塞事务2中的写操作
  2. 事务2中的更新操作阻塞了事务1中后来的读操作,如下图所示:
  3. 事务1两次查询得到的数据分别是48跟40,两次获得的数据内容不一样。所以也成read committed为不可重复读。在read committed隔离级别中,只在语句级别加锁,当语句执行完以后自动释放锁。比如事务1中的第一次查询,虽然查询在事务中进行,并且事务没有提交,但是此时查询语句执行完以后在table上就找不到锁了。

实验2:Snapshot Isolation

此示例中,在快照隔离下运行的事务将读取数据,然后由另一事务修改此数据。快照事务不会被其他事务执行的更新操作所阻塞,它忽略数据的修改继续从版本化的行读取数据。也就是说,读取到的是数据修改前的版本。但是,当快照事务尝试修改已由其他事务修改的数据时,它将生成错误并终止。

query1:事务1,快照事务

--step1:启用快照隔离
ALTER DATABASE HumanResourcesSET ALLOW_SNAPSHOT_ISOLATION ON;
GO--step2:设置使用快照隔离级别,前面没有设定是因为数据库默认的隔离界别就是Read Committed
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
GO--step3:开启第一个事务
BEGIN TRAN tran1--step4:执行select操作,查看VacationHours,对查找的记录加S锁SELECT EmployeeID, VacationHoursFROM Employee WHERE EmployeeID = 4;--step8:在事务2中修改了数据以后,在事务1中再次运行查询语句--此时查询语句没有被阻塞,返回的值是48,也就是事务2修改之前的数据--这是因为事务1是从版本化的行读取数据 SELECT EmployeeID, VacationHoursFROM EmployeeWHERE EmployeeID = 4;--step10:在事务2提交以后,事务1再次执行查询操作--发现查询结果还是48,这是因为事务依然从版本化的行中读取数据SELECT EmployeeID, VacationHoursFROM EmployeeWHERE EmployeeID = 4;--step11:在事务2提交修改以后,事务1想再做任何修改时,这里我们修改SickLeaveHours字段的值--此时会遇到3960错误,事务1会自动回滚,事务2中的修改不会被回滚.UPDATE EmployeeSET SickLeaveHours = SickLeaveHours - 8WHERE EmployeeID = 4;--附:假如事务2中执行了修改操作,但是没有提交,此时在事务1中执行修改操作会被阻塞--此时如果提交事务2中的修改操纵,事务1会遇到3960错误,跟上面一样.UPDATE EmployeeSET SickLeaveHours = SickLeaveHours - 8WHERE EmployeeID = 4;rollback tran tran1
commit tran tran1
/*
Msg 3960, Level 16, State 2, Line 1
Snapshot isolation transaction aborted due to update conflict. You cannot use snapshot isolation toaccess table 'Employee' directly or indirectly in database 'AdventureWorks' to update,delete, or insert the row that has been modified or deleted by another transaction. Retry the transaction or change the isolation level for the update/delete statement.
*/
--实验2结束------------------------------------

query2:事务2

--step5:开启第二个事务
BEGIN TRAN tran2;--step6:修改VacationHours,需要获得更新锁U,在VacationHours上有S锁,US不冲突,因此可以进行修改.--在修改VacationHours以后,更新锁U变成了排他锁XUPDATE Employee SET VacationHours = VacationHours - 8  WHERE EmployeeID = 4;-- step7:在当前事务中查询VacationHours,发现只有40小时SELECT VacationHoursFROM EmployeeWHERE EmployeeID = 4;--step9:提交事务2
commit tran tran2
--实验2结束------------------------------------

总结:

  1. 快照事务1的读操作没有阻塞普通事务2的读操作,但是阻塞了事务2的删除操作,如果在事务2中执行delete操作的话会报错:Employees cannot be deleted. They can only be marked as not current.Msg 3609, Level 16, State 1, Line 1The transaction ended in the trigger. The batch has been aborted.
  2. 普通事务2的更新操作,没有阻塞事务1的读操作,但是我们发现事务1中读到数据是事务2更新之前的内容。因为读取的是版本化中的行数据。

在上述实验中,我们发现下面两条语句使一起使用的,也就是首先允许数据库开启snapshot isolation,然后再将isolation level设定为snapshot。

复制代码
--step1:启用快照隔离
ALTER DATABASE AdventureWorksSET ALLOW_SNAPSHOT_ISOLATION ON;
GO--step2:设置使用快照隔离级别,前面没有设定是因为数据库默认的隔离界别就是Read Committed
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
GO
复制代码

在执行完step1以前,我们可以在sys.databases中查看AdvantureWorks的snapshot_isolation_state和 is_read_committed_snapshot_on这两个属性:

SELECT name,snapshot_isolation_state, snapshot_isolation_state_desc, is_read_committed_snapshot_on 
FROM sys.databases where name='AdventureWorks';

查询结果如下图所示:

假如我们不执行step1,只执行step2,然后开启事务进行查询,会报如下错误:

Msg 3952, Level 16, State 1, Line 3
Snapshot isolation transaction failed accessing database 'AdventureWorks' because snapshot isolation is not allowed in this database. Use ALTER DATABASE to allow snapshot isolation.

执行完step1以后,我们再次查看sys.databases中的内容,发现snapshot_isolation_state由0变为1,如下图所示:

实验3:使用行版本控制的已提交读隔离(READ_COMMITTED_SNAPSHOT)

在此示例中,使用行版本控制的已提交读事务与其他事务并发运行。已提交读事务的行为与快照事务的行为有所不同。与快照事务相同的是,即使其他事务修改了数据,已提交读事务也将读取版本化的行。与快照事务不同的是,已提交读将执行下列操作:

  • 在其他事务提交数据更改后,读取修改的数据。
  • 能够更新由其他事务修改的数据,而快照事务不能。

query1:事务1

--实验3:READ_COMMITTED_SNAPSHOT ---------------------stpe1:启用行版本控制的已提交读
-- 注意运行这句话的时候,不可以有其他连接同时使用AdventureWorks
use master
ALTER DATABASE HumanResourcesSET READ_COMMITTED_SNAPSHOT ONWITH ROLLBACK IMMEDIATE
GO--step2:设置使用已提交读隔离级别
USE HumanResources;
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
GO--step3:开启第一个事务
BEGIN TRAN tran1--step4:执行select操作,查看VacationHours,对查找的记录加S锁SELECT EmployeeID, VacationHoursFROM Employee WHERE EmployeeID = 4;--step8:在事务2中修改了数据以后,在事务1中再次运行查询语句--此时查询语句没有被阻塞,返回的值是48,也就是事务2修改之前的数据--这是因为事务1是从版本化的行读取数据 SELECT EmployeeID, VacationHoursFROM EmployeeWHERE EmployeeID = 4;--step10:在事务2提交以后,事务1再次执行查询操作-- 这里和实验2不同,事务1始终返回已提交的值,所以这里返回40,因为会话2已经提交了事务SELECT EmployeeID, VacationHoursFROM EmployeeWHERE EmployeeID = 4;--step11:这里修改会成功,不会报错.UPDATE EmployeeSET SickLeaveHours = SickLeaveHours - 8WHERE EmployeeID = 4;rollback tran tran1
--实验3结束------------------------------------

query2:事务2

--step5:开启第二个事务
BEGIN TRAN tran2;--step6:修改VacationHours,需要获得更新锁U,在VacationHours上有S锁,US不冲突,因此可以进行修改.--在修改VacationHours以后,更新锁U变成了排他锁XUPDATE Employee SET VacationHours = VacationHours - 8  WHERE EmployeeID = 4;-- step7:在当前事务中查询VacationHours,发现只有40小时SELECT VacationHoursFROM EmployeeWHERE EmployeeID = 4;--step9:提交事务2
commit tran tran2
--实验3结束------------------------------------

总结:

  1. 在事务2修改数据之后,提交之前,事务1中读到的是快照数据,也就是事务2没有修改之前的值。
  2. 在事务2提交修改之后,事务1读到了修改之后的数据。并且事务1可以修改由其他数据修改了的数据。

结论

  1. 使用 sys.databases 目录视图可以确定两个行版本控制数据库选项的状态。
  2. 对用户表和存储在 master 和 msdb 中的某些系统表的任何更新都会生成行版本。
  3. 在 master 和 msdb 数据库中,ALLOW_SNAPSHOT_ISOLATION 选项自动设置为 ON,并且不能禁用。
  4. 在 master 数据库、tempdb 数据库或 msdb 数据库中,用户不能将 READ_COMMITTED_SNAPSHOT 选项设置为 ON。
  5. 从上面的测试可以看到,原先会发生阻塞的两个会话在使用行版本控制的隔离级别后,都不会遇到阻塞了。但是两种行版本控制的结果又有不同。可以用表1来总结。

表1  使用行版本控制隔离级别后的不同

会话1

会话2

结果

A. 普通已提交事务

B. 使用快照隔离

C. 使用行版本控制的已提交读

BEGIN TRAN

查询1

48

BEGIN TRAN

修改1

成功

查询1

40

查询2

被阻塞

48

COMMIT TRAN

查询2返回40

查询3

40

48

40

修改2

ROLLBACK TRAN

成功

失败

成功

转载于:https://www.cnblogs.com/yxhblog/p/5012377.html

相关文章:

【机器学习基石笔记】八、噪声和错误

噪声的来源: 1、noise in y 2、noise in x 在有noise的情况下,vc bound还会work么??? 之前,x ~ p(x) 现在 y ~ P( y | x ) 在hoeffding的部分,只要 (x, y) 联合分布满足某个分布, 结…

H5用户地址位置选择地点获取经纬度(效果图)

效果图&#xff1a; uni-app <template><view class"flex-v flex-c wrap"><web-view src"https://apis.map.qq.com/tools/locpicker?search1&type1&key7QKBZ-SJ2HF-7TFJS-JL5NE-E6ZD7-SWFW5&referer鏅鸿兘鍚嶇墖"></we…

学习sql注入:猜测数据库_对于SQL的热爱:为什么要学习它以及它将如何帮助您...

学习sql注入:猜测数据库I recently read a great article by the esteemed craigkerstiens describing why he feels SQL is such a valuable skill for developers. This topic really resonated with me. It lined up well with notes I’d already started sketching out fo…

C++入门经典-例6.14-通过指针连接两个字符数组

1&#xff1a;字符数组是一个一维数组&#xff0c;引用字符数组的指针为字符指针&#xff0c;字符指针就是指向字符型内存空间的指针变量。 char *p; char *string"www.mingri.book"; 2&#xff1a;实例&#xff0c;通过指针连接两个字符数组&#xff0c;代码如下&am…

创建一个没有边框的并添加自定义文字的UISegmentedControl

//个性推荐 歌单 主播电台 排行榜NSArray* promoteArray["个性推荐","歌单","主播电台","排行榜"];UISegmentedControl* promoteSgement[[UISegmentedControl alloc]initWithItems:promoteArray];promoteSgement.frameCGRectMake(0, 6…

样式集(一) 通用商品列表样式

上图&#xff1a; 上代码&#xff1a; // pages/choosePackage/choosePackage.js Page({data: {list:[1,2,3],},onLoad: function (options) {},nav_upInfo(){wx.navigateTo({url: ../upInfo/upInfo,})}, }) <!--pages/choosePackage/choosePackage.wxml--> <view c…

2019 6月编程语言_今年六月您可以开始学习650项免费的在线编程和计算机科学课程...

2019 6月编程语言Seven years ago, universities like MIT and Stanford first opened up free online courses to the public. Today, more than 900 schools around the world have created thousands of free online courses, popularly known as Massive Open Online Cours…

mybatis分页练手

最近碰到个需求&#xff0c;要做个透明的mybatis分页功能&#xff0c;描述如下&#xff1a;目标&#xff1a;搜索列表的Controller action要和原先保持一样&#xff0c;并且返回的json需要有分页信息&#xff0c;如&#xff1a; ResponseBody RequestMapping(value"/searc…

样式集(二) 信息填写样式模板

上图&#xff1a; 代码&#xff1a; // pages/upInfo/upInfo.js Page({data: {tipsTxt: "请填写正确的业务流水号",showTips: false,showCityList:false,city:"",cityList:["济南市","青岛市","枣庄市","东营市"…

12小时进制的时间输出的编辑代码

关于时间输出的编辑代码个人思考了很久&#xff0c;包括顺序&#xff0c;进位之类的&#xff0c;求完善和纠错 public class yunsuanfu {public static void main(String[] arg){double t2;int h38;int m100;int s100;if(s>60){m(s/60)m;ss%60;}if (m>60){h(m/60)h;mm%6…

c++每调用一次函数+1_每个开发人员都应该知道的一些很棒的现代C ++功能

c每调用一次函数1As a language, C has evolved a lot.作为一种语言&#xff0c;C 已经发展了很多。 Of course this did not happen overnight. There was a time when C lacked dynamism. It was difficult to be fond of the language.当然&#xff0c;这并非一overnight而…

Linux ISCSI配置

一、简介 iSCSI&#xff08;internet SCSI&#xff09;技术由IBM公司研究开发&#xff0c;是一个供硬件设备使用的、可以在IP协议的上层运行的SCSI指令集&#xff0c;这种指令集合可以实现在IP网络上运行SCSI协议&#xff0c;使其能够在诸如高速千兆以太网上进行路由选择。iSCS…

样式集(三)成功页面样式模板

上图&#xff1a; 代码&#xff1a; <!--pages/result/result.wxml--> <view><image class"scc" src"/img/scc.png"></image><view class"resuil">办理成功</view> </view> <view class"btn…

C#中Request.servervariables参数

整理一下&#xff0c;我在asp.net下遍历的Request.servervariables这上集合&#xff0c;得出的所有参数如下&#xff1a; &#xff1a; Request.ServerVariables["ALL_HTTP"] 客户端发送的http所有报头信息 返回例&#xff1a;HTTP_CACHE_CONTROL:max-age0 HTT…

打开浏览器的包 node_如何发布可在浏览器和Node中使用的软件包

打开浏览器的包 nodeWhen you create a package for others to use, you have to consider where your user will use your package. Will they use it in a browser-based environment (or frontend JavaScript)? Will they use it in Node (or backend JavaScript)? Or bot…

存储过程中SELECT与SET对变量赋值

Create proc insert_bookparam1char(10),param2varchar(20),param3money,param4moneyoutputwith encryption---------加密asinsert into book(编号,书名,价格)Values(param1,param2,param3)select param4sum(价格) from bookgo执行例子&#xff1a;declare total_price moneyex…

AngularJs $resource 高大上的数据交互

$resource 创建一个resource对象的工厂函数&#xff0c;可以让你安全的和RESFUL服务端进行数据交互。 需要注入 ngResource 模块。angular-resource[.min].js 默认情况下&#xff0c;末尾斜杠&#xff08;可以引起后端服务器不期望出现的行为&#xff09;将从计算后的URL中剥离…

样式集(四)搜索框样式

上图&#xff1a; 代码&#xff1a; // pages/search/search.js var textPage({data: {input_val:"",list:[]},input_p(e){this.setData({input_val:e.detail.value})},onLoad: function (options) {}, }) <view classpage_row bindtap"suo"><vi…

初步了解React Native的新组件库firstBorn

first-born is a React Native UI Component Framework, which follows the design methodology Atomic Design by Brad Frost.first-born是React Native UI组件框架&#xff0c;它遵循Brad Frost的设计方法Atomic Design 。 Version 1.0.0 was recently published as an npm …

less里面calc() 语法

转载 Less的好处不用说大家都知道&#xff0c;确实让写CSS的人不在痛苦了&#xff0c;最近我在Less里加入calc时确发现了有点问题&#xff0c;我在Less中这么写&#xff1a;  div {  width : calc(100% - 30px);  }  结果Less把这个当成运算式去执行了&#xff0c;结果…

基于XMPP的IOS聊天客户端程序(XMPP服务器架构)

最近看了关于XMPP的框架&#xff0c;以文本聊天为例&#xff0c;需要发送的消息为&#xff1a; <message type"chat" from"kangserver.com" to"testserver.com"> <body>helloWord</body> </message> …

小程序云开发,判断数据库表的两个字段匹配 云开发数据库匹配之 and 和 or 的配合使用

云开发数据库匹配之 and 和 or 的配合使用 代码&#xff1a; // 获取成员消息onMsg2() {let that thiswx.cloud.init({env: gezi-ofhmx})const DB wx.cloud.database()const _ DB.command;var aa "1"var bb "2"DB.collection(message_logging).where…

react引入多个图片_重新引入React:v16之后的每个React更新都已揭开神秘面纱。

react引入多个图片In this article (and accompanying book), unlike any you may have come across before, I will deliver funny, unfeigned and dead serious comic strips about every React update since v16. It’ll be hilarious, either intentionally or unintention…

75. Find Peak Element 【medium】

75. Find Peak Element 【medium】 There is an integer array which has the following features: The numbers in adjacent positions are different.A[0] < A[1] && A[A.length - 2] > A[A.length - 1].We define a position P is a peek if: A[P] > A[P-1…

云开发地图标记导航 云开发一次性取所有数据

地图取 elx 表格的经纬度数据&#xff0c;存到云开发数据库里面&#xff0c;然后标记在地图上&#xff0c;点击地图的标记可以实现路线规划&#xff0c;导航&#xff0c;拨打电话。 elx数据格式如下&#xff1a; 云开发的数据库不能直接导入elx&#xff0c;所以需要转换为csv文…

未能加载文件或程序集“Report.Basic”或它的某一个依赖项。试图加载格式不正确的程序...

出现问题如下&#xff1a; 解决办法&#xff1a; 这是由于没有开启32位程序兼容模式 具体操作如下&#xff1a;找到对应的程序池--------高级设置-------修改“启用32位应用程序”状态修改为true 转载于:https://www.cnblogs.com/OliverQin/p/5018575.html

flutter开发小程序_为什么我认为Flutter是移动应用程序开发的未来

flutter开发小程序I dabbled a bit in Android and iOS development quite a few years back using Java and Objective-C. After spending about a month working with both of them, I decided to move on. I just couldn’t get into it.几年前&#xff0c;我使用Java和Obje…

小程序获取图片的宽高

代码&#xff1a; imgInfo(url){wx.getImageInfo({src: url,success (res) {console.log(res.width)console.log(res.height)return {width:res.width,height:res.height}}})},

凯撒密码、GDP格式化输出、99乘法表

1.恺撒密码的编码 plaincode input(明文&#xff1a;)print(密文&#xff1a;,end)for i in plaincode: print(chr(ord(i)3),end) 2.国家名称 GDP总量&#xff08;人民币亿元&#xff09; 中国 &#xffe5;765873.4375澳大利亚 &#xffe5; 78312.4375&#xff08;国家名称左…

random类的使用

小栗子a如下: string[] punch new[] { "石头", "剪刀", "布" }; string myPunch; public string MyPunch{get{Random random new Random();int Index random.Next(3);myPunch punch[Index].ToString();return myPunch;}} 转载于:https://ww…