MySQL InnoDB索引那点事儿( 三 )


MySQL InnoDB索引那点事儿

文章插图
 
注意Page和B+树节点之间并没有一一对应的关系,Page只是作为一个Record的保存容器,它存在的目的是便于对磁盘空间进行批量管理 。
3.2 非聚簇索引主键B+树在叶子节点存储指向真正数据行的指针,而非主键 。MyISAM使用的是非聚簇索引,非聚簇索引的两棵B+树看上去没什么不同,节点的结构完全一致只是存储的内容不同而已,
对于上面的表数据,非聚集索引的结构如下:
MySQL InnoDB索引那点事儿

文章插图
 
非聚集索引中,主键索引B+树的节点存储了主键,辅助键索引B+树存储了辅助键 。表数据存储在独立的地方,这两颗B+树的叶子节点都使用一个地址指向真正的表数据,对于表数据来说,这两个键没有任何差别 。由于索引树是独立的,通过辅助键检索无需访问主键的索引树 。
3.3 联合索引联合索引又叫复合索引 。对于复合索引,Mysql从左到右的使用索引中的字段,一个查询可以只使用索引中的一部分,但只能是最左侧部分,即我们常说的最左前缀匹配原则 。由于联合索引的匹配是从左往右进行,且不能跳过中间列,因而在设计联合索引时最好按照列的索引区分度来排序 。
4. InnoDB内存管理InnoDB存储引擎的内存管理主要采用预分配内存空间的方式,数据以页为单位加载进内存池,交由后台线程使用并进行维护:
MySQL InnoDB索引那点事儿

文章插图
 
其中内存池的主要工作包括:
  1. 维护所有进程/线程需要访问的多个内部数据结构
  2. 缓存磁盘上的数据,方便快速读取,同时在对磁盘文件修改之前进行缓存
  3. 缓存重做日志(redo log)
后台线程的主要工作包括:
  1. 刷新内存池中的数据,保证缓冲池中缓存的数据最新
  2. 将已修改数据文件刷新到磁盘文件
  3. 保证数据库异常时InnoDB能恢复到正常运行状态
这里主要关注内存池的管理,上图中缓冲池用于存放各种数据的缓存,缓冲池中的页可以分为:空闲页、数据页和脏页,如下:
MySQL InnoDB索引那点事儿

文章插图
 
Page Hash用于维护内存Page和文件Page的映射关系,同时维护3个列表用于管理空闲页、数据页以及脏页的装载和淘汰 。
1.LRU List
Innodb总是将磁盘中的数据按页为单位读取到缓冲池,然后按LRU算法来保存缓冲池中的数据,即最频繁使用的页在LRU列表的前端,而最少使用的页在LRU列表的尾端 。当缓冲池不能存放新读取到的页时,将首先释放LRU列表中尾端的页 。稍有不同的是InnoDB存储引擎对传统的LRU算法做了一些优化 。在InnoDB的存储引擎中,LRU列表中还加入了midpoint位置 。新读取到的页,虽然是最新访问的页,但并不是直接放入到LRU列表的首部,而是放入到LRU列表的midpoint位置,等待一定时间后再加入到LRU列表的new端成为热点数据 。在默认配置下,midpoint在LRU列表长度的5/8处 。
MySQL InnoDB索引那点事儿

文章插图
 
这样做的原因在于,若直接将读取到的page放到LRU的首部,那么某些SQL操作可能会使缓冲池中的page被刷出 。常见的这类操作为索引或数据的扫描操作 。这类操作访问表中的许多页,而这些页通常只是在这次查询中需要,并不是活跃数据 。如果直接放入到LRU首部,那么非常可能将真正的热点数据从LRU列表中移除,在下一次需要时,InnoDB需要重新访问磁盘读取,这样性能会低下 。
2.Free List
LRU列表用来管理已经读取的页,但当数据库刚启动时,LRU列表是空的,即没有任何的页 。这时页都存放在Free列表中 。当需要从缓冲池中分页时,首先从Free列表中查找是否有可用的空闲页,若有则将该页从Free列表中删除,放入到LRU列表中 。否则,根据LRU算法,淘汰LRU列表末尾的页,将该内存空间分配给新的页 。
3.Flush List
在LRU列表中的页被修改后,称该页为脏页,即缓冲池中的页和磁盘上的页的数据产生了不一致 。这时数据库会通过CHECKPOINT机制将脏页刷新回磁盘,而Flush列表中的页即为脏页列表 。需要注意的是,脏页既存在于LRU列表中,也存在于Flush列表中 。LRU列表用来管理缓冲池中页的可用性,Flush列表用来管理将页刷新回磁盘,二者互不影响 。



推荐阅读