# buffer_pool

buffer pool是主内存中的一个区域,InnoDB 引擎会将访问过的表和索引数据缓存在这个区域中。buffer pool 允许经常使用的数据可以直接从内存中访问,这可以加快处理速度。对于 Dedicated servers (专用服务器), 通常会将高达物理内存的80%分配给 buffer pool。

为了高效率的大量读取操作,缓冲池被分成可容纳多个行的数据页。

为了高效率的缓存管理,缓冲池被实现为一个链接列表的页面;很少使用的数据会通过变种的最近最少使用(LRU)算法从缓存中淘汰。 了解如何利用缓冲池来保持频繁访问的数据在内存中,是 MySQL 调优的一个重要方面。

这是 BufferPool 在 InnoDB 中的位置

# Buffer Pool LRU Algorithm

buffer pool 是使用 LRU 算法变体管理一个链表。当需要为 buffer pool 添加新页面( page )时,最近最少使用的页面会被淘汰,新的页面会添加到链表的中间。这种中间插入策略把链表视为两个子链表:

  • 在头部,包含新添加("年轻")但最近访问的页的 sublist (子列表)
  • 在尾部,包含较旧但访问较少的页面的子链表

# Figure 15.2 Buffer Pool List

该算法将频繁使用的页保存在 New Sublist (新子列表)中。Old Sublist (旧子列表) 包含使用频率较低的页面; 这些页可能会被淘汰 (opens new window))。

默认情况下,算法运行如下:

  • 缓存池的 3/8 用于旧子列表。
  • 列表的 midpoint (中点)是新子列表的尾部与旧子列表的头部相接的边界。
  • 当 InnoDB 将页读入缓冲池时,它最初将其插入到中点(旧子列表的头部),一个页可以被读取,因为它是用户发起的操作(如 SQL 查询)所必需的,或者是作为 InnoDB 自动执行的 read-ahead (opens new window)(预读)操作的一部分。
  • 访问旧子列表中的一个页面会使这个页面"变年轻",将它移动到新子列表的头部。如果这个页面是因为用户发起的操作需要而读取的,那么首次访问将立即发生,这个页面就会年轻化。 如果这个页面是由于预读操作读取的,那么首次访问可能不会立即发生,也可能在这个页面被淘汰之前都不会发生。
  • 当数据库运行的时候,未被访问的 buffer pool 中的页会随时间"变老",通过移动到链表的尾部实现。页既在新子列表,也在旧子列表中"变老",这取决于其他页面"变得年轻"。旧子列表中的页面还会随着新页面插入到中点位置而"变老"。最终,如果一个页面长时间未被使用,它最终会移动到旧子列表的末尾,并被淘汰。

查看 midpoint

mysql> show variables like 'innodb_old_blocks_pct';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_old_blocks_pct | 37    |
+-----------------------+-------+
1 row in set (0.01 sec)

37: 末尾处的 37% 的位置,即末尾 3/8 的位置

# 局部性原理与预读机制

局部性原理

  • 时间局部性:如果一个数据现在被访问了,在近期可能还会被多次访问。
  • 空间局部性:如果一个数据被访问了,那么存储在它附近的数据,很有可能立马被访问。

预读机制

MySQL 为了提高性能,提供了预读机制。

当你从磁盘上加载一个数据页的时候,他可能会连带着把这个数据页相邻的其他数据页,也加载到缓存里去。这个机制会带来这么一个问题:连带的数据页可能在后面的查询或者修改中,并不会用到,但是它们却在 lru 链表的头部。

为什么要使用 LRU

因为内存的大小是有限的,不可能无限的存放数据;

当请求的数据不存在的时候,我们只能去硬盘拿,这样速度会变慢,所以我们要尽可能的避免这种情况的发生。当内存的数据满了的时候,把用户经常访问的数据留着,淘汰一些不经常被访问的数据,腾出位置存放新访问的数据,这样就能提高效率,所以这里选择 LRU (最近最少使用)来对不经常访问的数据进行淘汰。

为什么MySQL 不使用传统的 LRU

使用传统 LRU 的缺点:索引扫描/数据扫描/全表扫描,会使 buffer pool 中大量的页被刷新出去。然而被扫描到的数据页只是本次操作所需要的,并非热点数据。而真正的热点数据还是从磁盘读取,影响了 buffer pool 效率。

为什么不把最新查到的数据放到首部? 放在首部就和传统 LRU 一样了

# Buffer Pool 配置

待完成

参考链接