数据库事务正确执行的4个基础要素是原子性、一致性、隔离性和持久性。
- 原子性:整个事务中的所有操作,要么全部完成,要么全部不完成,不肯停止在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没被执行过一样。
- 一致性:指一个事务可以改变封装状态(除非他是一个只读的)。事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少。
- 隔离性:指两个事务之间的隔离程度。
- 持久性:在事务完成以后,该事务对数据库所做的更改便持久保存在数据库中,并不会被回滚。
这里隔离性笔者没有过多的讲解,因为接下来我会详细的说一说隔离性。
隔离性涉及了多个事务并发的状态。首先多个事务并发会产生数据库丢失更新的问题,其次隔离性又分为多个层级。
这个理解起来比较抽象,我们用一个场景来说明:一张银行卡有2个用户都在使用。我们称之为用户1,用户2.
首先我们来说一说丢失更新,一般而言存在两类数据更新。
其一:
- T1时刻:用户1查询余额还有10000元
- T2时刻:用户2 也查询余额还有10000元
- T3时刻:用户2买衣服用了1000元
- T4时刻:用户1吃饭用了1000元
- T5时刻:用户1提交事务,所以银行卡显示余额还有9000元
- T6时刻:用户2又不想买衣服了,所以取消了支付,因此回滚事务到T2时刻,所以银行卡余额显示10000元
其二:
- T1时刻:用户1查询余额还有10000元
- T2时刻:用户2 也查询余额还有10000元
- T3时刻:用户2买衣服用了1000元
- T4时刻:用户1吃饭用了1000元
- T5时刻:用户1提交事务,所以银行卡显示余额还有9000元
- T6时刻:用户2也提交事务,但是根据之前余额还有10000元,所以现在银行卡余额显示9000元
上述过程存在两笔交易,一笔是用户1吃饭,一笔是用户2买衣服 ,但是两者都提交了事务,由于在不同的事务中,无法探知其他事务的操作,导致数据显示不对。
为了解决上述的数据丢失问题,数据库标准规范中定义了事物之间的隔离级别。隔离级别定义为4层,分别是:脏读、读/写提交、可重复读和序列化。
- 脏读:最低的隔离级别,其含义是允许一个事务读取另一个事务中未提交的数据。但是脏读也有问题,如果它读取了另一个事务中未提交的数据进行操作,但是另一个操作发生了回滚的话,数据又会出错。
- 读/写提交:一个事务只能读取另一个事务已经提交的数据,这个隔离级别可以很好的克服脏读,但是又会产生新的问题——不可重复读。
- 可重复读:这个隔离级别是为了克服不可重复读带来的错误。但是可重复读是针对数据库同一条记录而言的。而有时候数据库需要同时对多条记录进行读/写,此时就会出现幻读。
- 序列化:这个隔离级别是为了克服幻读,它可以让SQL按照顺序读/写的方式,消除数据库事务之间并发产生数据不一样的问题。
从脏读到序列化,虽然数据库存在的问题越来越少,但是系统性能也是直线下降;设置高的级别,比如序列化,会严重压制并发,从而引发大量的线程挂起,知道获得锁才能进一步操作,而恢复时就需要大量的等待时间。因此在选择隔离级别的出发点在于两点:性能和数据一致性。