什么是redo?
redo log是重做日志,是wal(Write ahead log)的基础.mysql在innodb buffer中修改的数据并不会实时刷新到磁盘,这就全部都是随机的io,性能很差,但是这时候需要redo来保证
数据的持久化。
主要的作用:
1.将随机刷盘转化为顺序刷盘,提高性能
2.crash recovery: 当故障发生导致内存数据丢失后,InnoDB会在重启时,通过重放REDO,将Page恢复到崩溃前的状态
这也是和普通的文件系统最大的区别
redo对我们来说最重要的几个点:
1.性能要高
2.数据一致性,crash recovery数据幂等
然后mysql的redo不像oracle 是物理的,mysql的redo可以说是基于物理的逻辑日志,这个意思就是redo里面记录的是某个page的id及对应的数据的偏移量及对应的字段的值
比(Page ID,Record Offset,(Filed 1, Value 1) … (Filed i, Value i) … )
逻辑的日志有什么问题:
1.就是mysql 默认的page是16K,操作系统是4k,所以就会产生也的分裂,也就是partialpage writes.
比如比如一个事务更新了4k的快,这时候没有double write的时候,操作系统cash了。这时候对于innodb 的 16k数据快来说这是不完整的。这时候启动需恢复的时候发现已经损坏了。
redo的结构?
redom是以ib_logfile0,ib_logfile1来进行命名,
主要的参数就是:
innodb_log_file_in_group:指定redo的个数,默认是2个,具体的可以看官方文档
innodb_log_file_size:每个日志文件指定的文件大小
如果做到高性能?
从大的方便上看让data page延迟刷盘减少随机的磁盘io,从更近一步的看
1.redo的生成,一个redo操作,可能会操作不同page,这时候innodb通过mtr(min-transaction)来保证redo操作的原子性。
mtr.start() 开启一个mini transaction
mtr_x_lock() 加锁,这个操作分成两步,1. 对space->latch加X锁;2. 将space->latch放入mtr_t::m_impl::memo中(这样在mtr.commit()后就可以将mtr之前加过的锁放掉)
mlog_write_ull 写数据,这个操作也分成两步,1. 直接修改page上的数据;2. 将该操作的redo log写入mtr::m_impl::m_log中
mtr.commit() 写redo log + 放锁,这个操作会将上一步m_log中的内容写入redo log file,并且在最后放锁
以上就是一个mtr大致的执行过程,这里仅需要知道mtr.commit()
这是时候就需要考虑到很多mtr操作如何保证在高并发下面的性能
这是可能考虑到redo是需要顺序的,不然如果发现cash 后的recovery会有数据不一致的问题,可能觉得这个会是一个性能的瓶颈点?
虽然看文档8.0开始并发的同步的mtr会调用不同的log_buffer_reseever函数来申请自己的一片log buffer.然后将m_log中的数据拷贝到这片中间。
但是这里想到的就是两个问题:
1.去申请log.sn 申请空间(通过偏移量)的时候应该也是有一把全局的latch
2.并发的写入会有很多的空洞
大概的理解是:使用link_buf 对lsn取模,相当于分片后,每个m_log里对应的lsn会分散在对应的link_buf段内来提供减少全局的latch,并发的写入
redo 和lsn什么关系?
上面提到mtr中的m_log都有对应的[start_lsn,end_lsn]
去申请log.sn 申请空间。这边redo本省的sn和lsn能进行转换 ,给sn每496B都增加16B的头尾,结果即为对应的lsn
redo和lsn和检查点的关系,需要另一个学习文档展开。
什么时候需要写redo log?
1.每一秒执行
2.事务提交
3.checkpoint
4.binlog切换
5.redo log不足
6.停库
相关的参数
innodb_flush_log_at_trx_commit(重要)
对应的值有 0,1,2
1是最安全的,每次事物提交的时候都会把redo log 直接持久化到磁盘
2每次事物commit的时候只是吧redo log wirte即写入到page cache ,但是持久化到磁盘是每一秒一次。
0日志是每秒执行一次,commit时没有及时写入磁盘。
这里特别提一下percona版本的mysql能session级别的设置这个参数
1、若innodb_use_global_flush_log_at_trx_commit为OFF,则使用session.innodb_flush_log_at_trx_commit;
2、若innodb_use_global_flush_log_at_trx_commit为ON,则使用global .innodb_flush_log_at_trx_commit(此时session中仍能设置,但无效)
3、每个session新建时,以当前的global.innodb_flush_log_at_trx_commit 为默认值
这个值的判断之前看过源码是在mtr_t::Command::execute() 判断。