文章目录
事务介绍
事务是由存储引擎实现的,实现了事务的存储引擎不多,我们重点放在InnoDB上。
事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。
事务用来管理 DDL、DML、DCL 操作,比如 insert,update,delete 语句,默认是自动提交的。
数据库事务具有ACID四大特性。ACID是以下4个词的缩写:
原子性(atomicity) :事务最小工作单元,要么全成功,要么全失败
一致性(consistency): 事务开始和结束后,数据库的完整性不会被破坏
隔离性(isolation) :不同事务之间互不影响,四种隔离级别为RU(读未提交)、RC(读已提交)、RR(可重复读)、SERIALIZABLE (串行化)
持久性(durability) :事务提交后,对数据的修改是永久性的,即使系统故障也不会丢失
下面我们就来详细讲解一下事务的ACID特性的具体实现原理。总结来说,事务的隔离性由多版本控制机制和锁实现,而原子性、一致性和持久性通过InnoDB的redo log、undo log和ForceLog at Commit机制来实现。
数据库事务的整个流程,如下图所示:
事务进行过程中,每次sql语句执行,都会记录undo log和redo log,然后更新数据形成脏页,然后redo log按照时间或者空间等条件进行落盘,undo log和脏页按照checkpoint进行落盘,落盘后相应的redo log就可以删除了。此时,事务还未COMMIT,如果发生崩溃,则首先检查checkpoint记录,使用相应的redo log进行数据和undo log的恢复,然后查看undo log的状态发现事务尚未提交,然后就使用undo log进行事务回滚。事务执行COMMIT操作时,会将本事务相关的所有redo log都进行落盘,只有所有redo log落盘成功,才算COMMIT成功。然后内存中的数据脏页继续按照checkpoint进行落盘。如果此时发生了崩溃,则只使用redo log恢复数据。
原子性,持久性和一致性
原子性,持久性和一致性主要是通过redo log、undo log和Force Log at Commit机制机制来完成的。
redo log用于在崩溃时恢复数据;
undo log用于对事务的影响进行撤销,也可以用于多版本控制
Force Log at Commit机制保证事务提交后redo log日志都已经持久化
那么原子性,持久性和一致性是怎么通过这些东西实现的?(个人理解)
原子性:
定义:事务最小工作单元,要么全成功,要么全失败
个人理解:
事务提交时:要么成功;失败后利用undo log rollback,回滚到之间的状态;
当数据库崩溃时:从redo log中取出未落盘的脏页数据,写入到磁盘,;针对未成功提交的事务进行UNDO操作,保证这个事务要么全成功,要么全失败
持久性:
定义:事务提交后,对数据的修改是永久性的,即使系统故障也不会丢失
个人理解:
事务提交后,通过Force Log at Commit机制保证事务提交后redo log日志都已经持久化,然后redo log 落盘(参考Mysql架构中的redo log落盘机制),写入磁盘。
一致性(consistency):
定义:事务开始和结束后,数据库的完整性不会被破坏
个人理解:
原子性,持久性,隔离性都是为了最终保证一致性。
隔离性(isolation) : 定义:不同事务之间互不影响
mysql有4中不同的隔离级别:
- Read uncommitted (读未提交):最低级别,任何情况都无法保证
- Read committed (RC,读已提交):可避免脏读的发生
- Repeatable read (RR,可重复读):可避免脏读、不可重复读的发生(InnoDB的RR还可以解决幻读,主要原因是Next-Key锁,只有RR才能使用Next-Key锁)
- Serializable (串行化):可避免脏读、不可重复读、幻读的发生
脏读、不可重复读、幻读产可参考漫画讲解脏读、不可重复读、幻读
MVCC(多版本并发控制)解决了在REPEATABLE READ和READ COMMITTED两个隔离级别下读同一行和写同一行的两个事务的并发。它是怎么实现的?
InnoDB MVCC实现原理
InnoDB 中 MVCC 的实现方式为:
当执行 insert update delete 时,会写 undo log(undo log存在回滚段中),这时还未提交;然后redo log页每行数据数据中有2个隐藏字段:事务ID和回滚指针,每次对数据操作时都会记录当前事务版本号和上一个版本的地址指针。
如果发生rollback,数据回滚到上一个版本,在该事务中写的undo log直接删除;
如果提交,针对insert的数据,完成数据插入包括当前事务版本号和上个版本的回滚指针后,该事务中写的undo log直接删除;
针对 update delete操作,也需要插入一条更新后的数据(update原来的数据产生的新数据),或者在原数据上加一个删除标志,因为会对已经存在的记录产生影响,为了提供 MVCC机制,因此 update undo log 不能在事务提交时就进行删除,而是将事务提交时放到入 history list 上,等待 purge 线程进行最后的删除操作。
如下面的例子:
那读取的时候怎么读呢?
InnoDB MVCC读操作可以分成两类:快照读 (snapshot read)与当前读 (current read)
- 快照读,读取的是记录的可见版本 (有可能是历史版本),不用加锁(select 操作)
- 当前读,读取的是记录的最新版本,并且当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录。
如果读取的行正在执行DELETE或UPDATE操作,这时读取操作不会因此去等待行上锁的释放。相反地,InnoDB会去读取行的一个最新可见快照
这个叫一致性非锁定读