InnoDB内存结构详解
buffer pool
buffer pool是InnoDB的缓存,用来存放各种数据,包括索引页(index page)、数据页(data page)、undo页、插入缓冲、自适应哈希索引(AHI)、innodb存储的锁信息、数据字典等。把磁盘上的数据加载到缓冲池中(通过预读机制加载当前页、相邻页),可避免每次访问都进行磁盘IO,起到加速访问的作用。应用程序在对数据库执行增删改操作的时候,实际上主要都是针对内存里的buffer pool中的数据进行的。
buffer pool包含三种数据类型:
- free page:从未用过的页。
- clean page:干净的页,即数据页的数据和磁盘一致。
- dirty page:脏页,即数据页的数据和磁盘不一致。
针对这3种页,InnoDB使用3种链表维护:
- free list:空闲页链表,管理free page。
- flush list:脏页链表,管理dirty page并在某个时刻对该链表的脏页进行刷盘,按脏页的修改时间排序,更新操作较早的脏页先被刷盘。
- lru list:正在使用的内存页链表,里面包含clean page和dirty page,也就是说lru list中的页包含flush list中的所有脏页。lru list遵循lru算法管理缓存页。
InnoDB需要保证buffer pool的数据都是热点数据,将无效的预读数据快速删除、不将读入后立即使用的数据替换热点数据,就引入了变种lru算法(新生代+老生代、老生代停留时间窗口)来解决“预读失效”与“缓冲池污染”的问题。通过下列指令可以查询到数据库设置冷热分界线和成为热块的所需时间:
show variables like 'InnoDB_old_blocks_pct'; -- 单位%,默认37,代表冷数据占比
show variables like 'InnoDB_old_blocks_time'; -- 单位ms,默认1000
Mysql5.7.5之后,buffer pool有分块(chunk)的特性,即一个buffer pool实例是由多个块组成,每个块的块内空间是连续的,块与块之间则是离散的。分块是为了方便用户在mysql运行期间能够调整buffer pool的大小。
注意,为了提高读写性能,避免过少的数据刷盘或随机IO,buffer pool一般不会对单个Page实时刷盘,所以这就出现了缓存和磁盘的一致性问题,InnoDB通过引入redolog来保存数量操作记录从而解决此问题,见下文。
change buffer
change buffer(写缓存)是一种特殊的数据结构,可以避免数据更改时因为隐式查询数据带来的磁盘IO。change buffer默认占buffer pool的 25%,最大允许占50%。可以根据写业务的量调整,写操作越频繁,change buffer带来的性能提升越明显。
change buffer工作原理如下:
综上:change buffer适合写多读少的场景,并且满足非唯一索引。
Adaptive Hash Index
Adaptive Hash Index(AHI,自适应哈希索引),是指InnoDB存储引擎通过监控表上索引页的查找模式,自动根据查找模式对“热点数据”来创建哈希索引。因为对B+树索引的访问需要依次访问根节点>中间节点>叶子节点,而对哈希索引的访问仅需要一次HASH计算即可定位到目标位置。一些资料统计,启用AHI后,读取和写入速度可以提高2倍,辅助索引的连接操作性能可以提高5倍。
通过下列指令可以查询到数据库的相关设置:
show variables like '%hash_index';(DMS设置的是OFF)
AHI使用条件:
AHI使用buffer pool中的数据页进行构造,仅保存在内存中,且仅对热点数据进行处理,因此构造AHI速度极快。
log buffer
log buffer就是redolog buffer的简称,是存储要写入磁盘上的redolog的内存区域。
log buffer由变量innodb_log_buffer_size定义大小,默认为16MB(DMS中设置了8GB)。log buffer的内容会根据设置刷盘,足够大的log buffer可以使得大事务完全依赖缓存运行,而不需要在事务提交前将redolog数据写入磁盘。因此,如果有更新、插入或删除许多行的事务,增加log buffer的大小可以节省磁盘I/O。
log buffer是顺序写的,刷盘也是顺序的,所以当某个脏页对应的redolog从log buffer刷盘时,会保证将在其之前产生的redolog也刷盘,详情见下文redolog的介绍。