数据库事务的ACID原则

百度百科
ACID原则是数据库事务正常执行的四个,分别指原子性、一致性独立性持久性
 
先说几个概念
1、事务

事务就是DBMS当中用户程序的任何一次执行,事务是DBMS能看到的基本修改单元。

事务就是一组原子性的SQL操作或者一个独立的工作单元,事务内的语句要么全部执行成功,要么全部执行失败。

2、回滚

回滚实际上是一个比较高层抽象的概念,大多数DB在实现事务时,是在事务操作的数据快照上进行的(比如,MVCC),并不修改实际的数据,如果有错并不会提交,所以很自然的支持回滚。而在其他支持简单事务的系统中,不会在快照上更新,而直接操作实际数据。可以先预演一边所有要执行的操作,如果失败则这些操作不会被执行,通过这种方式很简单的实现了原子性。

 

ACID原则

事务是指对系统进行的一组操作,为了保证系统的完整性,事务需要具有ACID特性,具体如下:
1、原子性(Atomic)
一个事务包含多个操作,这些操作要么全部执行,要么全都不执行。实现事务的原子性,要支持回滚操作,在某个操作失败后,回滚到事务执行之前的状态。

2、 一致性(Consistency)
一致性是指[事务操作]对数据可见性的约束,保证在一个事务中的多次操作的数据中间状态对其他事务不可见的。事务的一致性决定了一个系统设计和实现的复杂度,也导致了事务的不同隔离级别。例如:

张三给李四转账100元。事务要做的是从张三账户上减掉100元,李四账户上加上100元。一致性的含义是其他事务要么看到张三还没有给李四转账的状态,要么张三已经成功转账给李四的状态,而对于张三少了100元,李四还没加上100元这个中间状态是不可见的。

      2.1、事务可以不同程度的一致性:
      a、强一致性:读操作可以立即读到提交的更新操作。

      b、弱一致性:提交的更新操作,不一定立即会被读操作读到,此种情况会存在一个不一致窗口期(已经开始更新,但没有完成更新的那一段时间),指的是读操作可以读到最新值的一段时间。
      c、最终一致性:是弱一致性的特例。事务更新一份数据,最终一致性保证在没有其他事务更新同样的值的话,最终所有的事务都会读到之前事务更新的最新值。如果没有错误发生,不一致窗口的大小依赖于:通信延迟,系统负载等。
其他一致性变体还有:
      d、单调一致性:如果一个进程已经读到一个值,那么后续不会读到更早的值。
      e、会话一致性:保证客户端和服务器交互的会话过程中,读操作可以读到更新操作后的最新值。

3、 隔离性(或叫做独立性)(Isolation)
并发事务之间互相影响的程度,比如一个事务会不会读取到另一个未提交的事务修改的数据。

      3.1、在事务并发操作时,可能出现的问题有:
      a脏读:

事务A修改了一个数据,但未提交,事务B读到了事务A未提交的更新结果,如果事务A提交失败,事务B读到的就是脏数据。
      b不可重复读:

在同一个事务中,对于同一份数据读取到的结果不一致。比如,事务B在事务A提交前读到的结果,和提交后读到的结果可能不同。不可重复读出现的原因就是事务并发修改记录,要避免这种情况,最简单的方法就是对要修改的记录加锁,这回导致锁竞争加剧,影响性能。另一种方法是通过MVCC可以在无锁的情况下,避免不可重复读。
      c幻读:

在同一个事务中,同一个查询多次返回的结果不一致。事务A新增了一条记录,事务B在事务A提交前后各执行了一次查询操作,发现后一次比前一次多了一条记录。幻读是由于并发事务增加记录导致的,这个不能像不可重复读通过记录加锁解决,因为对于新增的记录根本无法加锁。需要将事务串行化,才能避免幻读。

      d更新丢失:

如下伪代码A:

Begin Transaction
         var _money=Select money  from  tb_user where  id=1;
	 update tb_user set _money-100 where id=1
Commit Transaction

如下伪代码B:

Begin Transaction
         var _money=Select money  from  tb_user where  id=1;
	 update tb_user set _money+100 where id=1
Commit Transaction

按照道理来说,AB两个事务同时执行完毕,id=1的用户金额应该不变。但是结果却是用户的账号金额多一百或少一百,就是说,其中一个update没效果。

      3.2、事务的隔离级别从低到高有:
      aRead Uncommitted:

最低的隔离级别,什么都不需要做,一个事务可以读到另一个事务未提交的结果。所有的并发事务问题都会发生。
      bRead Committed:

只有在事务提交后,其更新结果才会被其他事务看见。可以解决脏读问题。
      cRepeated Read:

在一个事务中,对于同一份数据的读取结果总是相同的,无论是否有其他事务对这份数据进行操作,以及这个事务是否提交。可以解决脏读、不可重复读。能够解决脏读、不可重复度、幻读、但丢失更新解决不了。
      dSerialization:

事务串行化执行,隔离级别最高,牺牲了系统的并发性。可以解决并发事务的所有问题。通常,在工程实践中,为了性能的考虑会对隔离性进行折中。所有问题都可以解决。可解决脏读、不可重复读、幻读、丢失更新。但性能要做出很大牺牲。

4、持久性(Durability)

事务提交后,对系统的影响是永久的。

 

单表事务锁

因为事务隔离级别的可以自行设置,咱们可以通过设置各种不同级别的事务隔离级别来控制“并发问题”。但是现实情况是,往往数据操作具有高并发的特性,他要求我们必须要考虑性能问题。在“高性能”这样的前提下,我们的事务隔离级别一般情况都是“RR”级别。RR级别不能解决丢失更新的问题,为了解决这个问题,“锁”就出现了。

数据库“单表事务”的锁,从宏观分类一般分为两类:悲观锁、乐观锁。从功能上来说就是:读写锁、只读锁。其实“悲观锁和读写锁”、“乐观锁和只读锁”是同一个东西从不同角度的描述。待续....

 

分布式事务锁

待续....

添加评论

Loading