【1】介绍

InnoDB维护一个称为缓冲池的存储区域, 用于在内存中缓存数据和索引。利用它将经常访问的数据保存在内存中,是 MySQL 调优的一个重要方面。

【2】缓冲池预取(Read-Ahead)

【2-1】含义

预读是当发起一个 I/O 请求时候,它异步地预取缓冲池中的多个页面。也就是认为你接下来很有可能会查询当前请求后面的数据,所以预先把接下来的部分数据页读取出来。

InnoDB 以 64 个 page 为一个 extent。以按 page 或 extent 为预读单位分为两种预算算法 线性预读 和 随机预读

【2-2】预读算法

线性预读(Linear read-ahead)

线性预读 以 extent 作为预定范围单位,它根据缓冲池中按顺序访问的页面来预测可能很快需要哪些页面。

触发时机由参数 innodb_read_ahead_threshold 控制,默认为 56 。含义是当一个 extent 中被顺序读取的页个数大于等于这个值,就会把下一个 extent 读取到 buffer pool 中,这个操作是伊布执行的。

show variables like 'innodb_read_ahead_threshold';

+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| innodb_read_ahead_threshold | 56    |
+-----------------------------+-------+

若该值没有被设置,InnoDB 只会在读取当前 extent 的最后一页时计算是否对整个next extent 发出异步预取请求。

所以可以根据你的数据特点合理的增加过减小这个值,从来享受到预读带来的性能提升。

随机预读 (Random read-ahead )

随机预读 以 页 作为预定范围单位,也就是一个 extent 中的 64个页为预读范围。它根据缓冲池中已有的页面来预测何时可能很快需要页面,而不管这些页面的读取顺序如何。

触发由参数 innodb_random_read_ahead 控制,默认是关闭的,开启需设置为 ON。

由于随机预读相对复杂性,同时在性能也存在不稳定性,在5.5中已经将这种预读方式废弃。

涉及到的参数如下:

  • Innodb_buffer_pool_read_ahead - 预读后台线程读入 InnoDB 缓冲池的页数。
  • Innodb_buffer_pool_read_ahead_evicted - 由预读后台线程读入 InnoDB 缓冲池的页面数,这些页面随后在未被查询访问的情况下被清除。
  • Innodb_buffer_pool_read_ahead_rnd - InnoDB 启动的“随机”预读次数。

【3】总结

预读是后台线程异步完成的,由参数 innodb_read_io_threads 控制数量。默认 4 个。

show variables like 'innodb_read_io_threads';

+------------------------+-------+
| Variable_name          | Value |
+------------------------+-------+
| innodb_read_io_threads | 4     |
+------------------------+-------+

可以使用 SHOW ENGINE INNODB STATUS;看到实时的信息。可以看到类似的信息:

BUFFER POOL AND MEMORY
----------------------
Total memory allocated 5494538240; in additional pool allocated 0
Total memory allocated by read views 1832
Internal hash tables (constant factor + variable factor)
    Adaptive hash index 234507568 	(84999368 + 149508200)
    Page hash           2657176 (buffer pool 0 only)
    Dictionary cache    33247833 	(21251248 + 11996585)
    File system         1092256 	(812272 + 279984)
    Lock system         13289536 	(13281976 + 7560)
    Recovery system     0 	(0 + 0)
Dictionary memory allocated 11996585
Buffer pool size        327678
Buffer pool size, bytes 5368676352
Free buffers            2042
Database pages          316511
Old database pages      79091
Modified db pages       6328
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 130098310, not young 30432004147
7.29 youngs/s, 30.49 non-youngs/s
Pages read 562355000, created 7100860, written 152881342
16.18 reads/s, 0.11 creates/s, 28.29 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

// Pages read ahead:表示每秒读入的pages
// evicted without access:表示每秒读出的pages
//  Random read ahead 0.00/s 随机读通常是0,因为一般不开启。

LRU len: 316511, unzip_LRU len: 0
I/O sum[4754]:cur[52], unzip sum[0]:cur[0]

InnoDB 提供一些信息直观的显示预读效果,可使用语句 show global status like ‘%read_ahead%’;

show global status like '%read_ahead%';

+---------------------------------------+-----------+
| Variable_name                         | Value     |
+---------------------------------------+-----------+
| Innodb_buffer_pool_read_ahead         | 122663562 |
| Innodb_buffer_pool_read_ahead_evicted | 0         |
| Innodb_buffer_pool_read_ahead_rnd     | 0         |
+---------------------------------------+-----------+

Innodb_buffer_pool_read_ahead 通过预读(后台线程)读入innodb buffer pool中数据页数。
Innodb_buffer_pool_read_ahead_evicted 过预读来的数据页没有被查询访问就被清理的pages,也就是无效预读的页数。
Innodb_buffer_pool_read_ahead_rnd 启动的“随机”预读次数,通常不启用所以是 0.