数据库基本知识点梳理系列 - 事务

数据库基本知识点梳理系列 - 了解数据库事务

Transaction 事务

事务是一种机制,是一种操作序列,它包含了数据库一组操作命令,这组命令要么全部执行,要么都不执行。因此事务是一组不可分割的事务逻辑单元,在数据库进行并发操作时候,事务是作为最小的控制单元来使用的.

事务的四大特性

名称 描述
原子性 事务必须是一个自动工作的单元,要么全部执行,要么全部不执行。
隔离性 并发多个事务时,各个事务不干涉内部数据,处理的都是另外一个事务处理之前或之后的数据。
一致性 事务结束的时候,所有的内部数据都是正确的。
持久性 事务提交之后,数据是永久性的,不可再回滚。

Sql Server中的事务

在SQL Server中事务被分为3类常见的事务:

事务名称 描述
自动提交事务 是SQL Server默认的一种事务模式,每条Sql语句都被看成一个事务进行处理,你应该没有见过,一条Update 修改2个字段的语句,只修该了1个字段而另外一个字段没有修改
显式事务 T-sql标明,由Begin Transaction开启事务开始,由Commit Transaction 提交事务、Rollback Transaction 回滚事务结束。
隐式事务 使用Set IMPLICIT_TRANSACTIONS ON 将将隐式事务模式打开,不用Begin Transaction开启事务,当一个事务结束,这个模式会自动启用下一个事务,只用Commit Transaction 提交事务、Rollback Transaction 回滚事务即可。

事务并发

多个事务并发时可能会造成的问题

问题名称 描述
更新丢失 多个用户同时对一个数据资源进行更新,必定会产生被覆盖的数据。
不可重复读 如果一个用户在一个事务中多次读取一条数据,而另外一个用户则同时更新这条数据,造成第一个用户多次读取数据不一致。
脏读 第一个事务读取第二个事务正在更新的数据表,如果第二个事务还没有更新完成,那么第一个事务读取的数据将是一半为更新过的,一半还没更新过的数据,这样的数据毫无意义。
幻读 第一个事务读取一个结果集后,第二个事务,对这个结果集经行增删操作,然而第一个事务中再次对这个结果集进行查询时,数据发现丢失或新增。

使用锁解决事务并发带来的问题

本专题主要关于事务的基础梳理, 为了提升阅读质量, 关于锁的详细梳理可以移步: 数据库基本知识点梳理系列 - 锁 .
数据库锁的主要分类: 表级锁, 行级锁

锁的模式和兼容性是SQL Server预先定义好的,没有任何参数或配置能够去修改它们。但是可以通过隔离级别来控制申请锁和释放锁的时机,如果应用申请的锁粒度都比较小,产生阻塞的几率就会比较小。如果一个连接会经常申请页面级、表级,甚至是数据库一级的锁资源,程序产生阻塞的可能性就会很大。

事务的隔离级别 - (原理是锁)

笔者按: 锁与事务的隔离级别孰先孰后并不重要, 重要的是二者紧密关联, 事务的隔离级别用于描述具体的业务场景, 实现事务的隔离级别的技术是使用锁.

语法

set tran isolation level <级别>

事务隔离级别名称 描述 原理
read uncommitted 事务B可以读取到事务A正在处理的数据,及时事务A还没提交事务和回滚事务, 事务B此时读到的数据可能是脏数据. 这是事务隔离的最低级别. 事务对当前被读取的数据不加锁; 事务在更新某数据时必须加行级共享锁,此时其他事务无法修改该数据, 直到事务结束释放共享锁.
read committed 事务B在事务A完成之前, 无法读取事务A正在操作的数据, 但是可以修改事务A正在操作的数据. 这个隔离级别是sql server数据库默认的隔离级别. 能够避免脏读, 但是会造成不可重复读, 什么叫做不可重复读, 即当事务A开始其事务, 读取一行数据后, 得到结果A, 等待了10s, 此时有一个并行事务B对同一个数据进行了update 操作将数据结果改为B, 10s过后, 事务A继续处理, 再次查询数据, 结果是B, 此时的现象叫做不可重复读现象. 这里要注意的是, 不可重复读指的是对同一个数据的多次读取, 获得的结果不同. , 还有一种类似的叫做幻读, 幻读的意思是在同一个查询条件下, 经过多次查询, 返回的结果集是不同的. 是因为有其他事务在对表进行insert或者delete. 所以不可重复读和幻读的案例中, 虽然都是事务A多次查询数据, 但是效果是不一样的. 所以称谓不同. 事务A对当前被读取的数据加行级共享锁, 事务B无法读取该数据, 直到事务A读取完毕后释放共享锁; 事务B可以获取此数据的排他锁(如果事务A只是获取了共享锁并为获取排他锁的情况), 事务B对数据进行修改后, 释放排他锁.
repeatable read 事务B无法读取和修改事务A正在处理的数据. 这一特点避免了不可重复读的问题, 但是无法避免幻读问题. 因为此隔离级别的读锁是加在数据级别的, 并不是加在表级别. 事务A如果读取一个范围的数据, 事务B只是对表中添加了一行数据, 恰巧也符合这个范围, 事务A再次读取这个范围的数据时, 会发现两次读取的结果不同.造成了幻读. 尚未查明,等待补充
snapshot 指定事务在开始的时候,就获得了已经提交数据的快照,因此当前事务只能看到事务开始之前对数据所做的修改。 尚未查明,等待补充
serializable 将并发转为串行, 实际上是对表进行加锁. 这样的隔离级别是最高的. 但是效率是最低的. 一般不会使用这个隔离级别的. 事务在读取数据时,必须先对其加 表级共享锁 ,直到事务结束才释放;事务在更新数据时,必须先对其加 表级排他锁 ,直到事务结束才释放。

事务隔离级别的例子

read uncommitted

  • 开启一个窗口输入以下命令1
  begin tran
  set deadlock_priority low
  update [UsageCheckHistories] set Message = 'set tran isolation level read uncommitted' where id = 11311
  waitfor delay '0:0:5'
  rollback tran
  • 开启另一个窗口输入以下命令2
set tran isolation level read uncommitted
waitfor delay '0:0:1'
select * from [WoodBlockModelKPIs].[dbo].[UsageCheckHistories] where id = 11311
waitfor delay '0:0:10'
select * from [WoodBlockModelKPIs].[dbo].[UsageCheckHistories] where id = 11311

命令1开启了一个显示事务, 并设置锁级别为low, 将id为11311的数据的message update为'set tran isolation level read uncommitted', 等待5秒后回滚事务.
命令2将事务隔离级别设置成为 read uncommitted, 等待1秒后, 读取数据id为11311的信息, 此时获取到message已经为'set tran isolation level read uncommitted', 等待10秒后, 再次读该数据, 发现数据和之前不同.

read committed

  • 开启一个窗口输入:
 --Transaction A
use woodblockmodelkpis
begin tran
waitfor delay '0:0:1'
select * from [UsageCheckHistories] where id = 11311
waitfor delay '0:0:10'
select * from [UsageCheckHistories] where id = 11311
commit tran
  • 开启另一个窗口输入
 --Transaction B
use woodblockmodelkpis
set tran isolation level read committed
waitfor delay '0:0:1'
update [UsageCheckHistories] set Message = 'update message by Transaction B.' where id = 11311

repeatable read

  • 开启一个窗口输入:
begin tran
update [UsageCheckHistories] set Message = '888' where id = 11311
waitfor delay '0:0:5'
select * from [UsageCheckHistories] where id = 11311
commit tran
waitfor delay '0:0:1'
select * from [UsageCheckHistories] where id = 11311
  • 开启另一个窗口输入:
set tran isolation level repeatable read
waitfor delay '0:0:1'
update [UsageCheckHistories] set Message = 'why we cannot see the record?' where id = 11311

命令1执行命令udpate, 延迟5秒后, 查询数据, 然后提交事务, 然后延迟1秒, 再查询数据
命令2将事务隔离级别设置成为 repeatable read, 然后延迟1秒等待命令1 update 888操作完成, 然后执行update, 此时命令执行完成的提示会等一会儿才有.
结果证明repeatable read使事务A无法修改事务B正在操作的数据.

posted @ 2019-11-22 15:20  YanyuWu  阅读(287)  评论(2编辑  收藏  举报