前面说了lsn值就是log sequence number代表记录redo日志存了多少字节,默认值是8000多,算偏移量直接减一下就好,还有flush_to_disk的lsn值,这个值之前的数据代表都已经持久化,这个持久化代表redo日志持久化,但是对应的buffer pool数据还不能覆盖,这时候又checkpoint lsn值,这个值之前的数据都代表已经持久化完毕,是可以覆盖的。因为redo日志存储有限,存满之后,又会从个文件循环存储。可以用show engine innoDB status查看。
innoDB_flush_log_at_trx_commit的用法
我们前面说过为了保证持久性,每次事务提交都会吧redo日志从buffer pool刷新到磁盘,但这样很明显会降低性能,我们可以选择修改innoDB_flush_log_at_trx_commit来自己设计:
0:当设置为0的时候,表示事务提交时候不会吧redo日志刷新到磁盘,只会在后台自己刷,这样就可能会导致,事务提交后,服务器挂了,后台线程没有及时刷新到磁盘,则就数据丢失。
1:当设置为1的时候,表示事务提交的时候需要刷新到磁盘,这也是系统默认的。
2:当设置为2的时候,表示事务提交需要将redo日志刷新到buffer pool缓冲区,这时候数据库挂了,操作系统没挂的话,事务持久性可以保证,但如果操作系统页挂了,则不能保证。
崩溃恢复
我们先要确定恢复的起点,我们前面说过checkpoint_lsn前面的数据都是刷新到磁盘的,所以之前的数据就没必要恢复了,所以起点就是从checkpont_lsn值开始。
当redo日志文件组个文件管理信息有两个block存了checkpoint_lsn信息,我们当然要选取近发生的依次checkpoint信息。衡量checkpoint发生早晚主要通过checkpont_no,我们只要吧checkpoint1和checkpoint2 的checkpoint_no拿出来比较,谁的更大,说明是近checkpoint。这样我们就能拿到对应的checkpoint_lsn和checkpoint_offset。
确定恢复的终点
前面我们说了block是循环使用的,log block header里有一个参数,log_block_hdr_data_len,当存满的时候,显示512字节,当有一个block不是512,就代表当前 block是恢复的终点。
怎么恢复
理论上我们只要在checkpoint_lsn值后面,按照日志记载的挨个进行恢复就行,但innoDB有更好的恢复办法。
使用哈希表:
根据redo日志的space id和page number计算出散列值,吧space id和page number相同的redo日志放入hash槽,如果有多个相同的,则把他们之间使用链表连接起来,按照先后顺序。
这样的好处是,一次性可以将一个页面恢复好,避免对同一个页面多次I/O,需要注意的时候,是严格按照时间顺序的,比如如果记录的是先删除,再增加,则就按这个时间顺序,不能先增加后删除。
跳过已经刷新到磁盘的页面
Checkpoint之前的值已经刷新到磁盘,但是checkpoint之后的值呢,因为后台线程在不断刷新flush链表和lru链表到磁盘。如果被刷新了,则没必要恢复。
那么怎么判断是否已经刷新过呢?我们前面说过每个页面都有一个file header,这里面有个file_page_lsn的属性,该属性记录了后一次修改lsn的值(就是页面控制块newest_modification)。如果在checkpoint之后,又有数据被刷新到磁盘,那么file_page_lsn的值肯定大于checkpoint_lsn的值,那么满足这个条件的,都不需要恢复,又提升了速度。
Log_block_hdr_no
Log_block_hdr_no个比特位比较特殊,称为flush bit如果是1,代表本block是个被刷入到磁盘的block。
该值也在log_block_header中,代表的标号,初次使用block分配的,计算该值的公式:
((lsn/512) & 0x 3FFFFFFFUL) + 1
后面的0x3FFFFFFFUL就是表示二进制。