前言
事务是数据库中非常重要的概念,本章主要介绍事务的基本概念、事务的四大特性和MySQL提供了四种隔离级别。同时在隔离级别中引入了MVCC的概念。
一、事务
事务:是一组数据库操作序列,可以使一组操作要么全部成功,要么全部失败。
举例:张三支付宝转账100给李四,张三的账户要扣除100,李四的账户要增加100,这两个操作必须同时成功或者同时失败,这样就能保证转账的正确性。
事务的4大特性,通常称ACID性质。
1.原子性:当前事务的操作要么同时成功,要么同时失败。原子性由undo log日志来保证,因为undo log记载着数据修改前的信息。
2.一致性:是使用事务的目的,而隔离性、原子性、持久性均是为了保障一致性的手段。例如:在发生的过程中,出现了异常情况,此时你就得回滚事务,而不是强行提交事务来导致数据不一致。
3.隔离性:一个事务的执行不能被其他事务干扰,即一个事务内部的操作对其他并发事务是隔离的。
MySQL提供了四种隔离级别供我们使用,底层实现是锁
4.持久性:一旦提交了事务,它对数据库的改变就应该是永久性的。
持久性由redo log保证,redo log记载着这次在某个页上做了什么修改。即便MySQL在中途挂了,我们还可以根据redo log来对数据进行恢复。
二、读未提交
介绍完了事务的基本概念和四大特性,接下来来介绍MySQL提供了四种隔离级别(只有 InnoDB 支持事务,MyISAM不支持事务,所以这里说的事务隔离级别是指 InnoDB 下的事务隔离级别)。
首先来说下read uncommit(读未提交):
张三给李四转账100块钱,执行了转账语句但未提交事务,李四读取账户余额发现钱变多了,打电话给张三说钱到账了,然后张三回滚了事务,等李四在查看余额时发现钱没有变多。
专业术语:一个事务读取了另外一个事务正在更新但还没有提交的数据,也称脏读。
总结:读未提交隔离级别最低,其他操作可以读取未提交的数据,产生脏读问题。
三、读已提交
在MySQL InnoDB引擎层面,提供了解决方案,叫做MVCC(Multi-Version Concurrency Control)多版本并发控制。
MVCC通过生成数据快照(Snapshot),并用这个快照来提供一定级别(语句级或事务级)的一致性读取。
针对于 read commit (读已提交) 隔离级别,它生成的就是语句级快照。
read commit (读已提交) 隔离级别解决了脏读。思想很简单:在读取的时候生成一个版本号,等到其他事务commit了之后,才会读取最新已commit的版本号数据。
例如:李四读取账户余额50元(生成版本号),然后张三向李四转账了100元但还没有提交事务(版本号没变),李四再去读的时候余额还是50元,因为版本号没有变,读取的还是之前版本号的数据。只有张三提交事务之后生成新的版本号,李四才能读到新版本的数据。
通过版本的概念,这样就解决了脏读的问题,而版本其实就是对应数据的快照。
读已提交解决了脏读,但也会有其他并发的问题:不可重复读。
不可重复读:同一个事务中两次读取的数据不一致。
四、可重复读和串行化
repeatable read (可重复复读,MySql默认的隔离级别)是事务级别的快照,每次读取的都是当前事务的版本,即使当前数据被其他事务修改了(commit),也只会读取当前事务版本的数据。
幻读:事务A按照一定条件读取某些数据记录之后,事务B插入或者删除了一些记录,事务A再次按照相同的条件读取数据时,发现多了或者少了一些记录。
InnoDB引擎下的的repeatable read (可重复复读)隔离级别下,快照读MVCC影响下,已经解决了幻读的问题(因为它是读历史版本的数据)
串行化:事务与事务之间执行是串行的,一个事务执行完了另外一个事务才能执行,效率最低,但不存在产生脏读、重复读、幻读的问题。
总结
频繁加锁会导致数据库性能低下,引入了MVCC多版本控制来实现读写不阻塞,提高数据库性能。MVCC原理即通过read view 以及undo log来实现,在以后的章节会介绍到。