InnoDB 引擎三大特性

双写缓冲区(DoubleWrite Buffer)

原理

在这里插入图片描述

双写缓冲区是 InnoDB 引擎为了保证数据安全性,完整性而开发的

双写缓冲区是在系统表空间中

InnoDB 引擎会在磁盘上划分出连续的两个区的范围

1个区包含64个页,一个页 16k,1个区 = 64x16 = 1024k = 1M

因此一个双写缓冲区大小为两个 1M ,共 2M

当我们写数据时,InnoDB 会先把数据从内存的双写缓冲区中,接着调用 fsync 写入到磁盘的双写缓冲区上(完整页数据),最后再把数据写入对应数据页中

如果在写页的过程中发生意外崩溃,InnoDB 在稍后的恢复过程中在双写缓冲区中找到完好的 page 副本用于恢复

为什么要用双写缓冲区?

问题:双写缓冲区要写磁盘,数据之后也是要写磁盘,为什么还要提前写个双写缓冲区?这样不是增加IO?

答:首先一页数据是 16 kb,磁盘写一次是 4kb,因此一页数据写磁盘,要写 4 次。
假如在写的过程中假如写了 8kb 数据库崩了,就会导致部分页写入问题,页的数据不完整,数据就会有问题。而双写缓冲区作为一块预分配的磁盘位置,数据库在写磁盘双写缓冲区时是使用的顺序写,因此效率上要比直接写数据的随机写快的多,出问题的概率小

问题:fsync 是什么?

答:fsync 是底层操作系统对外提供的一个 API 接口,他能保证数据必然已经落盘。

问题:假如数据库突然挂了,双写缓冲区是如何恢复数据的?

答:双写缓冲区存在的目的就是 Innodb 为了保证 MySQL 数据的原子性。假如数据库数据写一半挂了,那么 MySQL 启动时,会从磁盘的双写缓冲区中找到直接写入的完整的数据副本,再用这个完整的副本去对有问题的页数据进行恢复

问题:假如写双写缓冲区挂了呢?

答:既然 MySQL 的双写缓冲区是双写机制,第一步写到磁盘的双写缓冲区自然有可能失败。假如失败了,MySQL 会根据磁盘上 B+ 树的结构,再配合上 Redo 日志对数据进行恢复

问题:既然可以通过 Redo 日志进行恢复,那么为什么还要双写缓冲区?

答:MySQL 的 Redo 日志存储的是对页的物理操作,也就是修改部分的操作,非修改部分的数据不保存。也就说假如我修改了 Table 表的 Name 字段,那么 Redo 日志会记载 Name 字段在物理层的偏移量从而定位到 Name 字段的修改信息。但是我们写数据是一页一页的写的,一次必然要写 4KB,假如由于重启导致页数据损坏,正好损坏的不是 Name 字段的数据,而是这个页上其他的非修改数据。那么由于 Redo 日志是增量存储,上面值存储了修改操作的信息,而没有其他数据的信息,因此此时损坏页,Redo 日志就无法修复。而双写缓冲区则拥有全部数据就能修复。

问题:双写缓冲区多一次写操作,对性能的影响大吗?

答:不大,因为双写缓冲区的写操作是顺序写。经过测试,使用双写缓冲区与不使用相比,性能仅降低 5%-10%。

问题:既然双写缓冲区写要是失败了,需要 Redo 日志进行恢复,那么假如写 Redo 日志失败了怎么办?

答:不存在这种情况,因为每个事务在提交时都会将对应的 Redo 日志刷盘,只有 redo 日志刷盘成功了,事务才能算完成

AHI 自适应哈希

在这里插入图片描述

自适应哈希索引是 Innodb 引擎自行研发的,用于提高 MySQL 的查询效率。

在 InnoDB 存储引擎内部自己去监控索引表,如果监控到某个索引经常用,那么就认为是热数据,然后内部自己创建一个 hash 索引,称之为自适应哈希索引(Adaptive Hash Index,AHI)。

创建以后,如果下次又查询到这个索引, 那么直接通过 hash 算法推导出记录的地址,直接一次就能查到数据,比重复去 B+tree 索引中查询三四次节点的效率高了不少。

MySQL 在哈希索引的设计上还采用了热点分散技术,这样的哈希索引在 MySQL 上默认是启动 8 个的,热点数据会分散到不同的哈希索引上,因此热数据访问时,能将请求分散到不同的哈希索引上,提高了并发访问的性能。

在这里插入图片描述

Buffer Pool

什么是 Buffer Pool?

InnoDB 为了缓存磁盘中的页,在 MySQL 服务器启动的时候就向操作系统申请了一片连续的内存,他们给这片内存起了个名,叫做 Buffer Pool。

默认情况下 Buffer Pool 只有 128M 大小(这个值其实是偏小的)。

启动服务器的时候可以通过配置 innodb_buffer_pool_size 参数的值来控制 Buffer Pool 大小,

Buffer Pool 内部组成

Buffer Pool 中默认的缓存页大小和在磁盘上默认的页大小是一样的,都是 16KB。

为了更好的管理这些在 Buffer Pool 中的缓存页,InnoDB 为每一个缓存页都创建了一些所谓的控制信息,这些控制信息包括该页所属的表空间编号、页号、 缓存页在 Buffer Pool 中的地址、链表节点信息、一些锁信息以及 LSN 信息,当然还有一些别的控制信息,我们称之为控制块

PS:MySQL 大部分缓存的数据都在 Buffer Pool 中,缓存页就是数据,数据库查询出来的数据都会缓存到缓存页中,便于下次快速查询。控制块保存了缓存也的各种信息地址等,用来找缓存页
在这里插入图片描述

Buffer Pool 中的链表结构

Free 链表

Free 链表说简单些,就是将所有空闲的缓存页串起来,其实就是把空闲的缓存页对应的控制块的地址用一个链表来维护。这样下次有新数据进来,可以直接来 Free 链上直接找到空闲的缓存页。

Free 链还会有一个块用于存储,链表头,链表尾,以及链表上的数量。
在这里插入图片描述

Flush 链

在这里插入图片描述
Flush 链的结构与 Free 链完全一样

既然我们知道 Buffer Pool 缓存的是数据库中查询出来的数据,那么必然会存在一个问题,假如数据被修改了怎么办?

因此 MySQL 把缓冲区上面这种被修改过的数据的控制块也用一个链表进行维护,以此来快速定位被修改过的数据,也被称为脏数据(未落盘的数据),因此 Flush 链又被称为脏链。

PS:MySQL 数据提交后,并不是立刻落盘的,而是依然在缓冲区里,最后会统一落盘。既然数据提交了自然会有 Redo 日志,假如数据库挂了,数据也是能恢复的。

既然是脏数据为什么不知道把数据删掉?

答:MySQL 本身数据并不是立刻落盘的。Flush 链上的数据,MySQL 会有定时任务去定时落盘。其次虽然是叫脏数据,但是本质上这些数据就是用户提交的数据,只是没落盘而已,读取时候直接读取是没有问题的。

LRU 链表

在这里插入图片描述
LRU 链表是一种最近最少使用淘汰链表,简单逻辑就是维护一个链表,假如这个数据使用了就提到链表头。假如链表满了,需要淘汰,就从链表尾淘汰。

而 MySQL 对 LRU 链表做了自己的优化改进

既然知道 MySQL 在缓冲区中缓存了查询的数据,但是查询的数据那么多,内存肯定放不下咋办?MySQL 就将数据用一个 LRU 链表进行维护,用来淘汰不常使用的数据,剩下的就是热门数据。

MySQL 对 LRU 改进措施:

  1. 将 LRU 链表分为两部分,前面为热数据去(Young 区),后面为冷数据区(Old 区),Old 区大小占 37%。
    优点:冷热链的切分,排除了全表扫描等类似的大数据量查询,直接把热门数据淘汰出缓冲区的情况。
  2. 对冷链数据移动到热链上做了时间限定。限定时间内对冷链上数据的访问不会移动到热数据区,只有超过这个时间,再次访问冷链上的数据,才会被移动到热数据区。
    优点:避免了短时间内全表扫描等大数据量频繁刷新导致,热门数据被移出热链的情况。
  3. 热链的部分,并非每次访问都会向前移动。只有在热点的后 1/4 内的数据,在访问时才会移动到热链头部,减少移动带来的资源消耗,提升性能。

参数:
innodb_old_blocks_pct: 调整冷热区域占比占比,默认 37%。
innodb_old_blocks_time: 调整限定间隔时间,默认 1S

MySQL 刷新数据落盘的途径

MySQL 后台会有个定时器,定时将数据进行统一落盘,以不影响用 户线程处理正常的请求。

  1. BUF_FLUSH_LIST:从 flush 链表中刷新一部分页面到磁盘,刷新的速率取决 于当时系统是不是很繁忙。
  2. BUF_FLUSH_LRU:从 LRU 链表的冷数据中刷新一部分页面到磁盘。后台线程会定时从 LRU 链表尾部开始扫描一些页面,扫描的页面数量可以通 过系统变量 innodb_lru_scan_depth 来指定,如果从里边儿发现脏页,会把它们 刷新到磁盘。
  3. BUF_FLUSH_SINGLE_PAGE: 有时候后台线程刷新脏页的进度比较慢,导致用户线程在准备加载一个磁盘页到 Buffer Pool 时没有可用的缓存页,这时就会尝试看看 LRU 链表尾部有没有可以直接释放掉的未修改页面,如果没有的话会不得不将 LRU 链表尾部的一个脏页同步刷新到磁盘(和磁盘交互是很慢的,这会降低处理用户请求的速度)。当然,有时候系统特别繁忙时,也可能出现用户线程批量的从 flush 链表中 刷新脏页的情况,很显然在处理用户请求过程中去刷新脏页是一种严重降低处理 速度的行为,这属于一种迫不得已的情况。

InnoDB 的内存结构总结

在这里插入图片描述

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值