### 15.5.1 缓冲池

缓冲池是主存中的一块区域`InnoDB`在访问时缓存表和索引数据。缓冲池允许直接从内存中访问经常使用的数据，从而加快处理速度。在专用服务器上，高达 80% 的物理内存通常分配给缓冲池。

为了提高大容量读取操作的效率，缓冲池被划分为可能包含多行的页面。为了缓存管理的效率，缓冲池被实现为页链表；很少使用的数据使用最近最少使用 (LRU) 算法的变体从缓存中老化。

了解如何利用缓冲池将频繁访问的数据保存在内存中是 MySQL 调优的一个重要方面。

#### 缓冲池 LRU 算法

缓冲池使用 LRU 算法的变体作为列表进行管理。当需要空间来向缓冲池添加新页面时，最近最少使用的页面将被逐出，并将新页面添加到列表的中间。此中点插入策略将列表视为两个子列表：

-   在头部，最近访问的新（“年轻”）页面的子列表

-   在尾部，最近访问较少的旧页面的子列表

[](<>)

**图 15.2 缓冲池列表**

<img alt="Content is described in the surrounding text." src="images/innodb-buffer-pool-list.png" height="603" width="376" />

该算法将经常使用的页面保留在新的子列表中。旧的子列表包含不常用的页面；这些页面是候选人[驱逐](glossary.html#glos_eviction).

默认情况下，算法运行如下：

-   缓冲池的 3/8 专用于旧子列表。

-   列表的中点是新子列表的尾部与旧子列表的头部相交的边界。

-   什么时候`InnoDB`将页面读入缓冲池，它最初将其插入到中点（旧子列表的头部）。可以读取页面，因为它是用户启动的操作（如 SQL 查询）所必需的，或者作为[预读](glossary.html#glos_read_ahead)由自动执行的操作`InnoDB`.

-   访问旧子列表中的页面使其“年轻”，将其移动到新子列表的头部。如果页面是因为用户启动的操作需要而被读取的，则第一次访问会立即发生，并且页面会变得年轻。如果由于预读操作而读取了页面，则第一次访问不会立即发生，并且在页面被驱逐之前可能根本不会发生。

-   随着数据库的运行，缓冲池中未被访问的页面通过向列表尾部移动而“老化”。新旧子列表中的页面都会随着其他页面的更新而老化。旧子列表中的页面也会随着页面插入到中点而老化。最终，未使用的页面到达旧子列表的尾部并被驱逐。

    默认情况下，查询读取的页面会立即移动到新的子列表中，这意味着它们在缓冲池中的停留时间更长。表扫描，针对[**mysql转储**](mysqldump.html)操作或`选择`没有的声明`在哪里`例如，子句可以将大量数据带入缓冲池并驱逐等量的旧数据，即使新数据不再使用。类似地，由预读后台线程加载并且仅访问一次的页面被移动到新列表的头部。这些情况可以将经常使用的页面推送到旧的子列表中，在那里它们会被驱逐。有关优化此行为的信息，请参阅[第 15.8.3.3 节，“使缓冲池扫描抗性”](innodb-performance-midpoint_insertion.html)， 和[第 15.8.3.4 节，“配置 InnoDB 缓冲池预取（预读）”](innodb-performance-read_ahead.html).

`InnoDB`标准监视器输出包含多个字段`缓冲池和内存`关于缓冲池 LRU 算法操作的部分。有关详细信息，请参阅[使用 InnoDB 标准监视器监视缓冲池](innodb-buffer-pool.html#innodb-buffer-pool-monitoring).

#### 缓冲池配置

您可以配置缓冲池的各个方面以提高性能。

-   理想情况下，您将缓冲池的大小设置为尽可能大的值，从而为服务器上的其他进程留出足够的内存来运行而不会出现过多的分页。缓冲池越大，越`InnoDB`就像一个内存数据库，从磁盘读取一次数据，然后在后续读取期间访问内存中的数据。看[第 15.8.3.1 节，“配置 InnoDB 缓冲池大小”](innodb-buffer-pool-resize.html).

-   在具有足够内存的 64 位系统上，您可以将缓冲池拆分为多个部分，以尽量减少并发操作之间的内存结构争用。有关详细信息，请参阅[第 15.8.3.2 节，“配置多个缓冲池实例”](innodb-multiple-buffer-pools.html).

-   您可以将经常访问的数据保留在内存中，而不管操作的活动突然高峰会将大量不经常访问的数据带入缓冲池。有关详细信息，请参阅[第 15.8.3.3 节，“使缓冲池扫描抗性”](innodb-performance-midpoint_insertion.html).

-   您可以控制执行预读请求的方式和时间，以异步将页面预取到缓冲池中，以应对即将到来的需求。有关详细信息，请参阅[第 15.8.3.4 节，“配置 InnoDB 缓冲池预取（预读）”](innodb-performance-read_ahead.html).

-   您可以控制何时发生后台刷新以及是否根据工作负载动态调整刷新速率。有关详细信息，请参阅[第 15.8.3.5 节，“配置缓冲池刷新”](innodb-buffer-pool-flushing.html).

-   您可以配置如何`InnoDB`保留当前缓冲池状态以避免服务器重新启动后的长时间预热。有关详细信息，请参阅[第 15.8.3.6 节，“保存和恢复缓冲池状态”](innodb-preload-buffer-pool.html).

#### 使用 InnoDB 标准监视器监视缓冲池

[](<>)[](<>)[](<>)

`InnoDB`标准监视器输出，可以使用[`显示引擎 INNODB 状态`](innodb-standard-monitor.html)，提供有关缓冲池操作的指标。缓冲池指标位于`缓冲池和内存`部分`InnoDB`标准监视器输出：

```
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 2198863872
Dictionary memory allocated 776332
Buffer pool size   131072
Free buffers       124908
Database pages     5720
Old database pages 2071
Modified db pages  910
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 4, not young 0
0.10 youngs/s, 0.00 non-youngs/s
Pages read 197, created 5523, written 5060
0.00 reads/s, 190.89 creates/s, 244.94 writes/s
Buffer pool hit rate 1000 / 1000, young-making rate 0 / 1000 not
0 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read
ahead 0.00/s
LRU len: 5720, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
```

下表描述了由`InnoDB`标准监视器。

中提供的每秒平均值`InnoDB`标准监视器输出基于经过的时间`InnoDB`标准监视器输出最后一次打印。

[](<>)

**表 15.2 InnoDB 缓冲池指标**

| 姓名 | 描述 |
| --- | --- |
| 分配的总内存 | 为缓冲池分配的总内存（以字节为单位）。 |
| 分配的字典内存 | 分配的总内存`InnoDB`以字节为单位的数据字典。 |
| 缓冲池大小 | 分配给缓冲池的总页面大小。 |
| 空闲缓冲区 | 缓冲池空闲列表的总页面大小。 |
| 数据库页面 | 缓冲池 LRU 列表的总页面大小。 |
| 旧数据库页面 | 缓冲池旧 LRU 子列表的总页面大小。 |
| 修改的数据库页面 | 当前在缓冲池中修改的页数。 |
| 待读 | 等待读入缓冲池的缓冲池页数。 |
| 挂起的写入 LRU | 从 LRU 列表底部开始写入的缓冲池中旧脏页的数量。 |
| 待写刷新列表 | 检查点期间要刷新的缓冲池页数。 |
| 待写单页 | 缓冲池中挂起的独立页面写入数。 |
| 页面变得年轻 | 缓冲池 LRU 列表中年轻的页面总数（移动到“新”页面子列表的头部）。 |
| 页面不年轻 | 缓冲池 LRU 列表中未年轻化的页面总数（保留在“旧”子列表中但未被年轻化的页面）。 |
| 年轻人/秒 | 每秒访问缓冲池 LRU 列表中旧页面的平均次数，这些访问导致页面变得年轻。有关详细信息，请参阅此表后面的注释。 |
| 非青少年 | 每秒访问缓冲池 LRU 列表中导致页面不年轻的旧页面的平均次数。有关详细信息，请参阅此表后面的注释。 |
| 已读页数 | 从缓冲池中读取的总页数。 |
| 创建的页面 | 在缓冲池中创建的总页数。 |
| 写的页数 | 从缓冲池写入的总页数。 |
| 读/秒 | 每秒平均每秒读取缓冲池页面的次数。 |
| 创建/秒 | 平均每秒创建的缓冲池页数。 |
| 写/秒 | 每秒平均写入缓冲池页面的次数。 |
| 缓冲池命中率 | 从缓冲池读取的页面与从磁盘存储读取的页面的缓冲池页面命中率。 |
| 年轻化率 | 页面访问导致页面年轻的平均命中率。有关详细信息，请参阅此表后面的注释。 |
| 不是（年轻化率） | 页面访问没有导致页面年轻的平均命中率。有关详细信息，请参阅此表后面的注释。 |
| 预读页数 | 每秒平均预读操作。 |
| 页面被驱逐而无法访问 | 在没有从缓冲池访问的情况下被逐出的页面的每秒平均数。 |
| 随机预读 | 随机预读操作的每秒平均值。 |
| LRU 镜头 | 缓冲池 LRU 列表的总页面大小。 |
| 解压\_LRU 镜头 | 解压缓冲池的长度（以页为单位）\_LRU 列表。 |
| 输入输出总和 | 访问的缓冲池 LRU 列表页的总数。 |
| I/O 电流 | 当前时间间隔内访问的缓冲池 LRU 列表页的总数。 |
| I/O 解压总和 | 缓冲池解压总数\_LRU 列表页解压缩。 |
| I/O 解压文件 | 缓冲池解压总数\_在当前时间间隔内解压的 LRU 列表页。 |

**笔记**：

-   这`年轻人/秒`指标仅适用于旧页面。它基于页面访问次数。给定页面可以有多个访问，所有这些都被计算在内。如果你看到很低`年轻人/秒`当没有大扫描发生时，考虑减少延迟时间或增加用于旧子列表的缓冲池的百分比。增加百分比会使旧的子列表变大，从而使该子列表中的页面移动到尾部所需的时间更长，这增加了再次访问这些页面并使其变得年轻的可能性。看[第 15.8.3.3 节，“使缓冲池扫描抗性”](innodb-performance-midpoint_insertion.html).

-   这`非青少年`指标仅适用于旧页面。它基于页面访问次数。给定页面可以有多个访问，所有这些都被计算在内。如果你没有看到更高的`非青少年`执行大型表扫描时的值（以及更高`年轻人/秒`值），增加延迟值。看[第 15.8.3.3 节，“使缓冲池扫描抗性”](innodb-performance-midpoint_insertion.html).

-   这`年轻化`rate 考虑所有缓冲池页面访问，而不仅仅是旧子列表中的页面访问。这`年轻化`率和`不是`rate 通常不会与整个缓冲池命中率相加。旧子列表中的页面命中会导致页面移动到新子列表，但新子列表中的页面命中会导致页面移动到列表的头部，前提是它们与头部有一定距离。

-   `不是（年轻化率）`是由于定义的延迟而导致页面访问未导致页面年轻的平均命中率[`innodb_old_blocks_time`](innodb-parameters.html#sysvar_innodb_old_blocks_time)没有被满足，或者由于新子列表中的页面命中没有导致页面移动到头部。这个比率考虑了所有缓冲池页面访问，而不仅仅是旧子列表中页面的访问。

    缓冲池[服务器状态变量](server-status-variables.html)和[`INNODB_BUFFER_POOL_STATS`](information-schema-innodb-buffer-pool-stats-table.html)表提供了许多相同的缓冲池指标`InnoDB`标准监视器输出。有关详细信息，请参阅[例 15.10，“查询 INNODB\_缓冲\_水池\_统计表”](innodb-information-schema-buffer-pool-tables.html#innodb-information-schema-buffer-pool-stats-example).
