Sql Server并发和事务
锁的作用范围通常在事务中,事务是建立在并发模式下。
从SQL Server 2005开始,加入了一种新的并发模式-----乐观并发。不管使用哪种并发模式,如果多个会话同时修改相同的数据,都会产生资源争用,然后引发一系列的问题。
1.存在的读现象:包括脏读、不可重复读和幻读。
2.丢失更新:一个会话的修改效果被另外一个会话意外覆盖
3.过量的锁定:过量的锁定会导致阻塞,导致资源压力和终端用户的响应延时
4.死锁:最少两个会话互相阻塞对方,引发死锁。SQL Server 尽可能自动侦测并干预死锁,最后给客户端返回1205错误
绝大部分基于锁的阻塞问题都可以通过优化查询、调整索引、修改隔离级别及表结构的设计来大大降低
一:悲观并发和乐观并发
默认情况下,SQLServer 都会采用悲观模式来应对并发。大量的并发操作,如果不使用悲观并发来防止多个会话对数据同时修改,会造成数据不一致。在该模式下,将会通过加锁的形式保护正在被读取的事务,以保证这些数据在读取过程中不被其他会话修改。在会话修改数据的过程中也会加锁,防止其他会话读取或者修改对应的数据。也就是在悲观模式下,读操作阻塞写操作,写操作阻塞读写操作。数据库中所有操作都会加锁。
对于乐观并发,SQL Server 会假设只有少量的冲突发生。其默认的机制就是使用快照的行版本技术,把行版本存放在TempDB数据库中
在两种并发模式下,SQL Server 都会通过使用特定的事务隔离级别来协调事务间的争用问题。不同的隔离级别会申请不同的锁,也会影响锁的持有时间,越高的隔离级别,锁持有的时间越长,越能避免资源争用和数据的不一致性,但是并发度也越低。
基于读写,写写互相阻塞的特性(读读不互相阻塞),SQL Server中普遍存在锁定及阻塞情况,随着并发量和数量的逐步增长,其潜在的问题将会凸显。锁定的持续时间及范围越来越大,数据库的总体性能也会越来越低。
悲观并发对所有行为都加锁,怕在操作过程中意外影响,所以被称为悲观模式。甚至有点独占的意味。在这种情况下,读写是互相阻塞的。乐观并发通过把悲观并发在读写时加S锁的行为移到TempDB的行版本存储中,避免了读阻塞写的操作,从而把读写的资源争用降到最低,并尽量实现读操作不阻塞写操作,写操作永远阻塞读操作。
二:事务
不管使用悲观并发还是乐观并发,都会涉及事务这个概念,事务就是一个操作单元。这个操作可能是一行UPDATE语句,也可能是异常复杂的一系列增删改操作。
1.Atomicity,原子性。一个事务被当作单独的工作单元,不管事务内有什么东西,都被认为是一个整体。
2.Consistency,一致性。一个事务的执行前后状态都应该是一个逻辑一致性状态。
3.Isolation,隔离性。事务的内部不应该和其他事务有交互。
4.Durability,持久性,一旦事务完成,它的效果应该是永久的,不管是什么情况 ,包括系统关闭,都需要保证事务的结果。
SQL Server 默认保证其中3个特性:原子性、一致性、和持久性。如果要保证隔离性,就需要使用比默认级别更高级的隔离级别。
(1).事务作用域
SQL Server 事务有四类作用域,其中两种(显示事务、自动提交事务)是默认的。另外两种(隐式事务、批范围事务)是特定情况下才用的。
一:自动提交事务
自动提交事务是一个单独的数据修改操作。任何INSERT、UPDATE、DELETE、MERGE、BULK INSERT等操作,都属于自动提交事务。一个单独的UPDATE语句,不管其影响行数是1行还是100万行,都会当做一个原子操作。在其中发生故障,SQL Server 会回滚这些操作,就当没有发生过一样。
if DB_ID('Demo_Isolation')is not null
drop database Demo_Isolation
create database Demo_Isolation
go
use Demo_Isolation
if OBJECT_ID('Demo_Table','U') is not null
drop table Demo_Table
create table Demo_Table
(
id int identity(1,1),
name nvarchar(32)
);
检查一下当前数据库的隔离级别。
dbcc useroptions
insert into dbo.Demo_Table(name)
select top 100000000 a.name
from sys.columns a cross join sys.columns b
cross join sys.columns
然后马上执行关闭服务,模拟系统故障。
insert into dbo.Demo_Table(name)
select top 100000000 a.name
from sys.columns a cross join sys.columns b
cross join sys.columns c
rollback
关闭窗体,没有办法强制回滚,只有在系统故障时事务才会回滚
(2)显式事务
显式事务是以BEGIN TRANSACTION开头、以COMMIT TRANSACTION或者ROLLBACK TRANSACTION结尾的、包含有任意数据量语句的代码块。这类事务是最常见的事务类型
(3)隐式事务
隐式事务必须在会话开始时,使用SET IMPLICIT_TRANSACTIONS ON 开启。不需要使用Begin tran作为开头,但是必须显示使用COMMIT/ROLLBACK TRAN结束事务。不建议使用这种方式,尽可能使用显示事务来定义
(4)批范围事务
用于在客户端连接字符串中使用了Multiple Active Result Sets(MARS)选项的事务。SQL Server会回滚所有以BEGIN TRAN开头但是不包含COMMIT TRAN的事务,目的是避免应用程序死锁。
2.事务隔离级别
每一个事务都运行在一个特定的事务隔离级别中,这个由会话的隔离级别决定。通过隔离级别,SQL Server 会决定锁的持有时间。如果会话/事务过程中没有使用SET TRANSACTION ISOLATION LEVEL【隔离级别】或者Lock hints指定,默认会使用当前数据库的隔离级别,通常为READ COMMITTED。
(1)READ UNCOMMITTED:允许所有脏读,不可重复读和幻读。
(2)READ COMMITTED:防止脏读,但允许不可重复读和幻读。
(3)REPEATABLE READ:防止脏读和不可重复读,但允许幻读。
(4)SERIALIZABLE:防止所有读现象
上面的4类隔离隔离级别中,除了READ COMMITTED(其中基于快照的一种)是乐观并发之外,其他都是悲观并发。在这几种隔离级别下,SQL Server会申请共享锁和排他锁,以避免当前事务正在读取的数据被另一个事务更改。从SQL Server2005开始,添加了新的乐观并发隔离级别,称为SNAPSHOT隔离,其中READ COMMITTED也加入了一种新的隔离(READ_COMMITTED_SNAPSHOT),这两种隔离级别不需要加入共享锁,能够重一定程度上增强并发性。
隔离级别可以通过在会话的开端使用SET TRANSACTION ISOLATION LEVEL【隔离级别】来修改SQL Server的默认设置。也可以在语句中使用lock hints来修改。
3.需要预防的读现象
隔离级别存在的一个重要目的是预防3种读现象:脏读、不可重复读和幻读
(1)脏读
当事务运行在READ UNCOMMITTED隔离级别下时,会出现脏读。如果事务A修改了数据,但是没有提交,而另一个事务B此时又读取了这部分的数据,就很容易出现数据的非一致性状态,这种状态叫做脏读。默认情况下,SQL Server 使用了READ COMMITTED隔离级别,而这种级别不允许脏读,当事务正在修改的数据时,对其他事务是否能在提交前读取数据不会进行控制。这种特性会导致读取了非一致性的数据,俗称“脏数据”。除了数据不一致,还有另外一种潜在的风险,当事务A在READ UNCOMMITTED下进行大表扫描时,由于没有对访问的数据进行加锁,其他事务可以进行UPDATE操作。如事务A扫描了表中一半的数据,已经读取了某条数据,但是事务B中途进行了UPDATE操作,导致数据的存储位置改变,可能已经移到表的最末端。这时候事务A在完成扫描之后,实际上已经扫描了这条数据两次,甚至更多次。
打开两个窗口:
begin transaction;--显示事务
update Person.Person
set FirstName='James'
where LastName='Jones';
waitfor delay '00:00:05.000';
rollback transaction; select FirstName,LastName from Person.Person where LastName='Jones';
set transaction isolation level read uncommitted;
select FirstName,LastName from Person.Person where LastName='Jones'
两边的数值不同,窗口A中使用了ROLLBACK,理论上应该回滚,但是由于窗口B 使用了READ UNCOMMITTED隔离级别,会出现脏读,所以窗口B中的数据读出来和窗口A的不一样。
(2)不可重复读
如果在一个事务中单独两次查询相同的数据,获得不一样的结果,就称为不可重复读。(在同一个事务中的两次查询)通常是因为两次读取数据之间,有其他事务对这些数据进行了修改操作。从而导致第二次读取到不一致的数据。
(3)幻读
这种情况发生在带有限定词的查询中,如where条件,如一个事务中,两个相同的where 条件的select操作,反回了不同的数据行。为幻读。
3.丢失更新
如果一个事务进行了UPDATE操作后,在未提交或者回滚事务前,另一个事务又做了同样的操作,这样就会覆盖第一次的update,导致第一次update就像 没有出现过。所有的隔离级别都用于防止丢失更新,也就是说不允许两个事务同时更新相同的数据,以免其中一个事务的更新操作丢失。
DECLARE @SafetyStockLevel INT = 0 ,
@Uplift INT = 5;
BEGIN TRAN;
SELECT @SafetyStockLevel = SafetyStockLevel
FROM Production.Product
WHERE ProductID = 1;
SET @SafetyStockLevel = @SafetyStockLevel + @Uplift;
WAITFOR DELAY '00:00:05.000';
UPDATE Production.Product
SET SafetyStockLevel = @SafetyStockLevel
WHERE ProductID = 1;
SELECT SafetyStockLevel
FROM Production.Product
WHERE ProductID = 1;
COMMIT TRAN;
DECLARE @SafetyStockLevel INT = 0 ,
@Uplift INT = 100;
BEGIN TRAN;
SELECT @SafetyStockLevel = SafetyStockLevel
FROM Production.Product
WHERE ProductID = 1;
SET @SafetyStockLevel = @SafetyStockLevel + @Uplift;
UPDATE Production.Product
SET SafetyStockLevel = @SafetyStockLevel
WHERE ProductID = 1;
SELECT SafetyStockLevel
FROM Production.Product
WHERE ProductID = 1;
COMMIT TRAN;
A的代码:在waitfor前面会把数据加5,等待5s。在等待的过程中,窗口B执行Update,由于窗口A的事务为提交,所以实际修改没有发生,所以对事务之外的事务来说,数据还是1000,在B中,实际上已经对‘1000’这个值update了。两次update虽然在独立的事务内,但是由于窗口A中的update操作是在后面提交的,最终覆盖了窗口B的update,导致窗口B的操作“没有发生过”。最后提交的操作会覆盖先提交的操作。
对于丢失更新的问题,可以使用悲观并发或者乐观并发来避免。避免丢失的操作是发生在更新前还是跟新后。悲观并发是在更新前避免,乐观并发是在更新后避免。在悲观并发模式中,读取数据时申请锁,这样一来,其他事务就无法更新了。同样,在更新时也加锁,让其他事务无法读。从而避免了丢失更新。
Sql Server并发和事务的更多相关文章
- Microsoft SQL Server中的事务与并发详解
本篇索引: 1.事务 2.锁定和阻塞 3.隔离级别 4.死锁 一.事务 1.1 事务的概念 事务是作为单个工作单元而执行的一系列操作,比如查询和修改数据等. 事务是数据库并发控制的基本单位,一条或者一 ...
- SQL Server 中的事务与事务隔离级别以及如何理解脏读, 未提交读,不可重复读和幻读产生的过程和原因
原本打算写有关 SSIS Package 中的事务控制过程的,但是发现很多基本的概念还是需要有 SQL Server 事务和事务的隔离级别做基础铺垫.所以花了点时间,把 SQL Server 数据库中 ...
- 【转】SQL Server中的事务与锁
SQL Server中的事务与锁 了解事务和锁 事务:保持逻辑数据一致性与可恢复性,必不可少的利器. 锁:多用户访问同一数据库资源时,对访问的先后次序权限管理的一种机制,没有他事务或许将会一塌糊涂 ...
- SQL Server中的事务日志管理(1/9):事务日志概况
当一切正常时,没有必要特别留意什么是事务日志,它是如何工作的.你只要确保每个数据库都有正确的备份.当出现问题时,事务日志的理解对于采取修正操作是重要的,尤其在需要紧急恢复数据库到指定点时.这系列文章会 ...
- sql server 锁与事务拨云见日(中)
一.事务的概述 上一章节里,重点讲到了锁,以及锁与事务的关系.离上篇发布时间好几天了,每天利用一点空闲时间还真是要坚持.听<明朝那些事儿>中讲到"人与人最小的差距是聪明,人与人最 ...
- (转)深入sql server中的事务
原文地址:http://www.cnblogs.com/chnking/archive/2007/05/27/761209.html 参考文章:http://www.cnblogs.com/zhuif ...
- Sql Server中的事务隔离级别
数据库中的事物有ACID(原子性,一致性,隔离性,持久性)四个特性.其中隔离性是用来处理并发执行的事务之间的数据访问控制.SqlServer中提供了几种不同级别的隔离类型. 概念 Read UnCom ...
- SQL Server中的事务与其隔离级别之脏读, 未提交读,不可重复读和幻读
原本打算写有关 SSIS Package 中的事务控制过程的,但是发现很多基本的概念还是需要有 SQL Server 事务和事务的隔离级别做基础铺垫.所以花了点时间,把 SQL Server 数据库中 ...
- SQL Server中的事务日志管理的阶梯,级别1:事务日志概述
SQL Server中的事务日志管理的阶梯,级别1:事务日志概述 翻译:刘琼滨 谢雪妮 许雅莉 赖慧芳 级别1:事务日志概述 事务日志是一个文件,其中SQL服务器存储了所有与日志文件关联的数据库执行的 ...
随机推荐
- 【转】判断处理器是Big_endian的还是Little_endian的
首先说明一下Little_endian和Big_endian是怎么回事. Little_endian模式的CPU对操作数的存放方式是从低字节到高字节,而Big_endian模式则是从高字节到低字节,比 ...
- 《The Practice and Theory of Bolshevism》的笔记-第114页
章节名:International Policy 页码:第114页 2017-09-30 15:11:24 Among religions, Bolshevism is to be reckoned ...
- Spring MVC的核心控制器DispatcherServlet的作用
关于Spring MVC的核心控制器DispatcherServlet的作用,以下说法错误的是( )? 它负责接收HTTP请求 加载配置文件 实现业务操作 初始化上下应用对象ApplicationC ...
- Spring加载加密的配置文件
一.继承并实现自己的属性文件配置器类 /** * 带加密的Spring属性配置文件扩展类 * 加密方式:AES * @author simon * */ public class EncryptPro ...
- 解决log4j和self4j日志报错Could NOT find resource [logback.groovy]及Could NOT find resource [logback-test.xml]问题
事件背景: 我的log4j和self4j按照网上的配置,配置成功了,但是报错如下: 让我很是郁闷,于是找了一大圈........ 解决方案: 总结来说就是:log4j.properties和logba ...
- 安装v2ray+SwitchyOmega使用谷歌***
系统环境:ubuntu18.04 1.安装v2ray 在root用户下执行命令:bash < (curl -L -s https://install.direct/go.sh) $ cd /e ...
- python字符串前面u,r,b的含义详解
u/U:表示unicode字符串 不是仅仅是针对中文, 可以针对任何的字符串,代表是对字符串进行unicode编码. 一般英文字符在使用各种编码下, 基本都可以正常解析, 所以一般不带u:但是中文, ...
- Affiliate实战记录之一:CPI、CPA、CPM...名词解释
1.CPM (Cost Per Mille,或者Cost Per Thousand;Cost Per Impressions) 每千人成本,按展示次数收费 网上广告收费最科学的办法是按照有多少人看到你 ...
- 查看MySQL版本的命令及常用命令
Windows / Linux 系统 前提是已经正确安装了 MySQL,打开 Windows 系统中的命令行工具(Win + R --> 输入 cmd 并按下回车键)--> 输入命令: m ...
- Oracle 数据库导入与出
Oracle 数据库导入与出 导出( EXPORT )是用 EXP 将数据库部分或全对象的结构和导出 . 导入( 导入( IMPORT )是用 )是用 IMP IMP将 OS 文件中的对象结构和数据装 ...