提到MySQL的事务,我相信对MySQL有了解的同窗都能聊上几句,不管是面试求职,仍是平常开发,MySQL的事务都跟咱们息息相关。html
而事务的ACID(即原子性Atomicity、一致性Consistency、隔离性Isolation、持久性Durability)能够说涵盖了事务的所有知识点,因此,咱们不只要知道ACID是什么,还要了解ACID背后的实现,只有这样,不管在平常开发仍是面试求职,都能无往而不利。git
上一篇 跟面试官侃半小时MySQL事务隔离性,从基本概念深刻到实现 主要围绕“隔离性”展开,从基本概念,到隔离性的实现,最后以一个实战案例进行融会贯通。本篇内容将介绍原子性、一致性、持久性相关实现,因为这部份内容可能不少人会相对陌生,由于平常业务开发可能不太会去接触和深究,可是了解完后,你对MySQL会有更深入的认识。github
整个事务是不可分割的最小单位,事务中任何一个语句执行失败,全部已经执行成功的语句也要回滚,整个数据库状态要恢复到执行事务前到状态。面试
事务将数据库从一种状态转变为下一种一致的状态。在事务的先后,数据库的完整性约束没有被破坏。(事务的acid不是彻底正交的,尤为是一致性,可能跟原子性、隔离性都有必定关系,后面会看到)数据库
事务一旦提交,那么就是永久性的,不会由于宕机等故障致使数据丢失(外力影响不保证,好比磁盘损害)。持久性是保证了数据库的高可靠性(High Reliability),而不是高可用性(Hign Availability)。高可用性并不能经过事务来保证。缓存
MySQL的innoDB存储引擎,使用Redo log保证了事务的持久性。并发
当事务提交时,必须先将事务的全部日志写入日志文件进行持久化,就是咱们常说的WAL(write ahead log)机制(这个技术是保障持久性的关键技术,在HBase中也扮演重要角色,有兴趣的同窗能够参考我以前关于HBase的文章)。这样才能保证断电或宕机等状况发生后,已提交的事务不会丢失,这个能力称为 crash-safe。mvc
下面深刻聊一聊redo log的机制,给你们更深入的理解。分布式
Redo log包括两部分,重作日志缓冲(redo log buffer)和重作日志文件(redo log file),前者是易失的缓存,后者是持久化的文件。3d
举一个事务的例子:
这个事务的写入过程实际拆解以下:
innodb缓冲池的概念本文就不展开说明了,之后有机会能够展开说一下。
重点关注在这个事务提交前,将 redo log 的写入拆成了两个步骤,prepare 和 commit,这就是"两阶段提交”。
为何要采用两阶段提交呢?
实际上,两阶段提交是分布式系统经常使用的机制。MySQL使用了两阶段提交后,也是为了保证事务的持久性。Redo log 和bingo 有一个共同的数据字段,叫 XID,崩溃恢复的时候,会按顺序扫描 redo log。
这个事务要往两个表中插入记录,插入数据的过程当中,生成的日志都得先写入redo log buffer ,等到commit的时候,才真正把日志写到 redo log 文件。(固然,这里不绝对,由于redo log buffer可能由于其余缘由被迫刷新到redo log)。
而为了确保每第二天志都能写入日志文件,在每次将重作日志缓冲 写入 重作日志文件 后,InnoDB存储引擎都须要调用一次fsync操做,确保写入了磁盘。
对于redo log的持久化,能够以下图所示。
1)先写入redo log buffer,在蓝色区域。
2)写入redo log file,可是尚未fsync,这时候是处于黄色的位置,处于系统缓存。
3)调用fsync,真正写入磁盘。
为了控制 redo log 的写入策略,InnoDB 提供了 innodb_flush_log_at_trx_commit 参数,它有三种可能取值:
binlog的写入和redo log同样,也是包括bingo cache和bingo file,一样跟上面的三色层次相似(固然,binlog是server层的,不是存储引擎层的),包括log buffer、文件系统page cache、hard disk。
写入page cache 和 fsync到disk 的时机,是由参数 sync_binlog 控制的:
一般咱们说 MySQL 的“双 1”配置,指的就是 sync_binlog 和 innodb_flush_log_at_trx_commit 都设置成 1。也就是说,一个事务完整提交前,须要等待两次刷盘,一次是 redo log(prepare 阶段),一次是 binlog。
特别须要区分的是,redo log和binlog的不一样。这也是常常在面试中可能会问到的两种日志的差别。
注意有这么几点:
redo log是innodb的存储引擎产生的,而binlog是数据库的server层实现的。换句话说,若是你使用MySQL,换其余存储引擎,那么可能没有redo log,可是仍是会有binlog。
binlog是一种逻辑日志,记录对应的SQL语句,而redo log记录了物理日志,是针对每一个数据页的修改。
binlog只有在事务提交后完成一次写入,对于一个事物而言,在binlog中只有一条记录。而redo log在事务进行中不断被写入,并且是并发写入的,不是顺序写入的。
redo log 是循环写的,空间固定会用完;binlog 是能够追加写入的。“追加写”是指 binlog 文件写到必定大小后会切换到下一个,并不会覆盖之前的日志。
Undo log保证了事务的原子性。
在对数据库进行修改时,innoDB引擎除了会产生redo log,还会产生undo log。InnoDB实现回滚,靠的是undo log:当事务对数据库进行修改时,InnoDB会生成对应的undo log;若是事务执行失败致使事务须要回滚,就利用undo log中的信息将数据回滚到修改以前的样子。
有人认为undo log是redo log的逆过程,实际上是不对的。两个日志文件其实都能看做是一种对数据的恢复操做,redo log恢复事务致使的数据页的修改,而undo log可以恢复数据记录到某个特定的版本。
因此redo log是一种物理日志(数据页的修改),而undo log是一种逻辑日志(数据记录)。
undo log还要另一个重要做用,就是用于mvcc中,进行多版本控制,也就是实现事务隔离性的基础,当用户读取一行记录时,若是这个记录已接被其余事务占用,那么当前事务就能够经过undo读取以前的行版本信息,用来实现非锁定读取,就是“快照读”。(事务隔离性的问题,能够看我上一篇文章 跟面试官侃半小时MySQL事务隔离性,从基本概念深刻到实现 )。
就像一开始在定义的时候介绍的,事务的ACID性质不是彻底正交的,尤为是一致性,咱们能够认为原子性、持久性和隔离性都是为了实现事务的一致性。
固然,这里的一致性是指数据库层面的事务一致性。
若是说你在应用层面作一个操做,给转帐者扣钱,没给接收者加钱,那么这个不一致跟事务的不一致是没有关系的,须要开发人员本身作业务逻辑一致性的保证。
看到这里了,原创不易,点个关注、点个赞吧,你最好看了~
知识碎片从新梳理,构建Java知识图谱:https://github.com/saigu/JavaKnowledgeGraph(历史文章查阅很是方便)
扫码关注个人公众号“阿丸笔记”,第一时间获取最新更新。同时能够免费获取海量Java技术栈电子书、各个大厂面试题。