锁的作用范围通常在事务中,事务是建立在并发模式下。

从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并发和事务的更多相关文章

  1. Microsoft SQL Server中的事务与并发详解

    本篇索引: 1.事务 2.锁定和阻塞 3.隔离级别 4.死锁 一.事务 1.1 事务的概念 事务是作为单个工作单元而执行的一系列操作,比如查询和修改数据等. 事务是数据库并发控制的基本单位,一条或者一 ...

  2. SQL Server 中的事务与事务隔离级别以及如何理解脏读, 未提交读,不可重复读和幻读产生的过程和原因

    原本打算写有关 SSIS Package 中的事务控制过程的,但是发现很多基本的概念还是需要有 SQL Server 事务和事务的隔离级别做基础铺垫.所以花了点时间,把 SQL Server 数据库中 ...

  3. 【转】SQL Server中的事务与锁

    SQL Server中的事务与锁   了解事务和锁 事务:保持逻辑数据一致性与可恢复性,必不可少的利器. 锁:多用户访问同一数据库资源时,对访问的先后次序权限管理的一种机制,没有他事务或许将会一塌糊涂 ...

  4. SQL Server中的事务日志管理(1/9):事务日志概况

    当一切正常时,没有必要特别留意什么是事务日志,它是如何工作的.你只要确保每个数据库都有正确的备份.当出现问题时,事务日志的理解对于采取修正操作是重要的,尤其在需要紧急恢复数据库到指定点时.这系列文章会 ...

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

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

  6. (转)深入sql server中的事务

    原文地址:http://www.cnblogs.com/chnking/archive/2007/05/27/761209.html 参考文章:http://www.cnblogs.com/zhuif ...

  7. Sql Server中的事务隔离级别

    数据库中的事物有ACID(原子性,一致性,隔离性,持久性)四个特性.其中隔离性是用来处理并发执行的事务之间的数据访问控制.SqlServer中提供了几种不同级别的隔离类型. 概念 Read UnCom ...

  8. SQL Server中的事务与其隔离级别之脏读, 未提交读,不可重复读和幻读

    原本打算写有关 SSIS Package 中的事务控制过程的,但是发现很多基本的概念还是需要有 SQL Server 事务和事务的隔离级别做基础铺垫.所以花了点时间,把 SQL Server 数据库中 ...

  9. SQL Server中的事务日志管理的阶梯,级别1:事务日志概述

    SQL Server中的事务日志管理的阶梯,级别1:事务日志概述 翻译:刘琼滨 谢雪妮 许雅莉 赖慧芳 级别1:事务日志概述 事务日志是一个文件,其中SQL服务器存储了所有与日志文件关联的数据库执行的 ...

随机推荐

  1. MySql delete和truncate区别

    项目 delete truncate 添加where条件 可以添加 不可以添加 执行效率 略高 高 自增长列 delete删除后,插入数据的自增长 列值从断点开始 truncate删除后,插入数据的自 ...

  2. MySQL如何启用密码强度审计【转】

    1.密码验证插件安装 要使服务器可以使用,插件库文件必须位于MySQL插件目录(plugin_dir系统变量指定的目录)中.如有必要,请设置plugin_dir服务器启动时的值, 以告知服务器插件目录 ...

  3. oracle11g自动内存管理

    Oracle一直不停的在为Orace数据库的自动化管理努力着,11G中的自动内存管理是Oracle数据库中又一新的里程碑,通过新参数MEMORY_TARGET 来代替PGA和SGA的配置,ORACLE ...

  4. OpenStack实践系列⑥构建虚拟机实例

    OpenStack实践系列⑥构建虚拟机实例 四.创建一台虚拟机图解网络,并创建一个真实的桥接网络 创建一个单一扁平网络(名字:flat),网络类型为flat,网络适共享的(share),网络提供者:p ...

  5. 新建项目虚拟环境及pycharm配置

    基本操作 查询已有的虚拟环境 workon 激活虚拟环境 workon 虚拟环境名 退出虚拟环境 deactivate 删除虚拟环境 rmvirtualenv 虚拟环境名 查看python版本检查 p ...

  6. 排查linux系统是否被入侵

    在日常繁琐的运维工作中,对linux服务器进行安全检查是一个非常重要的环节.今天,分享一下如何检查linux系统是否遭受了入侵? 一.是否入侵检查 1)检查系统日志 检查系统错误登陆日志,统计IP重试 ...

  7. PyJWT 使用

    最近要用 Falsk 开发一个大点的后端,为了安全考虑,弃用传统的Cookie验证.转用JWT. 其实 Falsk 有一个 Falsk-JWT 但是我觉得封装的太高,还是喜欢通用的 PyJWT . J ...

  8. wx.chooseImage

    <view>上传图片</view> <view> <view> <button bindtap="getImg">上传图 ...

  9. Windows添加.NET Framework 3.0 NetFx3 失败 - 状态为:0x800f0950

    原文链接:https://answers.microsoft.com/zh-hans/insider/forum/all/win10-dism%E9%94%99%E8%AF%AF-0x800f0950 ...

  10. 基于 Confluence 6 数据中心在你的 Atlassian 应用中配置 SAML 授权

    希望在 Confluence 中配置SAML: Go to  > 基本配置(General Configuration) > SAMl 授权(SAML Authentication). 选 ...