本文,先从数据库事务的四大特性开始讲起,过渡到四种主要的事务隔离级别。
首先,ACID四种特性。
1:原子性(Atomicity)
原子性是指,一个事务包含的所有操作,要么全部成功,要么就全部失败回滚;通俗来说,就是事务的操作,要不然就是每一步都产生了相应的影响,最后对数据库造成的改动,是事务中各个操作进行的最后结果;否则,就不能对数据库产生任何影响。
2:一致性(Consistency)
一致性,就是说提交的事务执行过后,必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说,一个事务执行之前和执行之后都必须处于一致性状态。
3:隔离性(Isolation)
隔离性,就是当多个用户并发访问数据库时,比如操作同一张表时候,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
而下文讨论的隔离性级别,就是针对此的。
4:持久性(Durability)
持久性,是指一个事务一旦被提交了,那么,该事务对于数据库中数据做出的改变就是永久性的,这个永久性,意味着即便是数据库系统遇到故障的情况下,也不会丢失已经提交的事务的操作。
那么,事务的隔离性级别作何理解呢?
简单地说,隔离性是多用户并发操作数据库时候,需要对各个事务操作执行做出的隔离级别,根据效果的不同,大致分为四种。
大家可以想想,既然是并行操作,那么,隔离性级别最高的是什么呢?当然是串行化,这就是事务隔离性级别最高的。
1:事务隔离性级别最高的:串行化
在串行化的操作下,每个事务的操作都是按照时间先后来进行的,自然不会产生相互的任何影响,但同时,因为这个问题,也会导致性能很低,毕竟,对于正式运行的系统来说,对于数据库产生的操作很多都是并行的。
所以,其优点就是简单、安全,缺点,就是效率低,这是它唯一的副作用。
既然有隔离性级别最高的,那肯定也有隔离性级别最低的,如果隔离性级别最低,会发生什么呢?
2:事务隔离性系别最低:读未提交(Read Uncommited)
从字面意思就很好理解,在这种隔离级别下,假如有事务A和事务B 并行操作了,那么,即使B事务进行的操作还未曾提交,事务A都会读取到中途的数据。
网上都是这么解释的。
但是,仔细想想这句话,其实不合理的,如果事务未曾提交,那怎么会对数据库产生修改呢?所以,应该是事务A已经提交,还未曾结束的这个中间阶段,其他事务比如事务B能够读取到事务A运行的中间结果。
我们称呼这种现象,叫做脏读,读未提交会产生脏读。
3:事务隔离性级别仅高于读未提交的:读已提交(Read Commited)
从解决脏读的层面我们去思考,如果不想产生脏读这种现象,其实就相当于如果有业务在写入的时候,不能进行任何的读或者写的事务,这样,就可以解决脏读的问题。
但是,这样也不是万全的。
比如说,我们再一个事务中,前后两次读取数据,会发现同一条数据可能产生不同的结果,为什么会这样呢?
看看我们再读已提交的级别中,到底做了什么,我们实际就是:进行写操作时候,拒绝所有的读写操作;但是进行读操作的时候,并未约束任何的读写操作。
毫无疑问,在读操作的时候,别的事务还能对需要读取的数据产生影响,造成不可重复读的现象。
4:事务隔离性级别仅次于串行,但高于读已提交的:可重复读。
很好记,可重复就是针对不可重复读进行优化的。
可重复读,在读事务的时候,阻止了其他任何业务进行的读和写操作;但是,其并没有对插入操作进行加锁,其实际上针对的只是update和delete操作,在这种情况下,一个事务第一次可能读取到了十条记录,但第二次就读到了十一条记录,这就是幻读。
总结一下:隔离级别从高到低。
- 串行化:所有事务一个接一个执行,不会产生幻读、不可重复读、脏读。
- 可重复读:无法对插入操作上锁,所以会产生幻读,阻止了不可重复读和脏读;这里必须注意,幻读针对的是读取多条记录的情况才能看见这种现象。
- 不可重复读:第一次读取和第二次读取会读取到不同的结果,原因在于读操作并不屏蔽其他的读写操作造成的,所以叫做不可重复读;会导致幻读、不可重复读等问题。
- 读未提交:这种情况下,另外一个事务还没有完全执行完毕的中间结果,都会被其它事务看到,会导致幻读、不可重复读、脏读等各种问题。
这里,额外提一句,常用的MySQL事务级别是可重复读,但是具体的策略实现结果,好像与本文提到的可重复读效果不完全一样。