mysql-innoDB架构分析

先上一张官网的架构图,本文将按照架构图中的组件逐一分析。

https://zzk-markdown.oss-cn-hangzhou.aliyuncs.com/mysql/innodb-architecture.png

1. buffer pool

按照局部性原理,将预期会使用到的数据缓存到内存中,避免每次读取数据都需要进行磁盘i/o,提升i/o性能,这块存放缓存的内存区域就是buffer pool。

buffer pool 是一种降低磁盘访问的机制。

磁盘访问通常以页为单位。

缓存池常见的实现方式是LRU(链表实现,为了减少数据移动),管理磁盘页。

缓存池管理方式–LRU(链表实现,为了减少数据移动)

普通LRU会有以下问题:

  • 预读取失效,预读取的页不会真正被读取
    • 优化思路:让预读失效页尽快出内存,真正读取页才挪到LRU头部
    • 方案:分代管理,预读取进入老生代,真正读取再进入新生代
  • 缓冲池污染,要批量扫描大量数据,导致缓冲池中的热点页被大量替换出去
    • 方案:在老生代设置停留时间,只有被真正读取并且停留时间达到阈值,才会移步新生代

innoDB 的buffer pool 对应参数

参数:innodb_buffer_pool_size

介绍:配置缓冲池的大小,在内存允许的情况下,DBA往往会建议调大这个参数,越多数据和索引放到内存里,数据库的性能会越好。

参数:innodb_old_blocks_pct

介绍:老生代占整个LRU链长度的比例,默认是37,即整个LRU中新生代与老生代长度比例是63:37。

画外音:如果把这个参数设为100,就退化为普通LRU了。

参数:innodb_old_blocks_time

介绍:老生代停留时间窗口,单位是毫秒,默认是1000,即同时满足“被访问”与“在老生代停留时间超过1秒”两个条件,才会被插入到新生代头部。

Buffer pool 参考链接

对于读请求,buffer pool 能够减少磁盘的io,提高性能,那么对于写请求呢?

2. Change Buffer

而对于写请求的优化,就是使用change buffer 来降低磁盘io的

主要应用于不在缓冲池中的非唯一普通索引页的写操作

如果要写的页写已经在缓冲池中了是怎样一个写流程?

为什么唯一索引不适用呢?

唯一索引的话每次插入操作都需要检查索引的唯一性

change buffer 参考

相关参数:

参数:innodb_change_buffer_max_size

介绍:配置写缓冲的大小,占整个缓冲池的比例,默认值是25%,最大值是50%。

画外音:写多读少的业务,才需要调大这个值,读多写少的业务,25%其实也多了。

参数:innodb_change_buffering

介绍:配置哪些写操作启用写缓冲,可以设置成all/none/inserts/deletes等。

3. Log buffer

知其然,知其所以然。思路比结论重要

事务提交时,事务日志为什么要先写到log buffer 在写到os cache中呢?

虽然是内存操作,但是日志写到os cache中需要进行上下文切换切换到内核态,每次事务提交都直接写则每次都要切换到内核态。先写到log buffer中,将每次写优化为批量写,减少上下文切换次数。

这个优化思路很常见,高并发的MQ落盘,高并发的业务数据落盘,都可以使用。

4. AHI–Adaptive Hash Index

自适应哈希索引

为什么叫自适应?

用户不能创建,是mysql优化器自行判断,需要时创建

既然是hash,key是什么,value是什么?

key是索引键值

value是索引记录的页面位置

所以hash索引是索引的索引

为什么要用哈希索引进行优化?

通过附加索引查询数据时,有时候会进行回表查询,这样会导致查询连路很长降低查询效率

哪些业务适用,哪些业务不适用?

单行记录查询、索引范围查询、记录数不多能全部放到内存中—-适用

业务中有大量join、like时,AHI的维护会成为负担,建议手动关闭。

5. redo log

有单独文章讲解

6. double write buffer

知其然,知其所以然。思路比结论重要

解决什么问题?

innoDB数据页大小是16k,文件系统中的数据页(后称系统页)大小是4K,那么写数据库时我们将一页数据页落盘,需要刷写4页系统页,如果在此过程中系统掉电,将造成磁盘数据页损坏(例如,前两页系统页已被刷写,后两页未刷写)。

双写缓存

如何解决?

DWB缓存即将刷写的数据页。

DWB具有两层架构,分为内存和磁盘

当有数据要落盘时:

第一步:将内存中修改后的数据页memcopy到dwb内存中

第二步:将dwb内存中的数据页写入dwb磁盘

第三步:将dwb中的数据页落盘到磁盘数据页

假使第二步掉电,磁盘数据页也还是完整的,可以通过redo log进行恢复,redo无法修复这类“页数据损坏”的异常,修复的前提是“页数据正确”并且redo日志正常。

假如第三笔掉电,dwb中的数据页也是完整的,可以直接落盘

性能影响大吗?

第一步属于内存操作,速度很快

第二步属于磁盘顺序追加写,1秒几万次没问题

第三步不属于额外操作

另外,dwb 由128页组成,容量2MB,会分两次刷入dwb磁盘,每次1M,速度也很快

有第三方评测,性能损失约为10%

可以通过:

show global status like “%dblwr%” 查看dwb使用情况

Innodb_dblwr_pages_written 记录dwb中的写入页数

Innodb_dblwr_writes 记录dwb的写入次数