本文MySQL版本是8.X版本
这是官方文档给出来的架构图:MySQL :: MySQL 8.0 Reference Manual :: 17.4 InnoDB Architecture
可以看出,整体上是分成两部分的:内存结构(提高效率)和磁盘结构(数据持久化),下面将把每个区域都大致做一个介绍。
内存结构
缓冲池(Buffer Pool)
缓冲池是用来缓存各种数据,最主要的就是缓存 从磁盘加载的 数据页,当数据库请求数据时,InnoDB首先查看缓冲池中是否存在所需的数据,如果存在,则直接从内存中读取,从而大幅提高读取速度。
结构
Instances
缓冲池至少有一个Instances实例对象。内存操作都是在Instances中进行的。
Instances的优点:
- 提升并发性能: 将Buffer Pool分成多个实例可以减少锁竞争,从而提高并发读取的效率。不同的连接可以并行地访问不同的Buffer Pool实例,减少了单一全局锁的压力。
- 优化内存管理: 每个Buffer Pool实例可以配置不同的大小,使得可以更好地控制内存的使用和分配。这对于大内存系统特别有用,可以有效地分配和管理大量的内存。
Instances数量相关:
- 通过系统变量 innodb_buffer_pool_instances 可以设置缓冲池实例的个数,默认是 1,最大值为 64。
- 当缓冲池的大小小于 1GB 时,无论指定 innodb_buffer_pool_instances 数是多少都会自动调整为 1。
- 当缓冲池的大小大于等于 1GB 时,默认的 innodb_buffer_pool_instances 值为 8,也可以指定大于 8的值来设置实例的数量。增加多个实例可以提升服务器的并发性能。
- 为了获得最佳效率,建议通过指定 innodb_buffer_pool_instances 和 innodb_buffer_pool_size,为每个缓冲池实例设置至少 1GB 的空间。
-
# 查看instances的个数 show variables like "innodb_buffer_pool_instances";
Chunk
每个Instances实例中至少有一个Chunk块。每个Chunk中管理的是若干从磁盘加载到内存的Page数据页。
Chunk的优点
Chunk是在服务器运行状态下动态调整缓冲池大小时的操作单位。为了避免在调整大小过程中复制所有缓冲池中的数据页,调整操作以“块”为基本单位执行。
Chunk数量相关:
- Chunk大小可以通过系统变量innodb_buffer_pool_chunk_size进行设置,默认为134217728字节即128MB。在设置大小时,可以以1048576字节即1MB为单位增加或减少。
- 更改innodb_buffer_pool_chunk_size的值时需注意以下条件:
- 如果innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances大于当前缓冲池大小,innodb_buffer_pool_chunk_size将被截断为innodb_buffer_pool_size / innodb_buffer_pool_instances。
- 缓冲池大小必须始终等于或是innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances的倍数。如果修改了innodb_buffer_pool_chunk_size的值导致不符合这个规则,缓冲池在初始化时会自动四舍五入为最接近或者倍数于innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances的值。
Page
数据页/缓冲页,就是磁盘中数据页的数据结构,读到内存后与磁盘中的对应。
页与页如何连接
InnoDB中为了实现数据页在内存中的链表连接,定义了一个称为"控制块"(control block)的数据结构,其中包含三个重要的信息:指向数据页的内存地址 前一个控制块的内存地址 后一个控制块的内存地址
为了确定控制块链表的起始位置,InnoDB专门定义了一个头节点,头节点中包含了三个主要的信息:第一个控制块的内存地址 最后一个控制块的内存地址 链表中控制块的数量
控制块和页初始化
①在缓冲池初始化过程中,会为每个Chunk(内存块)分配内存空间。控制块会从Chunk的内存空间的左侧向右侧进行初始化,而数据页所占的内存则从右侧向左侧初始化。这意味着控制块的内存空间分配优先于数据页的分配。缓冲池在初始化过程中,剩余的内存空间无法容纳一个完整的控制块和其对应的数据页,就会产生碎片化的空间。
②在内存初始化完成后,建立了控制块与缓冲数据页之间的关系。
③后面从磁盘加载数据页是,直接缓存到缓冲页中即可。
页的管理
当磁盘中的页缓存到数据页之后,如果对于页有 删除、修改操作,此时就是操作缓存的页。
缓冲池使用三个链表来维护缓冲页,这三个链表代表页在内存的三种状态。
Free List:管理未被使用的内存页,即空闲页。当执行查询操作时,如果所需的页已经在缓冲池中,则直接返回数据。如果不在缓冲池中且Free List不为空,则从磁盘中读取对应的数据页,并将其存放到Free List中的某一页中。随后,从Free List中移除这个页,并将其放入LRU List中。
LRU List:管理从磁盘读取到的数据页,包括未修改的(干净页)和已修改的(脏页)。根据LRU(最近最少使用)算法对链表中的页节点进行维护和淘汰。数据库刚启动时,LRU List是空的,所有从磁盘读取的页都存放在Free List中。当数据从磁盘读取到缓冲池时,首先从Free List中查找可用的空闲页。如果找到,则将该页从Free List中删除并加入LRU List;如果没有找到,则根据LRU算法淘汰LRU List末尾的页,并将该页的内存空间分配给新的数据页。
Flush List:当LRU List中的页被修改后(即成为脏页),会将其加入Flush List。Flush List专门用来管理需要被写回到磁盘的脏页。数据库通过刷盘机制将Flush List中的脏页刷回磁盘,以确保数据的持久性和一致性。刷盘操作完成后,将脏页的空间释放,并返回给Free List。
缓冲池使用LRU算法管理链表,当有新页面添加到缓冲池时,最近最少使用的页面将被淘汰,并将新页面添加到列表的中间。这种中点插入策略将列表视为两个子列表:
链表头部,是存放最近访问的新页面(年轻页面)子列表;
链表尾部,是存放最近较少访问的旧页面子列表。