zoukankan      html  css  js  c++  java
  • 笔记47-徐 数据库引擎中基于行版本控制的隔离级别

    笔记47-徐 数据库引擎中基于行版本控制的隔离级别

    MSSQL隔离级别
    READ UNCOMMITTED(未提交读),相当于(NOLOCK)
    READ COMMITTED(已提交读,默认)
    REPEATABLE READ(可重复读),相当于(HOLDLOCK)
    SERIALIZABLE(序列化)
    MVCC
    SNAPSHOT(快照):可以防止幻读,可重复读
    READ COMMITTED SNAPSHOT(已提交读快照):不可防止幻读,可重复读

    参考:https://www.cnblogs.com/lyhabc/articles/3996187.html  (MICROSOFT SQL SERVER 2008技术内幕:T-SQL语言基础 笔记)

      1 --数据库引擎中基于行版本控制的隔离级别
      2 
      3 --在SQL标准的已提交读(read committed)隔离级别下,一个读操作会和一个写操作
      4 --相互阻塞。未提交读(read uncommitted)虽然不会有这种阻塞,但是读操作可能
      5 --会读到脏数据,这是大部分用户不能接受的。有些关系型数据库(例如ORACLE)
      6 --使用的是另一种处理方式。在任何一个修改之前,先对修改前的版本做一个复制,
      7 --后续的一切读操作都会去读这个复制的版本,修改将创建一个新的版本。在这种
      8 --处理方式下,读,写操作不会互相阻塞。使用这种行版本控制机制的好处,是
      9 --程序的并发性比较高,但是缺点是用户读到的虽然不是一个脏数据,但是可能
     10 --是个正在被修改马上就要过期的数据值。如果根据这个过期的值做数据修改,
     11 --会产生逻辑错误。
     12 
     13 --有些用户可能为了更高的并发性而不在乎这种缺点,所以更喜欢ORACLE的那种
     14 --处理方法。为了满足这部分用户的需求,SQL2005也引入了这种机制,来实现
     15 --类似的功能。所以选取行版本控制隔离级别也可以成为消除阻塞和死锁的一种
     16 --手段
     17 
     18 --SQL有两种行版本控制:
     19 --(1)行版本控制的已提交读隔离(read_committed_snapshot)
     20 --(2)直接使用snapshot事务隔离级别
     21 
     22 --(1)(read_committed_snapshot):read_committed_snapshot数据库选项为ON时,read_committed事务通过使用行
     23 --版本控制提供语句级读取一致性
     24 
     25 --(2)(snapshot事务隔离级别)allow_snapshot_isolation数据库选项为ON时,snapshot事务通过使用行版本
     26 --控制提供事务级读取一致性
     27 
     28 
     29 --下列示例可以说明使用普通已提交读事务,行版本控制的快照隔离事务和行版本
     30 --控制的已提交读事务的行为差异
     31 
     32 --示例:
     33 
     34 --A 普通已提交事务
     35 --在此示例中,一个普通read committed事务将读取数据,然后由另一事务修改此数据。执行
     36 --完的读操作不阻塞由其他事务执行的更新操作。但是,在其他事务已经做了更新操作后,读
     37 --操作会被阻塞住,直到更新操作事务提交为止
     38 
     39 --在会话1上:
     40 USE [AdventureWorks]
     41 GO
     42 BEGIN TRAN
     43 --查询1
     44 --这个查询将返回员工有48小时休假时间
     45 SELECT [EmployeeID],[VacationHours] FROM [HumanResources].[Employee] WHERE [EmployeeID]=4
     46 
     47 -----------------------------------------------------------------------------------------------
     48 
     49 --在会话2上:
     50 USE [AdventureWorks]
     51 GO
     52 BEGIN TRAN
     53 --修改1
     54 --休假时间减去8
     55 --修改不会被阻塞,因为会话1不会持有S锁不放
     56 UPDATE [HumanResources].[Employee]
     57 SET [VacationHours]=[VacationHours]-8
     58 WHERE [EmployeeID]=4
     59 
     60 --查询1
     61 --现在休假时间只有40小时
     62 SELECT [VacationHours] FROM [HumanResources].[Employee] WHERE [EmployeeID]=4
     63 
     64 --------------------------------------------------------------------------------------------------
     65 
     66 
     67 --在会话1上:
     68 --重新运行查询语句,会被会话2阻塞
     69 --查询2
     70 SELECT [EmployeeID],[VacationHours] FROM [HumanResources].[Employee] WHERE [EmployeeID]=4
     71 
     72 -----------------------------------------------------------------------------------------------
     73 
     74 --在会话2上:
     75 --提交事务
     76 COMMIT TRAN
     77 GO
     78 
     79 -----------------------------------------------------------------------------------------------
     80 
     81 --在会话1上:
     82 --此时先前被阻塞的查询结束,返回会话2修改好的新数据:40
     83 --查询3
     84 --这里返回40,因为会话2已经提交了事务
     85 SELECT [EmployeeID],[VacationHours] FROM [HumanResources].[Employee] WHERE [EmployeeID]=4
     86 
     87 --修改2
     88 --这里会成功
     89 UPDATE [HumanResources].[Employee]
     90 SET [SickLeaveHours]=[SickLeaveHours]-8
     91 WHERE [EmployeeID]=4
     92 
     93 SELECT [SickLeaveHours] FROM [HumanResources].[Employee] WHERE [EmployeeID]=4
     94 
     95 --可以回滚会话1的修改
     96 --会话2的修改不会受影响
     97 ROLLBACK TRAN
    
     98 GO
     99 
    100 SELECT [SickLeaveHours] FROM [HumanResources].[Employee] WHERE [EmployeeID]=4
    101 -------------------------------------------------------------------------------------------
    102 
    103 
    104 --B 使用快照隔离
    105 --此示例中,在快照隔离下运行的事务将读取数据,然后由另一个事务修改此数据。快照事务
    106 --不阻塞由其他事务执行的更新操作,他忽略数据的修改继续从版本化的行读取数据。也就是
    107 --说,读取到的是数据修改前的版本。但是,当快照事务尝试修改已由其他事务修改的数据时,
    108 --他将生成错误并终止
    109 
    110 --在会话1上:
    111 USE [AdventureWorks]
    112 GO
    113 
    114 --启用快照隔离
    115 ALTER DATABASE [AdventureWorks] SET ALLOW_SNAPSHOT_ISOLATION ON
    116 GO
    117 
    118 --设置使用快照隔离级别
    119 SET TRANSACTION ISOLATION LEVEL SNAPSHOT
    120 GO
    121 
    122 BEGIN TRAN
    123 --查询1
    124 --查询返回员工有48小时假期
    125 SELECT [EmployeeID],[VacationHours] FROM [HumanResources].[Employee] WHERE [EmployeeID]=4
    126 ---------------------------------------------------------------------------------------------
    127 
    128 --在会话2上:
    129 USE [AdventureWorks]
    130 GO
    131 
    132 BEGIN TRAN
    133 --修改1
    134 --假期时间减8
    135 --修改不会被会话1阻塞
    136 UPDATE [HumanResources].[Employee]
    137 SET [VacationHours]=[VacationHours]-8
    138 WHERE [EmployeeID]=4
    139 
    140 --查询1
    141 --确认值已经被改成40
    142 SELECT [VacationHours] FROM [HumanResources].[Employee] WHERE [EmployeeID]=4
    143 
    144 -----------------------------------------------------------------------------------------------
    145 
    146 --在会话1上:
    147 --查询2
    148 --再次运行查询语句
    149 --还是返回48(修改前的值),因为会话1是从版本化的行读取数据
    150 SELECT [EmployeeID],[VacationHours] FROM [HumanResources].[Employee] WHERE [EmployeeID]=4
    151 
    152 ------------------------------------------------------------------------------------------------
    153 
    154 --在会话2上:
    155 --提交事务
    156 COMMIT TRAN
    157 GO
    158 
    159 -------------------------------------------------------------------------------------------------
    160 
    161 --在会话1上:
    162 --查询3
    163 --再次运行查询语句
    164 --还是返回48(修改前的值),因为会话1还是从版本化的行读取数据
    165 SELECT [EmployeeID],[VacationHours] FROM [HumanResources].[Employee] WHERE [EmployeeID]=4
    166 
    167 --修改2
    168 --因为数据已经被会话2修改过,会话1想做任何修改时
    169 --会遇到3960错误
    170 --事务会自动回滚
    171 UPDATE [HumanResources].[Employee]
    172 SET [SickLeaveHours]=[SickLeaveHours]-8
    173 WHERE  [EmployeeID]=4
    174 
    175 --会话1的修改会回滚
    176 --会话2的修改不会回滚
    177 ROLLBACK TRAN
    178 GO
    179 
    180 ----------------------------------------------------------------------------------------------
    181 
    182 
    183 
    184 --C 使用行版本控制的已提交读
    185 --在此示例中,使用行版本控制的已提交读事务与其他事务并发运行。已提交读事务的行为与快照事务的行为
    186 --有所不同。与快照事务相同的是,即使其他事务修改了数据,已提交读事务也将读取版本化的行。
    187 --与快照事务不同的是,已提交读将执行下列操作:
    188 
    189 --(1)在其他事务提交数据更改之后,读取修改的数据
    190 --(2)能够更新由其他事务修改的数据,而快照事务不能
    191 
    192 --在会话1上:
    193 USE [AdventureWorks]
    194 GO
    195 --启用行版本控制的已提交读
    196 --注意运行这句话的时候,不可以有其他连接同时使用[AdventureWorks]
    197 ALTER DATABASE [AdventureWorks] SET READ_COMMITTED_SNAPSHOT ON
    198 GO
    199 
    200 --设置使用已提交读隔离级别
    201 SET TRANSACTION ISOLATION LEVEL READ COMMITTED
    202 GO
    203 
    204 BEGIN TRAN
    205 --查询1
    206 --这里将返回初始值48
    207 SELECT [EmployeeID],[VacationHours] FROM [HumanResources].[Employee] WHERE [EmployeeID]=4
    208 
    209 ----------------------------------------------------------------------------------------------------
    210 
    211 --在会话2上:
    212 USE [AdventureWorks]
    213 GO
    214 
    215 BEGIN TRAN
    216 --修改1
    217 --假期时间减8
    218 --修改不会被会话1阻塞
    219 UPDATE [HumanResources].[Employee]
    220 SET [VacationHours]=[VacationHours]-8
    221 WHERE [EmployeeID]=4
    222 
    223 --查询1
    224 --确认值已经被修改为40
    225 SELECT [VacationHours] FROM [HumanResources].[Employee] WHERE [EmployeeID]=4
    226 
    227 ------------------------------------------------------------------------------------------------
    228 
    229 --在会话1上:
    230 --查询2
    231 --再次运行查询语句
    232 --还是返回48(修改前的值),因为会话2还没有提交
    233 --会话1是从版本化的行读取数据
    234 SELECT [EmployeeID],[VacationHours] FROM [HumanResources].[Employee] WHERE [EmployeeID]=4
    235 
    236 -------------------------------------------------------------------------------------------------
    237 
    238 --在会话2上:
    239 --提交事务
    240 COMMIT TRAN
    241 GO
    242 --------------------------------------------------------------------------------------------------
    243 
    244 --在会话1上:
    245 --查询3
    246 --这里和范例B不同,会话1始终返回已提交的值
    247 --这里返回40,因为会话2已经提交了事务
    248 SELECT [EmployeeID],[VacationHours] FROM [HumanResources].[Employee] WHERE [EmployeeID]=4
    249 
    250 --修改2
    251 --这里会成功
    252 UPDATE [HumanResources].[Employee]
    253 SET [SickLeaveHours]=[SickLeaveHours]-8
    254 WHERE [EmployeeID]=4
    255 
    256 --可以回滚会话1的修改
    257 --会话2的修改不会受影响
    258 ROLLBACK TRAN
    259 GO
    260 
    261 
    262 ------------------------------------------结论-----------------------------------------------------------
    263 --从上面的测试结果可以看到,原先会发生阻塞的两个会话在使用行版本控制的隔离级别后,都不会遇到阻塞了。
    264 --但是两种行版本控制的结果又有不同
    265 
    266 --但是行版本控制并不是消除阻塞和死锁的万灵药。在决定使用之前,必须考虑下面两个问题
    267 
    268 --(1)最终用户是否接受行版本控制下的运行结果?  行版本控制:数据库级别
    269 --上面的3个测试返回的结果都各有不同。在不同的事务阶段,有的被阻塞住,有的读到的是旧版本值,
    270 --有的读到新版本值。用户期望的行为是什么?他是希望哪怕被阻塞住也要读到最新版本数据,还是
    271 --能容忍读到旧版本数据呢?某些应用程序依赖于读隔离的锁定和阻塞行为,例如生成一个串行的流水号
    272 --之类的操作。改成行版本控制,原先的处理逻辑就不能正常工作了。所以在采用新的隔离级别之前,
    273 --一定要做好测试,确保应用按预期的逻辑运行
    274 
    275 
    276 
    277 
    278 --(2)SQL是否能支持行版本控制带来的额外负荷?
    279 --开启了行版本控制之后,SQL会把行版本存放在tempdb里。修改的数据越多,需要存储的信息越多
    280 --对SQL额外的负载就越大。所以如果一个应用要从其他隔离级别转向使用行版本控制,需要做特别
    281 --的测试,以确保现有的软硬件配置能支持额外的负荷,应用程序能够达到相似的响应速度

    对于库存类业务,严格按照串行方式执行的,那么建议使用SNAPSHOT(快照,也叫全快照隔离级别),在SNAPSHOT(快照)下面,别的事务要等修改数据的那个事务运行完才能修改,保证串行,缺点是别的事务没有重试机制的话
    会造成更新丢失

    对于库存类业务,不建议使用READ COMMITTED SNAPSHOT(已经提交读隔离),不保证串行方式执行,两个事务可以同时修改同一行数据,A事务读的是当前版本,B事务读的是历史版本,同时修改的时候,会造成数据错乱
    因为B事务修改的是历史版本数据

    RDS-SQLSERVER的READ COMMITTED与READ_COMMITTED_SNAPSHOT的区别及各自优缺点
    解剖SQLSERVER 第一篇 数据库恢复软件商的黑幕(有删减版)
    https://www.cnblogs.com/lyhabc/p/3900229.html
    https://www.cnblogs.com/lyhabc/articles/3967438.html

    更新丢失更清楚的解释:https://www.2cto.com/database/201609/547323.html

    SQL Server实现了SQL99定义的四个标准隔离级别,并且额外实现了两个快照隔离级别
    需要说明的是,不同的DBMS的默认隔离级别和对隔离级别的实现是不完全一样的,也不一定是完全按照SQL99定义的四个标准隔离级别来实现的

    下面两种是mssql增加的为了兼容mvcc特性而增加的隔离级别,mysql默认就支持mvcc,所以不会有下面两种隔离级别

    SNAPSHOT(快照)
    READ COMMITTED SNAPSHOT(已经提交读隔离)

  • 相关阅读:
    记一次授权的APK渗透测试
    Web.config在渗透中的作用
    ctf中关于syscall系统调用的简单分析
    【文件包含&条件竞争】详解如何利用session.upload_progress文件包含进行RCE
    利用python免杀cs shellcode
    记一次ARM架构的ROP利用
    改造冰蝎对抗waf&OpenRASP计划-初探
    java后台管理 开源_12款开源的JAVA后台管理项目
    ECS 选款利器!PTS助您快速上云!
    Ocelot 负载均衡
  • 原文地址:https://www.cnblogs.com/lyhabc/p/3219693.html
Copyright © 2011-2022 走看看