缓冲池简单来说就是一块内存区域,通过内存的速度来弥补磁盘速度较慢对数据库性能的影响。在数据库中读取页的操作,首先将从磁盘读取的页存放在缓冲池中,这个过程称为将页 “FIX” 在缓冲池中。下一次再读取相同的页时,首先判断该页是否在缓冲中。若在则命中,否则读取磁盘上的页。
对于数据库中的页的修改,首先修改在缓冲池中的页,然后再以一定的频率刷新到磁盘上,页冲缓冲池刷新回磁盘的操作并不是在每次页发送更新时触发,而是通过一种称为 Checkpoint 的机制刷新回磁盘。这是为了提高数据库的整体性能。
缓冲区是一个很大的内存区域,其中存放各种类型的页,那么InnoDB 存储引擎怎么进行管理呢?
通常数据库中的缓冲池通过 LRU(最近最少使用)算法来进行管理的。最频繁使用的页在 LRU 列表的前端,而最少使用的页在 LRU 列表的尾端。当缓冲池不能存放新读取到的页时,将首先释放 LRU 列表中尾端的页。
缓冲池中的页大小默认为 16KB。但是 InnoDB 存储引擎对传统的 LRU 算法做了一些优化。 LRU 列表中加入了 midpoint 位置。新读取到的页,虽然是最新访问的页,但并不是直接放入到 LRU 列表的首部,而是插入到 LRU 列表的 midpoint 位置。在默认配置下,该位置在 LRU 列表长度的 5/8 处。在 InnoDB 存储引擎中,把 midpoint之后的列表称为 old列表,之前的列表称为 new 列表。可以简单地理解为 new 列表中的页都是最活跃的热点数据。
为什么要这样优化?如果直接讲读取的页放入到 LRU 列表的首部会有什么问题?
如果将直接读取的页放入到 LRU 的首部,那么某些 SQL 操作可能会使缓冲池中的页被刷新出来,从而影响缓冲池的效率。常见的这类操作作为索引或数据的扫描操作。这类操作需要访问表中的许多页,甚至全部的页,而这些页通常来说又仅在这次查询操作中需要,并不是活跃的热点数据。如果页被放入 LRU 列表的首部,那么非常可能将所需要的热点数据页从 LRU 列表中移除,而在下一次需要读取改页时,InnoDB 存储引擎需要再次访问磁盘。
执行命令 SHOW ENGINE INNODB STATUS;
Buffer pool size 共有 8192 个页
Free buffefreers表示free列表中页的数量
Database pages表示LRU列表中页的数量
Database pages与Free buffers之和不等于Buffer pool size,因为还可能分配给自适应哈希索引、lock信息、insert buffer等页。
因为是本地环境,没有做更新操作,所以即使设置了innodb_old_blocks_time,not young还是为0。
buffer pool hit rate,表示缓冲池命中率,一般不低于95%,如果偏低,要看看是不是有全表扫描造成LRU列表污染。
Free List
数据库刚启动的时候,LRU 列表为空,此时需要用到的时候直接将Free列表中的页删除,在LRU列表中增加相应的页,维持页数守恒。
Flush List
LRU列中数据被修改后,产生脏页。数据库通过checkpoint机制将脏页刷新会磁盘,flush list中的页即为脏页列表。脏页即存在于LRU中,也存在于Flush中。LRU list用于管理缓冲池中页的可用性,Flush list用于将页刷新回磁盘。
缓冲池的设计目的是为了协调 CPU 速度与磁盘速度的鸿沟,因此页的操作首先都是在缓冲池中完成。倘若每次一个页发生变化,就将新野的版本刷新到磁盘,那么这个开销是非常大的。若热点数据集中的某几个页中,那么数据库的性能将变得非常差。同时,如果在从缓存池将页的新版本刷新到磁盘时发生宕机,那么数据就不能恢复了。为了避免发生数据丢失的问题,当前事务数据库系统普遍都采用 Write Ahead Log 策略,即当事务提交时,先写重做日志,再修改页。当由于发生宕机而导致数据丢失,通过重做日志来完成数据的恢复。
Checkpoint技术目的是结局以下几个问题:
当数据库发生宕机时,数据库不需要重做所以的日志,因为 Checkpoint 之前的页都已经刷新回磁盘。故数据库只需对 Checkpoint 后的重做日志进行恢复。这样就大大缩短了恢复的时间。
当前事务数据库系统对重做日志的设计都是循环使用的,当数据库发生宕机时,数据库恢复操作不需要这部分的重做日志,因此这部分就可以被覆盖重用。
在 InnoDB 存储引擎内部,有两种 Checkpoint, 分别为:
Insert Buffer: 对于非聚集索引的插入或更新操作,不是每一次直接插入到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,若在,则直接插入,若不在,则先放入到一个 Insert Buffer 对象中,然后再以一定的频率和情况进行 Insert Buffer 和辅助索引页子节点的 merge(合并)操作,这时通常能将多个插入合并到一个操作中去,这就大大提高了对于非聚集索引插入的性能。
Insert Buffer 的使用需要同时满足以下两个条件:
若是索引唯一索引,那么在插入时需要判断插入的记录是否是唯一,这需要读取辅助索引页,而insert buffer 的设计就是避免读取insert buffer,这会导致失去insert buffer 的设计意义。
doublewrite : Insert Buffer 带给 InnoDB 存储引擎的是性能上的提升,而doublewrite 带给 InnoDB 存储引擎的是数据页的可靠性。
自适应哈希索引:InnoDB 存储引擎会监控对表上各索引页的查询。如果观察到建立哈希索引可以带来速度提升,则建立哈希索引,称之为自适应哈希索引。
异步IO:为了提高磁盘操作性能,与 AIO 对应的是 Sync IO,即每进行一次 IO操作,需要等待此次操作结束才能继续接下来的操作。如果用户发出的是一条索引扫描的查询,那么这条 SQL 查询语句可能需要扫描多个索引页,也就是需要进行多次的 IO 操作。在每扫描一个页等待其完成后再进行下一次的扫描,这是没有必要的。用户可以在发出一个 IO 请求后立即再发出另一个 IO 请求,当全部 IO 请求发送完毕后,等待所有 IO 操作的完成,这就是 AIO。
刷新邻接页:当刷新一个脏页时,InnoDB 存储引擎会检测该页所在的区(extent)的所有页,如果是脏页,那么一起进行刷新。
MySQL 技术内幕 InnoDB 存储引擎