一、事物的实现
事务隔离性由锁来实现,原子性、一致性、持久性通过数据库的redo log和undo log来完成。redo log称为重做日志,用来保证事务的原子性和持久性。undo log用来保证事务的一致性。
redo log和undo log都可以视为一种恢复操作,redo log恢复提交事务修改的页操作,而undo log回滚行记录到某个特定版本。因此两者记录的内容是不同的,redo通常是物理日志,记录的是页的物理修改操作。undo是逻辑日志,根据每行记录进行记录。
二、redo log
重做日志用来实现事务的持久性,其由两部分组成:一是内存中的重做日志缓冲(redo log buffer),其是易失的;二是重做日志文件(redo log file),其是持久的。
innodb的事务的存储引擎,其通过force log at commit机制实现事务的持久性,当事务提交commit时,必须先讲该事务的所有日志写入到重做日志文件进行持久化,待事务的commit操作完成才算完成。redo log基本上都是顺序写的,在数据库运行时不需要对redo log文件进行读取操作。
为什么要用redo日志
事务提交后,必须将事务对数据的修改刷(fsync)到磁盘上,才能保证事务的ACID特性
这个刷盘是一个随机写,随机写性能较低,如果每次事务提交都刷盘,会极大影响数据库的性能
随机写性能差的优化方案
redo日志的三层架构

粉色 — 是innodb的内存结构,日志缓冲区(log buffer),这一层,是mysql应用程序的用户态
黄色 — 是操作系统的缓冲区,这层是OS内核态
蓝色 — 是落盘的日志文件
redo日志落盘的步骤
- 事务提交时,会写入log buffer,这里调用的是mysql自己的函数writeRedoLog
- 只有当mysql发起系统调用写文件write时,log buffer里的数据,才会写到OS cache,注意:mysql系统调用完write之后,就认为文件已经写完,至于什么时候落盘,时操作系统决定的。
- 操作系统将OS cache里的数据,刷到磁盘上(mysql也可以主动去刷)
优缺点
- 将每次写优化为批量写,提高性能
- 可能会造成数据丢失,事务提交时,将redo log写入log buffer,认为事务提交成功,如果write OS cache之前或者时刷盘之前,系统崩溃则会造成数据丢失
刷盘的三种策略

注意:mysql通过innodb_flush_log_at_trx_commit参数设置redo log的刷盘策略
策略一:innodb_flush_log_at_trx_commit = 0 (性能最佳)
每隔1秒,将log buffer中的数据批量写到OS Cache中,同时mysql主动刷盘,如果数据库崩溃,有一秒的数据丢失
策略二:innodb_flush_log_at_trx_commit = 1 (强一致)
每次事务提交,都将log buffer中的数据write入OS Cache中,同时mysql主动刷盘,将数据写入磁盘,这种策略是innodb默认的策略,为的是保证ACID特性。
策略三:innodb_flush_log_at_trx_commit = 2
每次事务提交,都将log buffer中的数据写入os cache中,每隔一秒,mysql做一次刷盘,如果操作系统崩溃,会有1s的数据丢失
问:高并发业务,行业最佳实践,是使用第三种折中配置(=2)
配置为2和配置为0,性能差异并不大,因为将数据从log buffer拷贝到os cache,虽然跨越用户态与内核态,但毕竟是内存数据拷贝,速度很快
配置为2和配置为0,安全性差异巨大,操作系统崩溃的概率相比mysql应用程序崩溃的概率,小的多,设置为2,只要操作系统不崩溃,数据就不会丢失。
2.1、check point技术
缓冲池技术是为了协调cpu速度与磁盘速度的巨大鸿沟,因为页的操作首先都是在内存中完成,当update或者delete改变了页中的记录,那么此时页就是脏页,所以数据库需要将新的页刷新到磁盘,若每次页发生变化,都刷盘,那么性能会很低,如果热点数据集中在几个页中,性能会变得非常差。若不刷新,系统宕机就会出现数据丢失的问题,所以数据库普遍采用write ahead log策略,即事务提交时,先写重做日志,在修改页。如果系统宕机,则通过重做日志完成数据的恢复。
checkpoint(检查点)技术的作用
缩短数据库恢复的时间(因为缓存池和重做日志不是无限大,需将缓存池数据尽快刷新至磁盘,已保证空间可用)
缓存池不够用时,将脏页刷新到磁盘
重做日志不可用时,刷新脏页至磁盘
checkponit触发时间及时机
shark checkponit:数据库关闭时将所有的脏页都刷新回磁盘。
innodb存储引擎需要保证LRU列表中需要由100多个空闲页可用。
重做日志不可用的时候需要强制刷新
脏页太多的时候需要强制刷新。
三、undo log
redo log记录了事务的行为,但是事务有时还需要进行回滚操作,这时就需要undo log,如果用户执行的事务或者语句由于某种原因失败了,又或者用户执行了一条rollback语句,就可以用undo信息将数据回滚到修改之前的样子。
undo log是逻辑日志,因此只是将数据库逻辑的恢复到原来的样子,所有的修改都被逻辑取消,为什么undo log是逻辑日志,而非物理日志,这是因为在多用户并发系统中,可能会有数十、数百甚至数千个并发事务,数据库的主要作用就是协调对数据记录的并发访问,例如:一个事务在修改当前一个页中某几条记录,同事还有别的事务在对同一个页中另几条记录进行修改。因此,不能将一个页回滚到事务开始的样子,因为这会影响其他事务正在进行的工作。
例如:用户执行了一个insert 10W语句,这时会分配一个新的段,即表空间会增大;用户执行rollback时,innodb引擎执行回滚,它实际做的是与之前相反的操作,对于每个insert,innodb会完成一个delete,对于delete,innodb会执行一个insert,对于update,innodb会执行一个相反的update。
undo log是回滚日志,是为了实现事务的原子性,记录了事务的操作行为;在mysql数据库innodb存储引擎中,除了回滚操作,undo的另一个作用是MVCC,即在 InnoDB存储引擎中MVCC的实现是通过undo来完成。当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可以通过undo读取之前的行版本信息,以此实现非锁定读取。
