作者:刘仁鹏
参考资料:《MySQL技术内幕:InnoDB存储引擎》
1.序言
- InnoDB允许有多个缓冲池实例,每个页根据哈希值平均分配到不同的缓冲池实例中,以减少数据库内部的资源竞争,可以通过参数innodb_buffer_pool_instances来进行配置
- 可通过SHOW ENGIN INNODB STATUS或INNODB_BUFFER_POOL_STATUS来观察缓冲的状态
SHOW ENGINE INNODB STATUS\G
SELECT POOL_ID, POOL_SIZE, FREE_BUFFERS, DATABASE_PAGES FROM INNODB_BUFFER_POOL_STATUS\G
<center>图:InnoDB内存数据对象</center>
nnnn.png-94.8kB
2.具体实现
1.LRU List、Free List、Flush List
LRU列表
- 数据库中的缓冲池是通过LRU(Latest Recent Used)算法来进行管理的。即:最频繁使用的页在LRU列表的前端,最少使用的页在LRU列表的尾端
- 缓冲池中页的默认大小为16KB
- InnoDB对传统LRU算法做了优化,加入了midpoint位置(midpoint insertion strategy)。即新读取到的页,并不直接放到LRU列表的首部,而是放到midpoint位置
- 默认配置下,该位置在LRU列表长度的5/8处,midpoint位置可由参数innodb_old_blocks_pct控制
- 这么做的目的是为了避免批量数据查询操作将LRU列表中的真实热点数据刷出
- 为进一步解决批量操作刷出热点数据的问题,InnoDB还提供了innodb_old_blocks_time来表示页读取到mid位置后,需要等待多久才会被加入到LRU列表的热端。因此可在批量操作前将该值设大,操作完毕后再将该值设小。
Free列表
- 当需要从缓冲池中分页时,会首先从Free列表中查找是否有可用的空闲页。如有,则将该页从Free列表中删除,放入LRU列表中。否则,淘汰LRU列表末尾的页,将该内存空间分配给新的页。
- 观察方式:
SHOW ENGINE INNODB STATUS\G
SELECT POOL_ID, HIT_RATE, PAGES_MADE_YOUNG, PAGES_NOT_MADE_YOUNG FROM information_schema.INNODB_BUFFER_POOL_STATUS\G
SELECT TABLE_NAME, SPACE, PAGE_NUMBER, PAGE_TYPE FROM INNODB_BUFFER_PAGE_LRU WHERE SPACE = 1;
- InnoDB支持压缩页的功能,将原本16KB的页压缩到1KB、2KB、4KB、8KB
- 对于压缩页(非16KB的页),是通过unzip_LRU列表进行管理的。可通过SHOW ENGINE INNODB STATUS命令的unzip_LRU len查看。注意,unzip_LRU len是包含于LRU len的
- unzip_LRU列表对不同大小的压缩页进行分别管理,通过伙伴算法分配内存
Flush列表
- 在LRU列表中的页被修改后,称该页为脏页,即缓冲池中的页和磁盘上的页的数据产生了不一致,这时数据库会通过CHECKPOINT机制将脏页刷新回磁盘,而Flush列表中的页即为脏页列表
- 注意:脏页既存在于LRU列表中,也存在在Flush列表中。LRU列表用来保证可用性,Flush列表用来保证一致性,二者互不影响
- 通过元数据表INNODB_BUFFER_PAGE_LRU查看脏页时要加OLDEST_MODIFICATION > 0的查询条件
2.重做日志缓冲
- InnoDB存储引擎首先将重做日志信息放到这个缓冲区,然后按一定频率刷新到重做日志文件
- 重做日志缓冲大小默认为8M,可通过参数innodb_log_buffer_size控制。确保每秒产生的事务量在这个缓冲大小之内即可
- 重做日志在以下三种情况下会将缓存刷到磁盘:
- Master每秒刷一次
- 每个事务提交时刷一次
- 重做日志缓冲剩余空间小于1/2时刷一次
3.额外的内存池
- 在对一些数据结构本身的内存进行分配时,需要从额外的内存池中进行申请,当该区域内存不够时,会从缓冲池中申请
- 例如:
- 每个缓冲池中的帧缓冲
- 对应的缓冲控制对象(这些对象记录了一些诸如LRU、锁、等待等信息)
- 在申请了很大的InnoDB缓冲池时,也应考虑相应的增加额外内存池的大小
网友评论