【MySQL高阶】17.InnoDB 内存结构
文章目录
- 5. InnoDB 内存结构
- 5.1 InnoDB存储引擎中内存结构的主要组成部分有哪些?
- 5.1.1 新问题
- 5.2 为什么需要内存结构?
- 5.3 缓冲池 - Buffer Pool
- 5.3.1 缓冲池的作用?
- 5.3.2 缓冲池是如何组织数据的?
- 5.3.2.1 缓冲池的结构是怎样的?
- 5.3.2.2 缓冲池中页与页之间是如何建立连接的?
- 5.3.2.3 内存中的数据页与磁盘上的数据页是什么关系?
- 5.3.2.4 Buffer Pool(缓冲池)的大小可以设置吗?
- 5.3.2.5 Buffer Pool中Instances的数量如何确定?
- 5.3.2.6 Chunk的作用是什么?
- 5.3.2.7 Instances中Chunk的数量如何确定?
- 5.3.2.8 控制块与Page是如何初始化的?
- 5.3.2.9 可以通过缓冲池配置来提升性能吗?
5. InnoDB 内存结构
5.1 InnoDB存储引擎中内存结构的主要组成部分有哪些?
InnoDB存储引擎架构链接:MySQL :: MySQL 8.0 Reference Manual :: 17.4 InnoDB Architecture下图是官方给出的
MySQL8.0架构图,可以看出其中InnoDB主要包括内存结构和磁盘结构:
- 内存结构中包括:
- 缓冲池(
Buffer Pool)- 变更缓冲区(
Change Buffer)- 日志缓冲区(
Log Buffer)- 自适应哈希(
Adaptive Hash Index)
5.1.1 新问题
内存结构中各部分的作用是什么?
5.2 为什么需要内存结构?
- 从
MySQL实现的角度来思考这个问题,数据库的作用就是保存数据,用户的真实数据最终都会保存在磁盘上,在查询数据的过程中,如果每次都从磁盘上读取会严重影响效率,为了提高数据的访问效率,InnoDB会把查询到的数据缓存到内存中,当再次查询时,如果目标数据已经存在于内存中,就可以从内存中直接读取,从而大幅提升效率。- 磁盘结构中的文件是用来保存数据实现数据持久化的,内存结构是用来缓存数据提升效率的。
5.3 缓冲池 - Buffer Pool
5.3.1 缓冲池的作用?
- 缓冲池主要用来缓存被访问的
InnoDB表和索引数据页,是主内存中的一片区域,允许直接从内存访问频繁使用的数据从而提高效率。在专用数据库服务器上,通常会将多达80%的物理内存分配给缓冲池。- 其次缓冲池不仅缓存了磁盘的数据页,也存储了锁信息、
Change Buffer信息、Adaptive hash index、Double write buffer等信息。
5.3.2 缓冲池是如何组织数据的?
缓冲池组织数据的方式也可以说是缓冲池用到的数据结构,在这之前回顾一下
InnoDB表空间的存储结构。每个
InnoDB表空间在磁盘上对应一个.ibd文件,其中包含了叶子节点段和非叶子节点段等逻辑段,段中包含了区组,区组中管理着区,区别包含数据页,数据页中包含数据行,每分别对着不同的数据结构目的就是便于数据的管理与高效访问。
5.3.2.1 缓冲池的结构是怎样的?
- 从缓冲池的概念了解到它是主内存中的一片区域,在专用服务器上会将多达
80%的物理内存分配给缓冲池,在这么大的内存空间中如何保证效率就是要解决的问题- 缓冲池也采用与表空间类似的方式对数据进行组织,如下图所示:
- 缓冲池中包含至少一个
Instances实例,Instances是真正的缓冲池的实例对象,内存操作都是在Instances中进行的;- 每个
Instances中包含至少一个Chunk块,Chunk是在服务器运行状态下动态调缓冲池进行大小时操作的块大小;- 每个块中包含和管理若干个从磁盘加载到内存的
Page数据页,内存中的数据页是磁盘中数据页的副本
可以看出缓冲池通过定义不同的数据结构,但最终管理的是每个数据页,这些数据页是从磁盘中加载到内存的,也就是说磁盘中的数据页加载到内存中之后,对应的就是内存中的数据页,并且页与页之间用链表连接。
那么这时就有一个问题,我们知道磁盘中的数据页大小默认是
16KB,并且通过头信息中的next_record记录下一行地址偏移量,在页的结构定义中并没有一个字段用来表示内存中下一页的地址,那么在内存中如何为每个页建立连接呢?
5.3.2.2 缓冲池中页与页之间是如何建立连接的?
- 由于数据页中没有一个字段用来表示内存中下一页的地址,为了每个数据页在内存中实现链表连接,
InnoDB定义了一个叫"控制块"的数据结构,"控制块"中有三个重要的信息分别是:
- 指向数据页的内存地址
- 前一个控制块的内存地址
- 后一下控制块的内存地址
- 之后再用一个双向链表管理每个控制块,如下图所示:
- 为了确定控制块链表的超始位置,专门定义了一个头节点,头节点中包含了三个主要的信息,如图中所示:
- 第一个控制块的内存地址
- 最后一个控制块的内存地址
- 链表中控制块的数量
- 通过遍历控制块链表就可以遍历内存中的数据页
缓冲池中主要缓存的是磁盘中的数据页,由于数据页中没有一个字段用来表示内存中下一页的地址,
InnoDB定义了"控制块"的数据结构,控制块中有一个指向数据页内存地址的指针,实现"控制块"与数据页的一一对应,并且把每个控制块连接成一个双向链表,用一个单独的头节点记录链表的第一个和最后一个节点,这样通过遍历控制块链表就可以遍历内存中的数据页。
5.3.2.3 内存中的数据页与磁盘上的数据页是什么关系?
磁盘上的数据页加载到内存中后,在缓冲池中都有一个内存页与它对应,只不过内存中管理的是控制块组成的链表,控制块有一个指针指向了内存中真实的数据页。
5.3.2.4 Buffer Pool(缓冲池)的大小可以设置吗?
- 可以通过系统变量
innodb_buffer_pool_size进行设置,设置时以字节为单位:默认值为134217728字节,即128MB;最大值取决于CPU架构和操作系统,在32位系统上最大值为4294967295 (2^32 -1),在64位系统上最大值为18446744073709551615 (2^64 -1)- 这里需要注意的是,
InnoDB为"控制块"分配额外的内存空间,也就是说"控制块"并不会占用Buffer Pool的内存空间,所以实际分配的内存总空间比指定的缓冲池大小大10%左右。- 缓冲池设置的值越大,在多次访问相同表数据时,磁盘
I/O就会越少,因为数据都已经缓存在内存中,所以效率也就越高,但是服务器启动时初始化时间会比较长。- 查看缓冲池大小可以使用下面的
SQL语句
mysql> SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; +-------------------------+-----------+ | Variable_name | Value | +-------------------------+-----------+ | innodb_buffer_pool_size | 134217728 | # 以字节为单位默认128M +-------------------------+-----------+ 1 row in set (0.00 sec) mysql>5.3.2.5 Buffer Pool中Instances的数量如何确定?
- 通过系统变量
innodb_buffer_pool_instances可以设置缓冲池实例的个数,默认是1,最大值64;- 当缓冲池的大小 于
1GB时,无论指定innodb_buffer_pool_instances数是多少都会自动调整为1;- 当缓冲池的大小大于
1GB时,innodb_buffer_pool_instances默认值为8,也可以指定大于1的值来设置Instances的数量,多个Instances可以提升服务器的并发性;- 为了获得最佳的效率,通过指定
innodb_buffer_pool_instances和innodb_buffer_pool_size为每个缓冲池实例设置至少为1GB的空间;- 查看
Instances的数量可以使用下面的SQL语句
mysql> SHOW VARIABLES LIKE 'innodb_buffer_pool_instances'; +------------------------------+-------+ | Variable_name | Value | +------------------------------+-------+ | innodb_buffer_pool_instances | 1 | # 默认为1个 +------------------------------+-------+ 1 row in set (0.00 sec) mysql>5.3.2.6 Chunk的作用是什么?
Chunk是在服务器运行状态下动态调缓冲池进行大小时操作的块大小,为避免在调整大小操作期间复制所有缓冲池中的数据页,调整操作以 "块"为基本单位执行;
- 比如在服务器运行时想要调整缓冲池的大小可以通过以下
SQL语句:
#把缓冲池大小设置为1GB mysql> SET GLOBAL innodb_buffer_pool_size=1073741824;
注意:
启动调整大小操作时,在所有活动事务完成后操作才会开始。一旦调整大小操作开始,新的事务和操作必须等到调整大小操作完成才可以访问缓冲池。
5.3.2.7 Instances中Chunk的数量如何确定?
Chunk大小可以通过系统变量innodb_buffer_pool_chunk_size进行设置,默认为134217728字节即128MB;在设置大小时可以以
1048576字节即1MB为单位增加或减少;块中包含的数据页数取决于
innodb_page_size;更改
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_size会自动四舍五入为等于或者倍数于innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances的值。
注意:
innodb_buffer_pool_chunk_size的设置可能会影响到缓冲池大小innodb_buffer_pool_size的值,所以要调整innodb_buffer_pool_chunk_size的值之前一定要计算好
5.3.2.8 控制块与Page是如何初始化的?
- 前面介绍了
Chunk中管理的是具体的数据页,当缓冲池初始化完成时会把每个数据页所占用的内存空间和对应的控制块分配好,只不是没有从磁盘加载数据时,内存中的数据页是空的而已。- 当缓冲池初始化的过程中,会为
Chunk分配置内存空间,此时"控制块"会从Chunk的内存空间从左向右进行初始化,数据页所占的内存会从Chunk的内存空间从右向左进行初始化,当所剩的内存空间不够一组"控制块" + 数据页所占的空间时,就会产生碎片空间,如果适好够用则不会出现碎片空间,如下图所示:
- 内存初始化完成之后,建立控制块与内存中缓冲数据页之间的关系,从左开始第一个控制块指向第一个缓冲数据页的内存地址
- 当前从磁盘中加载数据页时,就可以在把数据缓存在内存中的空闭数据页中
5.3.2.9 可以通过缓冲池配置来提升性能吗?
当然可以,通过配置以下关于缓冲池的系统变量来提高性能,其中包括:
- 配置缓冲池大小
- 配置多个缓冲池实例
- 防止缓冲池扫描
- 配置缓冲池预取(预读)
- 配置缓冲池刷新策略
- 保存和恢复缓冲池状态
- 从核心文件中排除缓冲池页
- 这些变量可以通过以下语句查看
mysql> SHOW VARIABLES LIKE 'innodb_buffer_pool%'; +-------------------------------------+----------------+ | Variable_name | Value | +-------------------------------------+----------------+ | innodb_buffer_pool_chunk_size | 134217728 | | innodb_buffer_pool_dump_at_shutdown | ON | | innodb_buffer_pool_dump_now | OFF | | innodb_buffer_pool_dump_pct | 25 | | innodb_buffer_pool_filename | ib_buffer_pool | | innodb_buffer_pool_in_core_file | ON | | innodb_buffer_pool_instances | 1 | | innodb_buffer_pool_load_abort | OFF | | innodb_buffer_pool_load_at_startup | ON | | innodb_buffer_pool_load_now | OFF | | innodb_buffer_pool_size | 134217728 | +-------------------------------------+----------------+ 11 rows in set (0.00 sec) mysql>