zoukankan      html  css  js  c++  java
  • SQLserver锁和事务隔离级别的比较与使用

    对象

         :每条SQL语句

         隔离:事务

    并发问题

    丢失更新

    未确认的读取(脏读)

    不一致的分析(非重复读):多次读取相同的数据(行)不一致(其他用户更改update

    幻像读:多次读取有不存在和新增的数据(其他用户插入insert或删除delete

    隔离级别

    隔离级别

    脏读

    不可重复读取

    幻像

    说明

    未提交读(read uncommitted)

    如果其他事务更新,不管是否提交,立即执行

    提交读(read committed默认)

    读取提交过的数据。如果其他事务更新没提交,则等待

    可重复读(repeatable read)

    查询期间,不允许其他事务update

    可串行读(serializable)

    查询期间,不允许其他事务insert或delete

    提交读

    假设存在表A,如下所示

    A1

    A2

    A3

    11

    21

    31

    12

    22

    32

    打开查询分析器并打开两个连接,分别输入如下两个事务:

    --事务Ⅰ

    SET TRANSACTION ISOLATION LEVEL READ Committed

    begin tran

    update A set A2 = 20 where A1 = 11

    waitfor delay '00:00:10'

    rollback tran

    --事务Ⅱ

    SET TRANSACTION ISOLATION LEVEL READ Committed

    select * from A where A1 = 11

    如果先运行事务Ⅰ,然后紧接着运行事务Ⅱ,则事务Ⅱ要等待10秒钟(一个连接在修改数据块时别的连接也不能查询这个数据块,直到解。反之亦然:读的时候不能写和修改)。

    如果把事务Ⅱ改为如下

    SET TRANSACTION ISOLATION LEVEL READ UNCommitted

    select * from A where A1 = 11

    那么事务Ⅱ不需等待,立即执行(可以看出READ UNCommitted事务select不对数据发出共享

    (这里主要讲解 共享 排他 两种经常用到的)

    共享主要是为了共享读(select),如果存在事务(一个或多个)拥有对表中数据(关于数据的多少,视的粒度而定)的共享,不允许对定的数据进行更新(update)(从的角度讲,即不允许事务获取排他,要等到所有的共享都释放掉)。反之,如果事务对数据已经具有排他(只能有一个),其他的事务就不能对定的数据获取共享和排他(即排他与共享不能兼容,更多信息请查看兼容性),在此特别强调一下 定的数据 ,因为有的资料上讲解到“一个连接写的时候,另一个连接可以写”,实际上写的这种情况是各个连接的读写的数据不是相同的行,也就是说各个连接定的数据不同。

    根据以上分析,我们总结为六个字为“共享读,排他写”。

    了解了的情况之后,又涉及到一个问题。事务究竟要保持多久呢?

    一般来说,共享定时间与事务的隔离级别有关,如果隔离级别Read Committed的默认级别,只在读取(select)的期间保持定,即在查询出数据以后就释放了;如果隔离级别为更高的Repeatable readSerializable,直到事务结束才释放。另说明,如果select语句中指定了HoldLock提示,则也要等到事务结束才释放

    排他直到事务结束才释放。

    做出了以上分析,现在我们可能会存在这样的疑问,到底在执行SQL语句的时候发出什么样的呢,这就由事务的隔离级别决定了。一般情况,读语句(select)发出共享,写语句(update,insert,delete)发出排他。但是,如果这样不能满足我们的要求怎么办呢,有没有更多选择呢,别急,SQLserver为我们提供了定提示的概念。

           定提示对SQL语句进行特别指定,这个指定将覆盖事务的隔离级别。下面对各个定提示分别予以介绍(更多资料请查看SQLserver的联机帮助),笔者做出了以下分类。

    类型1

         READUNCOMMITTED:不发出

         READCOMMITTED:发出共享,保持到读取结束

         REPEATABLEREAD:发出共享,保持到事务结束

         SERIALIZABLE:发出共享,保持到事务结束

    类型2

         NOLOCK:不发出。等同于READUNCOMMITTED

         HOLDLOCK:发出共享,保持到事务结束。等同于SERIALIZABLE

         XLOCK:发出排他,保持到事务结束。

         UPDLOCK:发出更新,保持到事务事务结束。(更新:不阻塞别的事物,允许别的事物读数据(即更新可与共享兼容),但他确保自上次读取数据后数据没有被更新

         READPAST:发出共享,但跳过定行,它不会被阻塞。适用条件:提交读的隔离级别,行级select语句中。

    类型3

         ROWLOCK:行级

         PAGLOCK:页级

         TABLOCK:表

         TABLOCKX:表排他

    讲解完后,下面结合一个具体实例,具体看一下的使用。

           在很多系统中,经常会遇到这种情况,要保持一个编号的唯一,如会计软件中的凭证的编号。一种编号的处理是这样的,把表中的最大编号保存到表中,然后在这个编号上累加,形成新的编号。这个过程对并发处理要求非常高,下面我们就来模拟这个过程,看如何保持编号的唯一性。

           新建一张表code来保存凭证的最大编号。字段如下:编号:bh(numeric(18,0)),凭证表名pinzheng(varchar(50))

    假设表中有这样的一条记录:

    Bh

    Pinzheng

    18000

    会计凭证

    新建一个存储过程来生成新的凭证编号,如下:

    CREATE PROCEDURE up_getbh  AS

           Begin Tran

                  Declare @numnewbh numeric(18,0)

                  select  @numnewbh = bh FROM code  WITH (UPDLOCK,ROWLOCK) where pinzheng = '会计凭证'

                  set @numnewbh = @numnewbh + 1

                  update code set  bh = @numnewbh where pinzheng = '会计凭证'

                  print @numnewbh

           Commit tran

    GO

    然后,打开查询分析器,并多开几个连接(笔者开了8个连接,模拟有8个人同时并发,读者可以开更多的连接进行试验),把类似以下这样的语句复制到每个连接窗口中,

    declare @i numeric(18,0)

    set @i = 1

    while @i = 1

    Begin

           if getdate() > '2004-07-22 14:23'  --设定一个时间,到此时间同时执行upgetbh存储过程

                  set @i = 0      

    end

    exec up_getbh

    然后,接连运行各个连接,到2004-7-22 1423 这一刻,各个连接同时运行up_getbh。从运行结果可以看出连接顺序出现18001开始个数字,并没有重号或丢号的现象。

    分析:由于up_getbh中的select语句使用了更新,因更新之间不能兼容,所以各个连接要等到所有其他的连接释放掉才能执行,而更新的释放要等到事务结束,这样就不会发生号出错的现象了。

    附:的兼容性表

    现有的授权模式

    人生无处不PK
  • 相关阅读:
    字符编码相关
    函数之形参与实参
    文件操作模式
    函数对象,名称空间,作用域,和闭包
    吴裕雄天生自然SPRINGBOOT开发实战处理'spring.datasource.url' is not specified and no embedded datasource could be autoconfigured
    吴裕雄天生自然SPRINGBOOT开发实战处理XXXX that could not be found.
    吴裕雄天生自然SPRINGBOOT开发实战SpringBoot HTML表单登录
    吴裕雄天生自然SPRINGBOOT开发实战SpringBoot REST示例
    吴裕雄天生自然SpringBoot开发实战学习笔记处理 Could not write metadata for '/Servers'.metadata\.plugins\org.eclipse.core.resources\.projects\Servers\.markers.snap (系统找不到指定的路径。)
    吴裕雄天生自然SPRINGBOOT开发实战SpringBoot Tomcat部署
  • 原文地址:https://www.cnblogs.com/ivanyb/p/1526114.html
Copyright © 2011-2022 走看看