The InnoDB Storage Engine

Chapter 15 The InnoDB Storage Engine — 官方文档

前言

关于MySQL对于后端程序员的重要性不言而喻,而InnoDB也已经是MySQL默认的存储引擎。
作为我们每天打交道的存储引擎,我们对它可能需要对它有更多的了解。这样对于很多灵异事件,才能从容应对。本次文章的内容大部分来 …

关于MySQL对于后端程序员的重要性不言而喻,而InnoDB也已经是MySQL默认的存储引擎。
作为我们每天打交道的存储引擎,我们对它可能需要对它有更多的了解。这样对于很多灵异事件,才能从容应对。

本次文章的内容大部分来自MySQL8.0的官方文档,之所以没有选择现成的资料,而去挑战自己的弱点:英语。
学习知识应该到知识的源头,可能最开始会很吃力,但我相信对于后续的技术提升一定是有帮助的。与所有希望在技术上深挖的程序员同胞共勉~

关于下面的内容都摘抄自官方文档一小部分,在上面我给出了地址,大家可以去看。
在这里插入图片描述

简介

15.1 Introduction to InnoDB
在这里插入图片描述

首先来看官方文档对InnoDB的解释:InnoDB是一个平衡了高可用和高性能的通用存储引擎。
优势:

  • 保护用户数据:DML操作,通过事务来遵循ACID模型
  • 高性能:行级锁,一致性读取
  • 最小化主键查找的IO:聚簇索引
  • 数据完整性:外键
  • 崩溃恢复
  • 在主内存缓存索引数据和缓存表
  • 外键
  • 校验机制
  • 只要你在设计表时选择了合适的主键,主键列where、order by、group by、join操作会被自动优化
  • 自适应哈希索引

InnoDB架构

15.4 InnoDB 架构 — 官方文档

InnoDB的整体架构可以分为两个部分:内存架构、磁盘架构
在这里插入图片描述

InnoDB内存架构

15.5 InnoDB In-Memory Structures — 官方文档

InnoDB在内存中主要包括下面几个部分:缓冲池、Change缓冲区、自适应哈希索引、Log缓冲区

缓冲池

15.5.1 Buffer Pool — 官方文档

简介: 存储访问时的缓存表和索引数据。在专用服务器上,通常会为缓冲池分配80%的物理内存

作用: 可以快速从内存获取数据,加快了处理速度。

技术要点

Page:为了high-volume的读取效率,缓冲池进一步被分为页的结构。

LRU:为了缓存的管理效率,缓冲池实现page间的链表,使用LRU算法。缓冲池使用调整后的LRU(最近最少使用)算法,当需求添加新的page时,最近最少使用的page被清除,同时新页面被添加到链表的中间部分

这种中间点插入的策略,把链表分为两个子链表
头部:最近被访问过的“年轻”页
尾部:最近被访问的old page

这样使新子列表中保存更重要的page,旧子列表包含较少使用的page,这部分page是被清除的候选page
在这里插入图片描述
默认情况下,算法配置如下:

  • 旧子列表:缓冲池的3/8
  • midpoint(中间点)是新子列表尾部和旧子列表头部的交界
  • 当旧页被访问,会被移动到缓冲池的头部,随着数据库的运行,一直没有被访问的页会一直后移,直至最后被移除。

Change Buffer

15.5.2 Change Buffer — 官方文档

Change Buffer(更改缓冲区)是一种特殊的数据结构,当某些页面不在缓冲池中,缓存会改变二级索引page,这可能会造成insert,update,delete(DML)操作会与其他从缓冲池中的读操作加载的page合并。
在这里插入图片描述

1、二级索引是写入 change buffer,而不是直接写入 buffer pool。
2、会定期读取 change buffer 的内容(二级索引的写入),merge 到 buffer pool。
3、会定期将 buffer pool 中的更新操作写入磁盘。

不同于聚簇索引,二级索引通常不唯一,同时二级索引的插入相对随机。

同时,为了避免频繁的IO随机读写,当更新和删除操作时,并不会立即写入磁盘,而是会选择系统空闲时定期进行写入磁盘的操作。Change Buffer在内存中,是缓冲池中的一部分,在磁盘中,是系统表空间的一部分。

关于顺序读写可参考:MySQL 顺序读写,随机读写,linux底层读写原理

自适应哈希索引

15.5.3 Adaptive Hash Index — 官方文档
AHI ===> Adaptive Hash Index ===> 自适应哈希索引

简介: InnoDB可以基于搜索的模式,使用索引键前缀构建哈希索引,也就是说,这个哈希索引是由经常访问的索引页面构建的。

作用: 在不牺牲事务特性和可靠性的基础上,使InnoDB像一个内存数据库一样工作,也就是说在一定情况下,通过这种哈希索引的方式会提升查询速度。InnoDB中存在一种监视索引搜索的机制,但这种机制有时反倒带来额外的开销。所以在选择是否使用哈希索引前,可能需要做好基准测试,否则还是建议禁用。

自适应哈希索引:我是这样理解的。用 Hash 索引效率高的话,用 hash 索引。如果 hash 索引效率不高的话,就用 B+Tree 索引。这里的转化是 MySQL 自动完成的,所以叫自适应。(可以想象成,Java8 HashMap 底层 链表 转 红黑树的过程)

InnoDB磁盘架构

15.6 InnoDB On-Disk Structures — 官方文档

通过上面的整体架构图可以看到,InnoDB在磁盘中存储的信息包括:各种表空间(TableSpace),Redo Log。

InnoDB对数据存储方式的设计,主要是基于表空间的形式。表空间的种类如下图所示:
在这里插入图片描述
使用InnoDB表的限制,来自MySQL官方文档,感觉有些还是挺有趣的,但可能实际场景中并不会用到:
1、一个表最多包含1017列,表示并没有创建过这样多列的表
2、一个表最多可以创建64个二级索引
3、索引键前缀长度限制为3072字节

Doublewrite Buffer

15.6.4 双写缓冲区

在此还是要放出架构图
在这里插入图片描述

双写缓冲区是一个存储区域,在 InnoDB将page写入InnoDB数据文件之前,先从缓冲池中写入page 。如果在page写入过程中系统鸡掰了,则 InnoDB可以在崩溃恢复期间从双写缓冲区中找到该 page 的良好副本。

虽然数据被写入两次,但双写缓冲区不需要两倍的 I/O 开销或两倍的 I/O 操作。数据以一个大的顺序块写入双写缓冲区,只需fsync()调用一次操作系统(innodb_flush_method设置为 的情况除外 O_DIRECT_NO_FSYNC)。

在 MySQL 8.0.20 之前,doublewrite 缓冲区存储区位于InnoDB系统表空间中。从 MySQL 8.0.20 开始,双写缓冲区存储区域位于双写文件中。

默认情况下,为每个缓冲池实例创建两个双写文件:一个刷新列表双写文件和一个 LRU 列表双写文件。
刷新列表双写文件:用于从缓冲池刷新列表刷新的page。
LRU 列表双写文件:用于从缓冲池 LRU 列表中刷新的page。它还包含用于单页刷新的插槽。

总结:
buffer pool 中的 page 是需要落盘的,直接落盘的话,如果落盘途中系统意外鸡掰的话,就会存在数据丢失的可能性。
由此引出双写文件,让 page 先别直接落盘,先将 page 记录在双写文件中,双写文件再同步到磁盘。此时即使系统鸡掰了,在系统崩溃恢复期也会继续将 双写文件的数据同步到磁盘。
buffer pool -> doublewrite buffer -> disk 这是一个同步的过程。

我的理解(2021-06-23 16:29:19):

  • 数据少了没事,要是不完整的数据就很麻烦。

  • 直接落盘可能会少数据,但是 page 刷到双写文件也是也可能少数据的。所以,无论如何都避免不了内存中的数据会全部刷到盘中。

  • 直接落盘,数据不完整,重启没办法恢复。但是 先刷到双写文件,若双写文件中不完整,就不刷盘,若完整就刷盘,只有磁盘完全同步才进行双写中删除。从而保证磁盘的数据完整性。

Redo Log

重做日志(用于系统崩溃时数据恢复)
15.6.5 Redo Log — 官方文档

也就是ib_logfile0和ib_logfile1两个文件

这里结合的是MySQL的WAL(Write-Ahead Logging)也就是先写日志,再写磁盘,具体过程是下面这样:当有一条记录要更新,先将记录写到redo log,并更新内存,InnoDB会在空闲的时候,把操作记录更新到磁盘。

组提交
InnoDB,像任何其他 符合 ACID的数据库引擎一样,在事务提交之前刷新事务的重做日志。InnoDB 使用组提交 功能将多个刷新请求组合在一起,以避免每次提交一次刷新。使用组提交, InnoDB向日志文件发出一次写入以对大约同时提交的多个用户事务执行提交操作,从而显着提高吞吐量。

Undo Logs

撤销日志(用于事务 rollback 回滚)
15.6.6 Undo Logs — 官方文档

Undo Log是与事务读写关联的,主要作用在事务回滚和多版本并发控制中。

Undo Log在回滚段中存储,回滚段在Undo表空间和全局临时表空间中。Undo log被分为insert undo log 和update undo log。Insert undo log 只在事务回滚时需要,一旦事务提交就被丢弃。Update undo log 也被用在一致性读,在一致性读中可能需要update undo log的信息来生成该行数据早期的版本。

关于undo log的建议
定期地提交事务,包括哪些只包含一致性读的事务,否则,InnoDB不会丢弃update undo log中的数据,回滚段会变得越来越大,占满空间。undo log中回滚段的物理空间,通常小于相应插入或更新的行,可以利用这个信息计算回滚段需要的空间

官方建议

15.1.2 InnoDB 表的最佳实践 — 官方文档

官方建议的最佳实践

  • 指定主键【方便使用主键索引】
  • 外键
  • 关闭自动提交【手动提交】
  • DML的事务进行分组【批量提交事务,而不是单个单个的提交】
  • 不要用lock table,如果希望某行的独占写,用 select … for update【用行锁,不要用表锁】
  • 启用innode_file_per_table【使用独立表】