初识:数据库事务隔离级别
学习数据库的时候常常会接触到事务, ACID等概念,那么到底什么是数据库的事务,数据库事务又具有哪些特点,和ACID有怎样的关系,事务的隔离级别又是做什么的呢?
注:本文参考自<<从Paxos到ZooKeeper-分布式一致性原理和实践>>一书
事务
事务是由一系列对系统中数据进行访问与更新的操作所组成的一个程序执行逻辑单元,狭义上的事务特指数据库事务。
举个例子来说,张三给李四转了1000元钱,那么在数据库操作时,就要先把张三的账户减去1000元,再把李四的账户加上1000元,两部分操作放在一起,才是一个完整的转账过程,也可称之为事务。
ACID
事务具有一定的特点,也就是我们常常所说的ACID。
事务的四个特性
-
原子性(Atomicity)。事务包含的一系列操作要么全部成功,要么全部失败。一旦有一个操作失败,则将已经执行过的操作进行回滚撤销。
上述例子中,张三账户减去1000元,李四账户增加1000元两部分操作必须同时成功或失败,不能够出现张三减去了1000,但是李四并没有增加1000的情况。 -
一致性(Consistency)。这里的一致性指的是事务执行前后,数据库都处于一致性状态。
上述的例子中,无论怎么增加和减少,张三和李四两个人账户里的钱,加起来总数是不会发生改变的。 -
持久性(Durability)。一个事务一旦成功提交,那么它对数据库做出的改变是永久的。
- 隔离性(Isolation)。指并发环境中,事务之间是相互隔离,不可打扰的。
原子性和一致性,上述都已经进行了解释。持久性,顾名思义,对数据库的改变是永久的。
最后一个隔离性比较复杂,因为对事务之间进行隔离,隔离到什么程度比较合适呢?完全隔离的话,影响执行性能,完全不隔离的话,容易造成数据的不一致。
要解决这些问题,我们先来了解几个基本概念 :脏读,不可重复读,幻读
脏读:一个事务读取了另一个事务还未提交的数据。
事务A和事务B同时执行,事务A在整个执行阶段,将某个数据项的值由1加到10,然后进行事务提交。在此过程中。事务B进行一次读取,可能看到(1,2,3,4,5,6,7,8,9,10)中的任意值。
不可重复读:指在一个事务内读取表中的某一行数据,多次读取结果不同。
如果不考虑脏读的情况,假设事务A,事务B,事务C同时执行,事务A在整个执行阶段,将某个数据项的值由1加到10,然后进行事务提交。在此过程中,事务B进行读取,只可能读取到1和最终值10。之后事务B并没有结束,事务C再次将该项的值由10加到20,然后进行事务提交。在此过程中,事务B再次进行读取,只可能读取到10和最终值20。因此事务B在执行过程中有可能两次读取结果不同。
幻读:指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。
如果不考虑脏读和不可重复读的情况,假设事务A对一个表中的某个数据项设置为1,这种修改涉及到表中的全部数据行,与此同时,事务B向表中插入一条新的数据且该数据项为2,那么此时操作事务A的用户就会发现表中有一条数据的该数据项居然不是1,好像发生了幻觉。
在上述的例子上,不可重复读和幻读的区别就在于:
+ 不可重复读:在同一事务中,两次读取同一数据,得到内容不同
+ 幻读:同一事务中,用同样的操作读取两次,得到的记录数不相同
为了解决以上这些问题,保证事务与事务之间的隔离性,数据库中对事务的隔离级别做出了限制,分别在一定程度上对以上问题进行了解决。
事务隔离级别
在标准SQL规范中,定义了四个事务隔离级别。
- 读未提交。最低级别,以上问题均无法解决。
- 读已提交。可避免脏读情况发生。
- 可重复读取。可避免脏读、不可重复读情况的发生。保证在事务的处理过程中,多次读取同一个数据的时候,其值都和事务开始的时候是一致的。这也是为何下图中只能读取到1的原因。
- 串行化。最严格的事务隔离级别,要求所有事务被串行执行,不能并发执行,可避免脏读、不可重复读、幻读情况的发生。
下图中是一个很好的例子,分别解释了四种事务隔离级别下,事务B能够读取到的结果。
总的来说,事务隔离级别越高,越能保证数据的完整性和一致性,但是付出的代价却是并发的性能。