您的位置:首页 > 其它

什么是事务,事务的ACID特性

2017-11-13 12:47 1311 查看
一.什么是事务

事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。

事务的结束有两种,当事务中的所以步骤全部成功执行时,事务提交。如果其中一个步骤失败,将发生回滚操作,撤消撤消之前到事务开始时的所以操作。

突出事物原子性操作,要么都做,要么撤销回滚都不做。


二:事务的ACID特性

1. Atomicity(原子性)

原子性很容易理解,也就是说事务里的所有操作要么全部做完,要么都不做,事务成功的条件是事务里的所有操作都成功,只要有一个操作失败,整个事务就失败,需要回滚。

2. Consistency(一致性)

一致性也比较容易理解,也就是说数据库要一直处于一致的状态,事务开始前是一个一致状态,事务结束后是另一个一致状态,事务将数据库从一个一致状态转移到另一个一致状态。

3. Isolation(独立性)

从字面上来说,独立性是其中最难理解的一点,但如果结合Oracle中的undo,也就不难理解了。所谓的独立性就是指并发的事务之间不会互相影响,如果一个事务要访问的数据正在被另外一个事务修改,只要另外一个事务还未提交,它所访问的数据就不受未提交事务的影响。换句话说,一个事务的影响在该事务提交前对其它事务是不可见的。

注意:这里的Isolation跟隔离级别(Isolation Level)是无关的。

4. Durability(持久性)

持久性也不难理解,是指一旦事务提交后,它所做的修改将会永久的保存在数据库上,即使出现宕机也不会丢失。

三:理解事务ACID特性及隔离机制


理解原子性(Atomicity)

   原子性意味着数据库中的事务执行是作为原子。即不可再分,整个语句要么执行,要么不执行。

   在SQL SERVER中,每一个单独的语句都可以看作是默认包含在一个事务之中:



  所以,每一个语句本身具有原子性,要么全部执行,这么全部不执行,不会有中间状态:



  上面说了,每一条T-SQL语句都可以看作是默认被包裹在一个事务之中的,SQL
Server对于每一条单独的语句都实现了原子性,但这种原子粒度是非常小的,如果用户想要自己定义原子的大小,则需要包含在事务中来构成用户自定义的原子粒度:



 
  对于用户来说,要用事务实现的自定义原子性往往是和业务相关的,比如银行转账,从A账户减去100,在B账户增加100,如果这两个语句不能保证原子性的话,比如从A账户减去100后,服务器断电,而在B账户中却没有增加100.虽然这种情况会让银行很开心,但作为开发人员的你可不希望这种结果.而默认事务中,即使出错了也不会整个事务进行回滚。而是失败的语句抛出异常,而正确的语句成功执行。这样会破坏原子性。所以SQL SERVER给予了一些选项来保证事务的原子性.

SQL SERVER提供了两大类方式来保证自定义事务的原子性:


  1.通过SET XACT_ABORT ON来设置事务必须符合原子性

       利用设置XACT_ABORT选项设置为ON,来设置所有事务都作为一个原子处理.下面例子利用两个语句插入到数据库,可以看到开启SET XACT_ABORT
ON选项后,事务具有了原子性:




 2.按照用户设置进行回滚(ROLLBACK)

       这种方式具有更高的灵活性,开发人员可以自定义在什么情况进行ROLLBACK,利用TRY CATCH语句和@@ERROR进行判断都属于这种方式.




理解一致性(Consistency)

     一致性,即在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏

     一致性分为两个层面


    1.数据库机制层面

      数据库层面的一致性是,在一个事务执行之前和之后,数据会符合你设置的约束(唯一约束,外键约束,Check约束等)和触发器设置.这一点是由SQL
SERVER进行保证的.

 


     2.业务层面

      对于业务层面来说,一致性是保持业务的一致性.这个业务一致性需要由开发人员进行保证.很多业务方面的一致性可以通过转移到数据库机制层面进行保证.比如,产品只有两个型号,则可以转移到使用CHECK约束使某一列必须只能存这两个型号.

比如你定义一个事务,里面的操作是A,B,C三步,系统原子性能保证的是这三步要么都执行,要么都不执行,不可拆分。但是有可能你这三部破坏了数据的一致性,比如你定义了从A账户划拨200块给B账户。如果你的事务里只包含从A账户扣款,而不包含B账户加钱。那么系统保证原子性后,能保证要么从A账户都扣款成功,要么都不成功,不会出现扣了一半款之类的异常情况。但是没办法保证B账户加钱了。所以A账户扣款,B账号加钱,这是一个连续的动作,业务逻辑上要保证一致性,所以必须把两个步骤放在一个事务里,这样才能保证一致性。


理解隔离性(Isolation)

    隔离性:指的是在并发环境中,发事务之间互相影响的程度(即并发事务间数据的可见程度),当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。

    在Windows中,如果多个进程对同一个文件进行修改是不允许的,Windows通过这种方式来保证不同进程的隔离性:



而SQL
Server中,通过SQL SERVER对数据库文件进行管理,从而可以让多个进程可以同时访问数据库:




 事务间的相互影响

SQL Server利用加锁和阻塞来保证事务之间不同等级的隔离性.

    一般情况下,完全的隔离性是不现实的,完全的隔离性要求数据库同一时间只执行一条事务(即事务的执行时序列化的),这样的性能可想而知.想要理解SQL Server中对于隔离性的保障,首先要了解事务之间是如何干扰的.但是同一个事务内的数据库操作的数据是透明的,即同一个事务内的一个数据库操作可以读取另一个未提交(commit)的数据库操作。

    事务之间的互相影响的情况分为几种,分别为:脏读(Dirty Read),不可重复读(Non-Repeatable Reads),幻读(Phantom
Reads)


脏读

    脏读意味着一个事务读取了另一个事务未提交的数据,而这个数据是有可能回滚的:



下面来看一个例子:

     两个事务,事务A插入一条数据,但未提交,事务B在此期间进行了读取,读取到了事务A未提交的数据,造成脏读




不可重复读(Unrepeatable Read)

     不可重复读意味着,在数据库访问中,一个事务先后读取同一条记录,但两次读取的数据不同,我们称之为不可重复读。也就是说,这个事务在两次读取之间该数据被其它事务所修改。就是说事务A中两处读取数据,第一次读时是100,然后事务B把值改成了200,事务A再读一次,结果就发现值变了,造成A事务数据混乱。




幻读(phantom read)

     幻读,和不可重复读相似,也是同一个事务中多次读不一致的问题。但是不可重复读的不一致是因为它所要取的数据集被改变了而幻读所要读的数据不一致却不是他所要读的数据改变,而是它的条件数据集改变。比如:Select
id where name="ppgogo*",第一次读去了6个符合条件的id,第二次读时,由于事务B把第一个贴的名字由"dd"改成了“ppgogo9”,结果取出来7个数据,就好象发生了幻觉一样.




事务间相互影响解决方案--设置隔离级别

 为了避免上述几种事务之间的影响,通过设置不同的隔离等级来进行不同程度的避免。因为高的隔离等级意味着更多的锁,从而牺牲性能.所以这个选项开放给了用户根据具体的需求进行设置。不过默认的隔离等级Read
Commited符合了99%的实际需求.事务隔离级别的出现,是为了使你在性能与数据的有效性之间做一个平衡,不是说级别越高越好,只有合适才是最好的。

    隔离事务之间的影响是通过锁来实现的,这个概念比较繁杂,所以本文不会详细对这个概念进行讲解.通过阻塞来阻止上述效果

    SQL Server提供了5种选项来避免不同级别的事务之间的影响

    隔离等级由低到高分别为


   
1)未提交读(Read Uncommitted)(最高的性能,但可能出现脏读,不可重复读,幻读):
SELECT语句以非锁定方式被执行.


   
2)提交读(Read Committed)(可能出现不可重复读,幻读):只能读取到已经提交的数据。


   3)可重复读(Repeated Read)(可能出现幻读):通过加共享记录锁实现,即 SELECT .... FOR UPDATE


   
4)串行读(Serializable)(最低的性能,Range锁会导致并发下降):完全的串行化读,所有SELECT语句都被隐式的转换成SELECT
... LOCK IN SHARE MODE,即读取使用表级共享锁,读写相互都会阻塞。隔离级别最高。

  5)  SNOPSHOT(这个是通过在tempDB中创建一个额外的副本来避免脏读,不可重复读,会给tempDB造成额外负担,因为不是标准ANSI SQL标准,不详细讨论)

 隔离级别对比表:



    总之,不同的隔离级别是通过加不同的锁,造成阻塞来实现的,来看一个例子:

    SQL SERVER通过阻塞来阻止脏读,所以保持独立性会以付出性能作为代价:




理解持久性(Durability)

     持久性,意味着在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。

     即使出现了任何事故比如断电等,事务一旦提交,则持久化保存在数据库中.

     SQL SERVER通过write-ahead transaction log来保证持久性。write-ahead transaction log的意思是,事务中对数据库的改变在写入到数据库之前,首先写入到事务日志中。而事务日志是按照顺序排号的(LSN)。当数据库崩溃或者服务器断点时,重启动SQL SERVER,SQL SERVER首先会检查日志顺序号,将本应对数据库做更改而未做的部分持久化到数据库,从而保证了持久性.


总结:

事务的(ACID)特性是由关系数据库管理系统(RDBMS,数据库系统)来实现的。数据库管理系统采用日志来保证事务的原子性、一致性和持久性。日志记录了事务对数据库所做的更新,如果某个事务在执行过程中发生错误,就可以根据日志,撤销事务对数据库已做的更新,使数据库退回到执行事务前的初始状态。

数据库管理系统采用锁机制来实现事务的隔离性。当多个事务同时更新数据库中相同的数据时,只允许持有锁的事务能更新该数据,其他事务必须等待,直到前一个事务释放了锁,其他事务才有机会更新该数据。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: