首先,咱们都知道数据库事务的四大特性:mysql
用于处理数据,page是Innodb存储的最基本结构,也是Innodb磁盘管理的最小单位,数据变动的时候,缓存里的数据页和磁盘的数据页不一致,该数据页被称为脏页。sql
redo log保证数据的可靠性,存储数据以前首先要存储变动数据的日志,一旦系统出现故障能够从日志中找。Innodb采用了Write Ahead Log(预写日志)策略,就是当事务提交时,先写重作日志,而后再择时将脏页落盘。Force Log at Commit机制保证事务的持久性,事务提交的时候,必须先将该事务的全部日志记录落盘,在每次将重作日志缓冲写入到重作日志后,必须调用一次fsync操做,将缓冲日志从文件系统缓存真正写入磁盘数据库
redo log落盘机制有三种,能够经过参数innodb_flush_log_at_trx_commit进行设置,默认值为1缓存
上图是数据落盘和redo log日志落盘的机制,咱们说下这个数据落盘时候的双写机制和检查点bash
Innodb经过双写机制保证数据的可靠性。Double Write由两个部分构成,一个是内存中的double write buffer,大小为2MB。第二部分是物理磁盘,共享表空间中的128个连续的页,大小也是2MB。数据结构
在对缓冲池中的脏页进行刷新的时候,并非直接将数据写到磁盘。首先,经过mencpy函数将脏页复制到内存中的double write buffer区,而后将double write buffer中的数据分两次,每次1MB,将数据顺序地写入到共享表空间的磁盘上,而后立刻调用fsnyc真正地落盘。完成以后,再讲double write buffer中的数据写入到各个表空间中。若是操做系统在写入磁盘的过程当中崩溃了,能够从共享表空间中找到数据副本恢复数据。架构
表示将脏页写入到磁盘的时机:并发
当数据库崩溃以后会利用redo log将尚未及时落盘的数据恢复,从新写入磁盘。在恢复的过程当中还须要去回滚尚未提交的事务,回滚事务就须要利用到undo log,而undo log的完整性和可靠性须要redo log保证,所以恢复数据库的时候,首先将redo log里面数据恢复,而后作undo log的回滚。异步
数据和回滚日志的每条记录都会有三个额外的字段:函数
undo log并无使用额外的文件存储,而是存放在共享表空间的回滚段中。undo log的产生也能够当作是数据库的数据,所以,undo log 也会写入到redo log中,也就是undo log 的产生会伴随着redo log的产生,undo log的完整性和可靠性也是由redo log来保证
原子性,一致性和持久性主要是经过redo log,undo log和Force Log at commit机制来完成的。redo log用在数据库崩溃的时候,从redo log恢复数据,undo log用于对事务的影响进行撤销,也就是回滚,还用于多咱们后面会讲到的多版本控制。Force log at commit保证事务提交以后可以持久化到redo log。
针对上面的四个问题,事务之间有不一样的隔离级去解决上面的问题
在Innodb中,使用的是MVCC来保证事务之间的隔离,MVCC使得广泛的select语句不会加锁,提升数据库的并发处理能力
MVCC:多版本并发控制,是一种并发控制的方法,用来实现数据库的事务性。
在MVCC中,读操做能够分红两种
在Innodb中简单的select语句属于快照读,不加锁,读的是历史版本。而其余的增删改语句属于当前读,须要加锁,读的是当前版本。
一致性非锁定读:Innodb引擎经过MVCC读取当前数据库中行数据的方式。若是读取的是正在执行删除或者更新操做的记录,那么本次读操做不会所以阻塞去等待锁的释放。而会去读取该行的一个最新的可见快照。
MVCC的实现主要依赖的是undo log和read view(事务链表)
咱们知道undo log的行记录中有三个隐藏字段:分别对应该行的rowid、事务号db_trx_id和回滚指针db_roll_ptr,其中db_trx_id表示最近修改的事务的id,db_roll_ptr指向回滚段中的undo log。 根据行为的不一样,undo log分为两种
事务链表,mysql中的事务在开始到提交的阶段都会被保持在一个叫trx_sys的事务链表中啊,事务链表中保持的都是还未提交的事务,事务一旦被提交,就会从链表中删除该事务。
说道这里,你们就应该清楚了为何RR能够避免不能够重复读而RC不行,由于二者获取事务链表的方式不一样,RC每一个语句都会从新获取,所以能够读取到其余事务最新提交的数据。 咱们能够经过show engine innodb status语句查看事务链表
咱们知道事务开启的时候会获取事务链表,这个类中存储了当前链表中最大的事务ID和最小的事务id。 假设当前活跃的事务链表以下:
ct-trx-->trx11 --> trx10 --> trx9 --> trx5 --> trx2;
复制代码
ct-trx表示当前事务id,对应的read_view数据结构以下
read_view->creator_trx_id = ct-trx;
read_view->up_limit_id = trx2; 低水位
read_view->low_limit_id = trx11; 高水位
read_view->trx_ids = [trx11, trx10, trx9, trx5, trx2];
复制代码