事务的原子性:事务中的所有操作,要么全部完成,要么不做任何操作,不能只做部分操作。
事务的持久性:事务一旦完成,该事务对数据库所做的所有修改都会持久的保存到数据库中。
undo log:为了实现事务的原子性,在innodb中,还用undo log实现MVCC
undo log原理:在操作任何数据之前,首先将数据备份到一个地方(即undo log),然后进行数据修改,如果出现错误或用户执行了rollback语句,则系统就可以利用undo log中的备份数据恢复到事务开始之前的状态
redo log原理:redo log记录的是新数据的备份。在事务提交前,只要将redo log持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是redo log已持久化,系统可以根据Redo Log的内容,将所有数据恢复到最新的状态。
undo +redo 事务的特点:
1.为了保证持久性,必须在事务提交前将redo 持久化
2.数据不需要在事务提交前写入磁盘,而是缓存在内存中
3.redo 保证事务的持久性
4.undo 保证事务的原子性
5.数据必须要晚于redo log写入持久存储
undo+redo主要是为了提升IO性能,通过缓存数据,减少了写数据的IO。但是却引入了redo log 的IO,通过以下特点提升redo log的IO性能:
1.尽量保持redo log存储在一段连续的空间上,所以在系统第一次启动时就会将日志文件的空间完全分配
2.批量写入日志,引入了redo log buffer,先写入redo log buffer.当需要将日志刷新到磁盘时
3.并发的事务共享redo log的存储空间,它们的Redo Log按语句的执行顺序,依次交替的记录在一起, 以减少日志占用的空间。
4.当一个事务将Redo Log写入磁盘时,也会将其他未提交的事务的日志写入磁盘。
5.Redo Log上只进行顺序追加的操作,当一个事务需要回滚时,它的Redo Log记录也不会从Redo Log中删除掉。
恢复(recovery)
未提交的事务和回滚了的事务也会记录redo log,因此进行恢复时,这些事务要进行特殊的处理
恢复的策略:
1.进行恢复时,只重做已经提交了的事务
2.进行恢复时,重做所有事务包括未提交的事务和回滚了的事务,然后通过undo log回滚那些未提交的事务
mysql中innodb使用了2的恢复策略,恢复机制有如下特点:
1.在重做redo log时,并不关心事务性。即恢复时,没有BEGIN,也没有COMMIT,ROLLBACK的行为。也不关心每个日志是哪个事务的。尽管事务ID等事务相关的内容会记入Redo Log,这些内容只是被当作要操作的数据的一部分。
2.使用2策略必须将undo log 持久化,且必须要在写redo log之前将对应的undo log写入磁盘,innodb将undo log看做数据,因此记录undo log的操作也会记录到redo log中,这样undo log就可以像数据一样缓存起来,而不用在redo log之前写入磁盘
3.一个回滚了的事务在恢复时的操作就是先redo再undo,
物理的日志(Physical Log)
A. 记录完整的Page
B. 记录Page中被修改的部分(page中的偏移,内容和长度).
优点:不需要持久化的数据保持在一个一致的状态。如:在写一个页面到磁盘上时,系统发生故障,页面上的一部数据写入了磁盘,另一部分丢失了。 这时仍然可以恢复出正确的数据。
缺点:记录的内容很多,占用很大的空间
逻辑的日志(Logical Log)
记录在关系(表)上的一个元组操作。
A. 插入一行记录。
B. 修改一行记录。
C. 删除一行记录。
逻辑日志比起物理的日志,更简洁且占用空间更小。
逻辑日志的缺点:
1.部分执行;如:表T有2个索引,在向T插入1条记录时,需要分别向2个B-Tree中插入记录。 有可能第一个B-Tree插入成功了,但是第二个B-Tree没有插入成功。在恢复或回滚时,需要处理这些特殊情况。
2.操作的一致性问题; 一个插入操作有一个B-Tree的分裂,页A的一半数据移到了B页,A页写入了磁盘,B页没有写入磁盘。如果这时候发生了故障,需要进行恢复,逻辑日志是很难搞定的。
逻辑和物理结合的日志
1.物理到page. 将操作细分到页级别。为每个页上的操作单独记日志。如:一个Insert分别在2个B-Tree的节点上做了插入操作,那么就分别为每一个页的操作记录一条日志。
2.Page内采用逻辑的日志。如:对一个B-Tree的页内插入一条记录时,Page内的逻辑日志只记录:‘这是一个插入操作’和’这行数据的内容‘。
redo log记录是物理和逻辑相结合的日志
1.部分执行是不存在的,因整个页面的操作是原子性的,
2.数据一致性问题仍然存在,通过double write来解决
undo log记录的是逻辑的日志
double write
1.在覆盖磁盘上的数据前,先将Page的内容写入到磁盘上的doublewrite buffer(不是内存空间,是持久存储上的空间)
2.然后再将Page的内容覆盖到磁盘上原来的数据。
如果在1步骤时系统故障,原来的数据没有被覆盖,还是完整的。
如果在2步骤时系统故障,原来的数据不完整了,但是新数据已经被完整的写入了doublewrite buffer. 因此系统恢复时就可以用doublewrite buffer中的新Page来覆盖这个不完整的page.
double write的特点:
1.Double write buffer是一段连续的存储空间,可以顺序写入。
2. Double write有自己的写buffer.
3. 先将多个要做doublewrite的page写入内存的buffer,然后再一起写到磁盘上
checksum
检测页面是否一致。每个页面修改完成后都会计算一个页面的checksum。这个checksum存放在页面的尾部.每次从磁盘读一个页到内存时,都需要检测页的一致性。
checkpoint
随着时间的积累,Redo Log会变的很大很大。如果每次都从第一条记录开始恢复,恢复的过程就会很慢。为了减少恢复的时间,就引入了Checkpoint机制。
checkpoint原理:在某个时间点,所有的脏页都被刷新到了磁盘上.这个时间点之前的所有Redo Log就不需要重做了。系统记录下这个时间点时redo log的结尾位置作为checkpoint. 在进行恢复时,从这个checkpoint的位置开始即可。Checkpoint点之前的日志也就不再需要了,可以被删除掉。为了更好的利用日志空间,InnoDB以环形缓存(circular buffer)的方式来使用日志空间。
sharp checkpoint
在某个时间开始停止一切更新操作,直到所有的脏页被刷新到磁盘,Checkpoint被记录。 显然对于繁忙的系统, 这种方法是不合适的。
Sharp checkpoint完成时,持久存储中存储的数据是某个确切时间点的内存数据的快照。
fuzzy checkpoint
刷脏页的同时用户还在更新数据,LSN1前的某个脏页在刷到持久存储之前就有可能被
LSN1之后的某个操作给修改了。当checkpoint完成时,LSN1后的部分操作(R1,R2对应的操作)也被 持久化了。
说明:checkpoint 在LSN1位置,当checkpoint完成时R1,R2对应的修改也被刷到了持久存储。
恢复时要从LSN1位置开始,包括R1, R2在内。
Fuzzy checkpoint完成时,持久存储中存储的数据不是某个确切时间点的内存数据的快照。
脏页(dirty page)
如果一个数据页在内存中修改了,但是还没有刷新到磁盘。这个数据页就称作脏页。
日志顺序号(Log Sequence Number)
LSN是日志空间中每条日志的结束点,用字节偏移量来表示。在Checkpoint和恢复时使用。
数据页的最新LSN
InnoDB中每个数据页上都记录有一个LSN。每次更新数据页时,将LSN修改为当前操作的redo log的LSN。在恢复时,如果数据页的LSN大于等于当前redo log的LSN,则跳过此日志。
异步checkpoint
实现了幂等规则后,脏页就可以在任何时间,以任何顺序写入持久存储了。InnoDB的buffer pool有 一套单独的机制来刷脏页。因此很多情况下checkpoint时,并不写脏页到存储。只是将所有脏页的最小的LSN记做checkpoint.
同步checkpoint
InnoDB的buffer pool通过LRU的算法来决定哪些脏页应该被写入持久存储。如果包含最小LSN的页面频繁的被更新,它就不会被刷到存储上。这样就可能导致checkpoint点很长一段时间无法前进,甚至导致日志空间被占满。这时就要按照LSN由最小到大的顺序写一部分脏页到持久储。
译者介绍:家华,从事mysqlDBA的工作,记录自己对mysql的一些总结