InnoDB引擎 内存概览
本文最后更新于:2022年7月24日 下午
概览:InnoDB引擎
InnoDB特点
- 行锁设计
- 支持MVCC
- 支持外键
- 提供一致性非锁定读
InnoDB 内存
InnoDB内存分类:
- 缓冲池
- 重做日志缓冲
- 额外内存池
InnoDB存储引擎基于磁盘存储,由于CPU速度与磁盘速度之间的鸿沟,需要使用缓冲池技术来提高数据库的整体性能。
缓冲池其实就是一块内存区域,通过内存速度来弥补磁盘速度较慢对数据库的影响。
整体操作:
- 数据库读取页,将磁盘中的页放到缓冲池,下一次再读相同页时,首先判断该页是否在缓存中,若在,命中缓存,否则读取磁盘。
- 数据库修改页,先修改在缓存中的页,然后再以一定的频率写回磁盘。 —— 使用Checkpoint机制刷回。
缓存池中的数据:
- 索引页
- 数据页
- undo页
- 插入缓冲(insert buffer)
- 自适应哈希索引
- 锁信息等
InnoDB还允许多个缓冲池实例,每个页根据哈希值分配到不同的缓冲池实例中 —— 减少数据库内部资源竞争,增加数据库并发处理能力。
重做日志缓冲 redo log buffer
先将重做日志信息放入缓冲区,在按照一定的频率刷到重做日志文件中。
一般情况下,重做日志缓冲区不大,8MB,因为每一秒都会将重做日志缓冲刷新到文件。
刷新场景:
- Master Thread每一秒刷新,
- 每个事务提交时会刷新,
- 当重做日志缓冲池剩余空间小于一半时,也刷新
InnoDB内存 —— LRU、Free List、Flush List
LRU —— Latest Recent Used,最近最少使用。
缓冲池使用LRU来管理,最频繁使用的再LRU List的前端,最少使用的在LRU List的尾端,当缓存池放不下时,首先释放尾端数据。
LRU优化
InnoDB在LRU列表中加入了midpoint位置,新读取到的页,不是放在LRU列表的首部,而是放在LRU列表的midpoint位置。
1 |
|
默认位于距离首部长度5/8的位置。
InnoDB存储引擎,把midpoint之后的列表称为old列表,之前的列表称为new列表 —— 存储活跃的热点数据。
为什么要做这样的优化呢?
对于某些sql,它会做索引或者数据的扫描操作,会加载很多的页,但是这些页可能并非会被频繁使用,如果页放到了LRU首部,会把很多真正的热点数据移除,影响性能。
那么就还需要一个机制,来讲那个数据放入热点数据中
1 |
|
这个用来表示当前页读取到midpoint位置之后需要过多久才会加入到LRU列表前端。
Free List
表示当前还空闲着的缓冲池的页的集合。
当Free List不为空时,使用其中的空闲页;当没有空闲页的时候,新增页面需要使用LRU来淘汰替换。
但是并不是所有申请的页都会放到LRU列表中,类似自适应哈希索引、Lock信息等时不需要使用LRU维护的。
参考链接:https://blog.csdn.net/lijuncheng963375877/article/details/124011204
Flush List
LRU列表中被修改的页称为脏页,即缓冲池中的数据和磁盘中的数据不一致。
Flush List列表中的页就是脏页,使用Checkpoint机制来将页刷回磁盘。
这里的脏页既在LRU中,又在Flush List中。
Checkpoint技术
回顾:
- 引入缓冲池技术来协调CPU与磁盘之间速度不一致的问题。数据更新操作都是先在缓冲池中完成的,数据修改后就是脏页,需要刷回磁盘。
- 一修改就刷新磁盘这样的开销也是很大的,例如修改总是在某几页中。此外如果刷盘的过程中磁盘宕机,数据就丢失无法恢复,由此引入了Write Ahead Log策略,先写重做日志,再刷盘。
对于重做日志和刷盘来说,要考虑几个点:
- 对于重做日志,我怎么知道数据库的哪一部分数据还没有更新呢?
- 缓冲池不够用时?脏页如何处理?
- 如果重做日志不可用了?怎么处理?
引入checkpoint,它表示在其之前的页一定都刷回了磁盘,宕机发生时,只对checkpoint及其之后的页做恢复,这样可缩短恢复时间。
当缓冲池不够用的时候,LRU会溢出最近最少使用的页面,若页面为脏页,则需要强制执行checkpoint,将脏页刷回磁盘。
脏页的修改数据都是存放在redo log中的,如果脏页不再管理了,那么对应的redo log中的数据应该刷到磁盘,下一次读取的时候就是最新的数据了。
那么什么时候会出现重做日志不可用呢?
设计上,重做日志是循环使用的,而不是无限增大的,如果出现了重做日志写了一圈写到了checkpoint的位置,并且还需要继续写的情况时,这是重做日志就不可用了。
做法:先强制checkpoint技术,刷新页,再继续使用重做日志。
参考链接:https://www.jianshu.com/p/69da2c9939d2
Async/sync Flush Checkpoint —— 解决重做日志不可用
1 |
|
两个标志位
1 |
|
- checkpoint_age < async_water_mark,不需要刷盘
- async_water_mark <= checkpoint_age < sync_water_mark, 刷盘,使其满足第一个条件位置
- sync_water_mark > checkpoint_age ,一般很少发生,触发Sync Flush操作,使其满足第一个条件。
使用这种方式,保证了重做日志循环使用的可能性。
InnoDB其他优点 —— 插入缓冲
Insert Buffer是一种提高非主键索引、非唯一索引的数据的索引插入、更新性能的设计。
InnoDB存储引擎的索引使用了B+树,索引可分为聚簇索引和非聚簇索引两类。
新插入行时,一般是按照主键递增的顺序做的插入,对于聚簇索引来说,一般是顺序的,不涉及随机读。
但是对于表的其他字段,可能会产生一些非聚簇的、非唯一的索引,对于这种索引的叶子节点插入不是顺序的了,而是需要离散的访问非聚集索引页,这是随机读的过程。
Insert Buffer设计:对于非聚簇索引的插入或者更新操作,不是每一次直接插入到索引页中,而是:
- 先判断非聚集索引页在缓冲池吗?在的话直接更新
- 不再的话,先放入Insert Buffer之中
- 再以一定的频率和情况将Insert Buffer中的数据和二级索引叶子节点合并。
适用条件:非聚簇索引、非唯一索引。
为什么是非唯一索引?
因为插入缓冲中时,引擎并不去查找索引页来判断插入的记录的唯一性,如果要验证唯一性,就需要离散读,那就没必要使用这个了。
缺点:当大量数据都使用了Insert Buffer时候,若发生了宕机,则恢复的时候可能需要很长时间。
InnoDB 1.0.x之后,引入了Change Buffer,可看作Insert Buffer的升级版。
InnoDB其他优点 —— 两次写 double write
数据库宕机的时候,可能引擎正在写入某个页到磁盘中,但是只写了一部分,这种称为部分写失效。
发生写失效,可以使用重做日志来恢复吗?
- 重做日志记录的是对一个页的物理操作,eg,偏移量800的地方,写’aaaa’
- 如果页发生了损坏,那么重做日志就没有意义了。
- 即,重做前,需要一个页的副本,写失效时,先通过页副本还原,再写重做日志。
实现:
doublewrite由两部分组成:
- 内存中的doublewrite buffer, 2MB
- 物理磁盘上共享表空间中的连续128页(一页=16KB),也就是2个区,大小也2MB
对缓冲池脏页刷新时,并不直接写磁盘,而是先将脏页复制到内存中的doublewrite buffer,然后将doublewrite buffer分成2份,每次1MB顺序写入共享表的物理磁盘,最后马上同步磁盘。
未完待续…
参考书籍:《MySQL技术内幕 —— InnoDB存储引擎》
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!