对于原子性
undolog记录了反向的操作
对于一致性
使用undolog保证了事务要么提交要么回滚
使用MVCC保证事务内的读一读一致性
对于隔离性
MySQL有四个隔离级别来保证隔离性
通过锁和MVCC实现隔离级别下的读写隔离
对于持久性
通过redolog重做日志来保证事务的持久性
1、redo
实现事务的持久性,由两部分组成,重做日志缓冲(易失)和重做日志文件(持久)
redo日志是物理日志,记录的是页的物理修改操作(例如增大表空间),这一点和undo日志有区别,undo记录的是逻辑操作,对于物理操作不能恢复
每次commit事务后,InnoDB引擎会调用一个fsync将缓冲区的重做日志刷新到磁盘中,所以在这方面来说磁盘的性能决定了数据库的性能
我们可以设置redo机制的发生——设置innodb_flush_log_at_trx_commit
,0、1、2分别代表三种状态
默认是1:每次commit事务执行一次fsync
0:commit时不执行fsync,而是在某个空闲的时间段写入,但是当数据库宕机就会有数据的丢失
2:将重做日志写入文件的缓冲区,但是不调用fsync写入磁盘中,这样当数据库宕机时虽然不会丢失事务的修改,但是当操作系统宕机就会造成数据丢失,因为没有写入到磁盘中
2、undo
实现事务的一致性。与redo重做日志文件不同,它是放在数据库内部的共享表空间的
两个数据字典表,分别是INNODB_TRX_ROLLBACK_SEGMENT
和INNODB_TRX_UNOD
表
undo日志是逻辑日志,只能将数据库逻辑地恢复到原来的样子,对于数据结构和页不能恢复
undo实现了MVCC,提供了多版本的非锁定读
undo log也会产生redo log,因为undo log也需要持久化
通过查看数据字典表我们可以发现,当执行delete操作时,并没有直接删除数据,而是将delete flag标志为1,而最终的删除操作是在purge操作中完成的;当执行update操作时,对于非主键的update会只有一个update操作,而对于一个主键的update会先delete操作(同样是delete flag置为1),再进行insert操作。
3、purge
上面提到过delete和update操作并不会直接删除原有数据,只是标记为删除,真正的操作被延时到purge操作中完成。所有操作产生的undolog按照事务提交的顺序,保存在history list中,在执行purge时,会从该链表头部开始找到第一个需要被清理的记录,并根据事务id向后检测该undolog是否被其他事务使用,如果未使用则进行purge操作并删除,否则放弃,继续判断链表的下一个undolog。
为了提高purge效率,可以通过 innodb_purge_batch_size 设置每次purge操作需要清理的undolog数量,越大则每次回收undo页越多,可供重用的undo页就越多,减少了磁盘存储空间和分配的开销。不过该参数设置得过大,会导致每次需要purge的undo配置越多,从而导致CPU和磁盘IO过于集中使用,使性能下降。
全局动态参数 innodb_max_purge_lag 用来控制history list的长度,若长度大于该参数时,会延缓DML操作(dalay单位为毫秒),默认值为0,表示不对list大小做任限制,但是当InnoDB存储引擎压力太大时,purge操作并不高效。
4、group commit
什么是group commit?
是非只读事务提交时需要进行一次fsync操作,保证重做日志成功刷盘,以便当数据库发生宕机时,可以通过重做日志进行数据恢复。为了提高磁盘fsync的效率,数据库提供了group commit功能,在一次fsync时可以刷新多个事务日志写入磁盘。
事务提交后的操作
1)将重做日志写入重做日志缓冲区
2)将缓冲区的重做日志刷新到磁盘中
事务提交的两个阶段来说,多个事务可以先进行第一阶段,再进行一次第二阶段,提高了性能。
group commit失效问题
在MySQL5.6之前即InnoDB引擎1.2版本之前,开启了二进制日志,导致了InnoDB存储引擎的group commit失效
为了保证InnoDB存储引擎层的事物和二进制日志的一致性,在事物提交时采用了如下操作:
1)InnoDB存储引擎进行prepare操作
2)MYSQL数据库层写入二进制日志
3)InnoDB存储引擎层将重做日志写入到重做日志文件(上面的两个阶段)
每个步骤都需要进行一次fsync操作才能保证MYSQL层和InnoDB层的一致性,在内部使用了prepare_commit_mutex
锁,导致了InnoDB的group commit失效。
如何解决失效?
在MYSQL5.6开始采用了一个Binary Log Group Commit(BLGC),在MYSQL数据库层通过Group commit
来写入二进制日志
参考:MYSQL技术内幕 InnoDB存储引擎