一、Buffer Pool(缓存池)

缓存池是主内存中的一块区域,当表数据和索引数据被访问时,InnoDB会将其放入缓存池中缓存起来。
为了加速数据的处理,缓存池允许直接从内存中读取被频繁使用的数据。
在专用的数据服务器上,大约 80% 的物理内存会分配给缓存池进行使用

1)内部结构

为了提高大容量读取操作的效率,缓冲池被分割成多个数据页,每个数据页存储了多条数据记录。
为了提高缓存管理的效率(也就是方便进行数据页的插入、移动或删除操作,那么使用链表数据结构再合适不过),缓存池是用页对象的链表形式实现的
会使用最近最少使用算法(LRU)的一种变体,来将最近最少使用的数据从缓存中老化(移除)。

2)缓存池的 LRU 算法

InnoDB使用的是一种LRU算法变体来对缓存池进行管理的。

算法将频繁使用的数据页放在 new sublist 中,old sublist中保存的是不经常使用的数据页(这些数据页都是待删除的候选项),列表的中间位置是 new sublist 尾部与 old sublist 头部相交的边界。

当需要为缓存池添加新的数据页时,最近最少使用的数据页会从new sublist被移出,新的数据页会添加到列表的中部。
中插策略将这个列表当作两个子列表

  • 在链表的头部,存储的是最近被访问的新数据页(“young pages”)
  • 在链表的尾部,存储的是很少使用的旧数据页的子列表
image-20220902054137882
主要任务(一个规则三个策略)
  • 三八规则(冷热规则)

    buffer pool 中38\frac{3}{8}的内存空间是专门给 old sublist 存储数据页的

  • 中插策略

    当 InnoDB 将一个数据页的数据读进 buffer pool 时,它是将该数据页插入到中点位置的(旧子列表的头位置)

  • young 策略

    old sublist中的某个数据页只要被访问了,那么该数据页就会变成 “young”,InnoDB就会将其移动到 new sublist 的头部位置

    如果用户发起某个操作(a user-initiated operation)需要读取某个数据页,那么该数据页立即会被第一次访问且变成 “young”

    由于预读操作(a read-ahead operation)而被读取的数据页不会立即被第一次访问,而且在数据页被丢弃前可能都不会发生。

  • evicted 策略

    随着数据库的运行,缓存池中未被访问到的数据页渐渐会"老化",从而会向列表尾部移动。
    新、旧子列表中的数据页都会随着其他数据页的更新而"老化"。old sublist 中的数据页也会随着数据页在中点位置的插入而 “老化”。
    最终,当数据页一直未被使用,就会到达 old sublist 的尾部,最终就会 老化

默认情况下,被查询读取到的数据页,会被立即移动到new sublist 头部,这意味着它们在缓存池保存的时间会更长。
如果是 执行的是类似 mysqldump 操作 或 某个不带 WHERE 子句的 SELECT 语句的全表扫描,那么会将大量的数据引入到缓存池中且导致new sublist中相同数量的旧数据"老化",即使这些新数据不再被使用。相似地,被后端线程通过预读操作和一次性访问加载的数据页,会被移动到 new sublist头部。这些场景会导致被频繁使用的数据页流向old sublist,最终它们会成为 "老化"的对象。

InnoDB标准监视输出中包含 BUFFER POOL AND MEMORY 部分中有关缓存池LRU算法操作的几个字段

缓存池配置

可以对缓存池的多个方面进行配置,从而提高性能

  • 理想情况下,尽可能地将 buffer pool 的存储空间设置成足够大

    因为有足够的内存,才可以在不需要在过多分页的情况下执行服务器上的某些操作。

    缓存池越大,InnoDB就越像一个内存数据库,只需要从磁盘读取一次,然后在接下来的读取操作中就直接从内存中获取数据。

  • 在一个有充足内存的64位系统上,可以将 buffer pool 分成多个部分,从而减少并发操作之间的内存结构争用问题。

  • 在不受某些操作将大批量非频繁使用的数据引入缓存池的影响下,而将被频繁使用的数据永久保留在内存中

  • 控制好预读请求的执行时间与执行方式,从而将数据页以异步方式读取到 buffer pool中,以便于应对即将到来的需求

  • 控制后台刷新的时间以及根据工作负载情况判断是否需要动态调整刷新的频率。

  • 配置InnoDB保存当前缓存池状态的方式,从而避免服务器重启后的一段 ”热机时间“的消耗

使用InnoDB标准监视器监控缓存池

InnoDB标准监视器的输出,提供有关缓冲池操作的指标,可以使用 SHOW ENGINE INNODB STATUS语句进行查看

InnoDB标准监视器的输出
```()
=====================================
2022-08-16 05:29:43 140109353817856 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 61 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 4 srv_active, 0 srv_shutdown, 28522 srv_idle
srv_master_thread log flush and writes: 0
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 1
OS WAIT ARRAY INFO: signal count 1
RW-shared spins 0, rounds 0, OS waits 0
RW-excl spins 0, rounds 0, OS waits 0
RW-sx spins 0, rounds 0, OS waits 0
Spin rounds per wait: 0.00 RW-shared, 0.00 RW-excl, 0.00 RW-sx
------------
TRANSACTIONS
------------
Trx id counter 23819
Purge done for trx's n:o < 23817 undo n:o < 0 state: running but idle
History list length 0
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421584865787904, not started
0 lock struct(s), heap size 1128, 0 row lock(s)
---TRANSACTION 421584865787096, not started
0 lock struct(s), heap size 1128, 0 row lock(s)
---TRANSACTION 421584865786288, not started
0 lock struct(s), heap size 1128, 0 row lock(s)
---TRANSACTION 421584865785480, not started
0 lock struct(s), heap size 1128, 0 row lock(s)
--------
FILE I/O
--------
I/O thread 0 state: waiting for completed aio requests (insert buffer thread)
I/O thread 1 state: waiting for completed aio requests (log thread)
I/O thread 2 state: waiting for completed aio requests (read thread)
I/O thread 3 state: waiting for completed aio requests (read thread)
I/O thread 4 state: waiting for completed aio requests (read thread)
I/O thread 5 state: waiting for completed aio requests (read thread)
I/O thread 6 state: waiting for completed aio requests (write thread)
I/O thread 7 state: waiting for completed aio requests (write thread)
I/O thread 8 state: waiting for completed aio requests (write thread)
I/O thread 9 state: waiting for completed aio requests (write thread)
Pending normal aio reads: [0, 0, 0, 0] , aio writes: [0, 0, 0, 0] ,
 ibuf aio reads:, log i/o's:, sync i/o's:
Pending flushes (fsync) log: 0; buffer pool: 0
1031 OS file reads, 236 OS file writes, 52 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.03 writes/s, 0.02 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
 insert 0, delete mark 0, delete 0
discarded operations:
 insert 0, delete mark 0, delete 0
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 1 buffer(s)
Hash table size 34679, node heap has 0 buffer(s)
Hash table size 34679, node heap has 2 buffer(s)
Hash table size 34679, node heap has 6 buffer(s)
0.00 hash searches/s, 0.00 non-hash searches/s
---
LOG
---
Log sequence number          31164471
Log buffer assigned up to    31164471
Log buffer completed up to   31164471
Log written up to            31164471
Log flushed up to            31164471
Added dirty pages up to      31164471
Pages flushed up to          31164471
Last checkpoint at           31164471
20 log i/o's done, 0.00 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 0
Dictionary memory allocated 403013
Buffer pool size   8192
Free buffers       7031
Database pages     1152
Old database pages 408
Modified db pages  0
Pending reads      0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 63, not young 1666
0.00 youngs/s, 0.00 non-youngs/s
Pages read 1008, created 151, written 182
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 1152, unzip_LRU len: 0
I/O sum[1]:cur[0], unzip sum[0]:cur[0]
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
0 read views open inside InnoDB
Process ID=1, Main thread ID=140109396829952 , state=sleeping
Number of rows inserted 180, updated 0, deleted 0, read 180
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
Number of system rows inserted 0, updated 317, deleted 0, read 9602
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================
```()
	
InnoDB缓存池指标
名称 描述
总分配内存(Total memory allocated) 分配给缓存池的总内存大小,单位:字节
已分配的字典内存(Dictionary memory allocated) 给InnoDB数据字典分配的总内存大小,单位:字节
缓存池大小(Buffer pool size) 分配给缓存池的总页数
空闲缓存(Free buffers) 缓存池中空闲的页数
数据库页数(Database pages) 缓存池LRU列表的总页数
旧数据库页数(Old database pages) 缓存池旧的LRU子列表的总页数
修改后的数据库页数(Modified db pages) 缓存池中被修改的页数
等待读(Pending reads) 等待被读进缓存池的缓存池页数
等待写入LRU(Pending writes LRU) 缓存池中需要从LRU列表底部开始写入的旧的脏数据页数
等待写入刷新列表(Pending writes flush list) 在检查点期间需要刷新的缓存池页数
等待写入单个数据页(Pending writes single page) 缓存池中等待单独写入的数据页数
“年轻的” 数据页(Pages made young) 缓存池的LRU列表中的”年轻“的数据页大小
"非年轻的"数据页(Pages made not young) 缓存池的LRU列表中的"非年轻"的数据页大小
youngs/s The per second average of accesses to old pages
in the buffer pool LRU list that have resulted in
making pages young. See the notes that follow
this table for more information.
non-youngs/s The per second average of accesses to old pages
in the buffer pool LRU list that have resulted in not
making pages young. See the notes that follow
this table for more information
Pages read The total number of pages read from the buffer
pool.
Pages created The total number of pages created within the
buffer pool.
Pages written The total number of pages written from the buffer
pool.
reads/s The per second average number of buffer pool
page reads per second.
creates/s The average number of buffer pool pages created
per second.
writes/s The average number of buffer pool page writes
per second.
Buffer pool hit rate The buffer pool page hit rate for pages read from
the buffer pool vs from disk storage.
young-making rate The average hit rate at which page accesses have
resulted in making pages young. See the notes
that follow this table for more information.
not (young-making rate) The average hit rate at which page accesses
have not resulted in making pages young. See the
notes that follow this table for more information.
Pages read ahead The per second average of read ahead
operations.
Pages evicted without access The per second average of the pages evicted
without being accessed from the buffer pool.
Random read ahead The per second average of random read ahead
operations.
LRU len The total size in pages of the buffer pool LRU list.
unzip_LRU len The length (in pages) of the buffer pool
unzip_LRU list.
I/O sum The total number of buffer pool LRU list pages
accessed.
I/O cur The total number of buffer pool LRU list pages
accessed in the current interval.
I/O unzip sum The total number of buffer pool unzip_LRU list
pages decompressed.
I/O unzip cur The total number of buffer pool unzip_LRU list
pages decompressed in the current interval.

注意事项:

  • youngs/s (每秒变年轻的数据页个数)

    只适用于old pages,是基于被访问的数据页的数量

  • non-youngs/s (每秒变得非年轻得数据页个数)

    只适用于旧的数据页,是基于访问页的数量的

  • young-making rate(年轻化比率)

    年轻化比率是针对所有缓存池页面的访问来统计的,而不仅仅是针对旧子列表中的页面访问进行统计的

    年轻化比率和非年轻化比率通常不会增加缓存池的命中率。对旧子列表中的页面的命中会导致数据页移动到新子列表中,但是对新子列表页面的命中,只有在该数据页与列表头相距有一定距离时,才会将该数据页移动到列表的头部位置。

  • not (young-making rate)(非年轻化比率)

    这个指标是 页面访问没有导致页面年轻化的平均命中率,可能是因为 没有满足 innodb_old_blocks_time定义的延迟时间,也有可能是在新列表上的页面命中并没有将该数据页移动到列表头的缘故。此比率考虑了所有缓存池的页面访问,而不仅仅是旧子列表中页面的访问