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

从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. STL 智能指针

    转自: https://blog.csdn.net/k346k346/article/details/81478223 STL一共给我们提供了四种智能指针:auto_ptr.unique_ptr.sh ...

  2. boost.log在项目中应用

    //头文件#pragma once #include <string> #include <boost/log/trivial.hpp> using std::string; ...

  3. 《转》return *this和 return this有什么区别?

    别跟我说 return *this 表示返回当前对象,return this 表示返回当前对象的地址(指向当前对象的指针). 正确答案为:return *this 返回的是当前对象的克隆或者本身(若返 ...

  4. redhat yum

    背景 这个redhat可让我好一顿折腾,对于这个yum的安装,如果成功则罢,不成功可有的收拾的.还是centos比较好啊. 转载 1 下载下面四个软件包(可以从163下载,版本号自己搜索) wget ...

  5. Excel 2013 表格自用技巧

    参考 Excel表格的基本操作(精选36个技巧) Excel2013基本用法 关于VLOOKUP函数 目录 快速复制单元格 单元格内强制换行 锁定标题行 查找重复值 万元显示 单元格中显示001 按月 ...

  6. zepplin0.7.2报错ERROR, exception: null, result: %text java.lang.NullPointerException的处理

    zepplin0.7.2报错ERROR, exception: null, result: %text java.lang.NullPointerException的处理 问题描述: 使用zeppli ...

  7. ASP.NET MVC5高级编程 之 HTML辅助方法

    Html属性调用HTML辅助方法,Url属性调用URL辅助方法,Ajax属性调用Ajax辅助方法. HTML辅助方法 1.Html.BeginForm @using (Html.BeginForm(& ...

  8. Light OJ 1148

    题意: 给你N 个人, 每个人说出有多少人和他一队, 不包括他自己, 输出总人数最少值 思路: 排个序, 按照给的数目把人分为一组,就可以得出最少人数 #include<bits/stdc++. ...

  9. 金九银十中,看看这31道Android面试题

    阅读目录 1.如何对 Android 应用进行性能分析 2.什么情况下会导致内存泄露 3.如何避免 OOM 异常 4.Android 中如何捕获未捕获的异常 5.ANR 是什么?怎样避免和解决 ANR ...

  10. STM32L476应用开发之六:电池SOC检测

    便携式设备由于使用需求而配备了锂电池,但使用过程中需要掌握电源的状态才能保证设备正常运行.而且在电池充放电的过程中,监控电池的充放电状态也是保证设备安全的需要. 1.硬件设计 电池SOC检测是一个难题 ...