InnoDB 内存结构
InnoDB In-Memory Structures
InnoDB 在内存中的结构包括 缓冲池(Buffer Pool)、Change Buffer、Adaptive Hash Index、Log Buffer。
Buffer Pool 缓冲池
缓冲池是主内存中的一个区域,用于在 InnoDB访问表和索引数据时对其进行缓存。缓冲池允许直接从内存中访问经常使用的数据,从而加快处理速度。在专用服务器上,高达 80% 的物理内存通常分配给缓冲池。
为了提高大容量读取操作的效率,缓冲池被划分为可能包含多行的页面。为了缓存管理的效率,缓冲池被实现为页链表;很少使用的数据使用最近最少使用 (LRU) 算法的变体从缓存中老化。
了解如何利用缓冲池将频繁访问的数据保存在内存中是 MySQL 调优的一个重要方面。
缓冲池 LRU 算法
缓冲池使用 LRU 算法的变体作为列表进行管理。当需要空间来向缓冲池添加新页面时,最近最少使用的页面将被逐出,并将新页面添加到列表的中间。此中点插入策略将列表视为两个子列表:
-
在头部,最近访问 的新( “年轻” )页面的子列表
-
在尾部,最近访问较少的旧页面的子列表
缓冲池列表
该算法将经常使用的页面保留在新的子列表中。旧的子列表包含不常用的页面;这些页面是驱逐的候选者。
默认情况下,算法运行如下:
-
缓冲池的 3/8 专用于旧子列表。
-
列表的中点是新子列表的尾部与旧子列表的头部相交的边界。
-
当InnoDB将页面读入缓冲池时,它最初将其插入到中点(旧子列表的头部)。一个页面可以被读取,因为它是用户启动的操作(如 SQL 查询)所必需的,或者是 由InnoDB.
-
访问旧子列表中的页面使其 “年轻”,将其移动到新子列表的头部。如果页面是因为用户启动的操作需要而被读取的,则第一次访问会立即发生,并且页面会变得年轻。如果由于预读操作而读取了页面,则第一次访问不会立即发生,并且可能在页面被驱逐之前根本不会发生。
-
随着数据库的运行,缓冲池中未被访问的页面通过向列表尾部移动而“老化” 。新旧子列表中的页面都会随着其他页面的更新而老化。旧子列表中的页面也会随着页面插入到中点而老化。最终,未使用的页面到达旧子列表的尾部并被驱逐。
默认情况下,查询读取的页面会立即移动到新的子列表中,这意味着它们在缓冲池中的停留时间更长。例如,对mysqldump操作或 SELECT没有子句的语句 执行的表扫描WHERE可以将大量数据带入缓冲池并驱逐等量的旧数据,即使新数据不再使用。类似地,由预读后台线程加载且仅访问一次的页面被移动到新列表的头部。这些情况可以将经常使用的页面推送到旧的子列表中,在那里它们会被驱逐。有关优化此行为的信息,请参阅 第 15.8.3.3 节,“使缓冲池扫描抗性”和 第 15.8.3.4 节,“配置 InnoDB 缓冲池预取(预读)”。
InnoDBBUFFER POOL AND MEMORY标准监视器输出包含有关缓冲池 LRU 算法操作的部分中的几个字段。有关详细信息,请参阅使用 InnoDB 标准监视器监视缓冲池。
Change Buffer 更改缓冲区
更改缓冲区是一种特殊的数据结构,它会缓存对没有在缓存池中的二级索引页面的更改。这些缓存的改变,可能由 INSERT UPDATE DELETE 操作 (DML) 导致的结果,稍后在页面通过其他读取操作加载到缓冲池中时被合并。
与聚集索引不同,二级索引通常是非唯一的,并且插入二级索引以相对随机的顺序发生。类似地,删除和更新可能会影响索引树中不相邻的二级索引页面。稍后合并缓存的更改,当受影响的页面被其他操作读入缓冲池时,避免了将二级索引页面从磁盘读入缓冲池所需的大量随机访问 I/O。
在系统大部分空闲或缓慢关闭期间运行的清除操作会定期将更新的索引页写入磁盘。与将每个值立即写入磁盘相比,清除操作可以更有效地为一系列索引值写入磁盘块。
当有许多受影响的行和许多二级索引要更新时,更改缓冲区合并可能需要几个小时。在此期间,磁盘 I/O 会增加,这可能会导致磁盘绑定查询显着变慢。在事务提交后,甚至在服务器关闭和重新启动之后,更改缓冲区合并也可能继续发生(有关更多信息,请参阅第 15.21.3 节,“强制 InnoDB 恢复” )。
在内存中,更改缓冲区占据了缓冲池的一部分。在磁盘上,更改缓冲区是系统表空间的一部分,其中索引更改在数据库服务器关闭时被缓冲。
缓存在更改缓冲区中的数据类型由 innodb_change_buffering变量控制。有关详细信息,请参阅 配置更改缓冲。您还可以配置最大更改缓冲区大小。有关更多信息,请参阅 配置更改缓冲区最大大小。
如果索引包含降序索引列或主键包含降序索引列,则二级索引不支持更改缓冲。
Adaptive Hash Index 自适应哈希索引
自适应散列索引能够InnoDB在系统上执行更像内存数据库,具有适当的工作负载组合和足够的内存用于缓冲池,而不会牺牲事务功能或可靠性。innodb_adaptive_hash_index
变量启用自适应哈希索引,或在服务器启动时 --skip-innodb-adaptive-hash-index
.
根据观察到的搜索模式,使用索引键的前缀构建哈希索引。前缀可以是任意长度,也可能只有B树中的一些值出现在哈希索引中。哈希索引是针对经常访问的索引页面的需求而构建的。
如果一个表几乎完全适合主内存,哈希索引通过启用任何元素的直接查找来加速查询,将索引值转换为一种指针。 InnoDB具有监视索引搜索的机制。如果InnoDB注意到查询可以从构建哈希索引中受益,它会自动这样做。
对于某些工作负载,哈希索引查找的加速大大超过了监控索引查找和维护哈希索引结构的额外工作。在繁重的工作负载(例如多个并发连接)下,对自适应哈希索引的访问有时会成为争用的来源。带有 LIKE运算符和% 通配符的查询也往往不会受益。对于不能从自适应哈希索引中受益的工作负载,关闭它可以减少不必要的性能开销。因为很难提前预测自适应哈希索引是否适合特定系统和工作负载,请考虑在启用和禁用它的情况下运行基准测试。
Log Buffer 日志缓冲区
日志缓冲区是保存要写入磁盘上日志文件的数据的内存区域。日志缓冲区大小由 innodb_log_buffer_size变量定义。默认大小为 16MB。日志缓冲区的内容会定期刷新到磁盘。大型日志缓冲区使大型事务能够运行,而无需在事务提交之前将重做日志数据写入磁盘。因此,如果您有更新、插入或删除许多行的事务,则增加日志缓冲区的大小可以节省磁盘 I/O。
innodb_flush_log_at_trx_commit
变量控制日志缓冲区的内容如何写入和刷新到磁盘。innodb_flush_log_at_timeout
变量控制日志刷新频率。