InnoDB存储引擎执行原理深度剖析
先从一个简单的update语句入手,分析它在被执行时是如何与InnoDB存储引擎的各种机制结合起来的,并依次完成整个update语句的执行
1.数据页和缓存页是什么?如何知道哪些缓存页是空闲的,哪些缓存页是可被清除的?
2.mysql预读机制了解过吗,什么情况下会触发它?mysql是为了应对什么样的场景才设计预读机制?
3.类比redis在内存中也存在冷热数据共存的场景,如何考虑利用lru链表解决预读机制的思想、来对redis缓存的设计进行优化?
4.内存极度不够用情况下,可能每当要加载一个数据页时就要先把一个缓存页刷到磁盘中,出现双倍IO的性能问题,对于这种现象如何考虑优化Mysql
内核参数来尽量避免该情况的性能损耗?
磁盘数据如何加载到mysql中?
一般我们要更新一条数据,数据一开始肯定是存放在磁盘中的,用到时才会被加载到mysql,存放的数据在逻辑概念上我们称为表,物理层面上在磁盘中是按数据页形式存放的,那么加载到mysql中的就称为缓存页。
每个缓存页都有对应的一份描述信息,存放了缓存页的一些元数据相关的一些信息,通过描述信息可以快速定位到缓存页,最开始描述信息指向的缓存页当然都是空闲没有数据的,从磁盘加载数据页信息流程如下图所示:
那么从磁盘中加载一个数据页到mysql中真的就这么简单吗?会不会同一份数据页加载到mysql中出现重复加载的情况?如何快速知道当前数据页是否已经加载到mysql中了?
缓存。对于已经加载到mysql中的数据页,我们大可以设计一个缓存将加载过的数据页信息缓存一下,一方面可以防止同一份数据页重复加载到mysql,另一方面当我们需要使用到数据页的信息时,可以通过缓存信息快速定位mysql中对应的缓存页,没错,InnoDB存储引擎中就是按照这样的思路设计了一个数据页缓存:
当一条update语句执行时,通过sql语句中的数据库名和表名解析可以知道我们需要加载的数据页处于哪个表空间,根据sql语句本身也可以通过一致性算法得数据页号(具体的sql解析和算法这里暂可简单了解下),根据数据页号和表空间号,可以从数据页缓存中(本质也就是一个哈希表)得到对应缓存页地址,通过缓存页地址我们直接就可以到InnoDB的缓冲池中定位到缓存页;当然,如果数据页还没有加载过,缓存页地址肯定是不存在,此时就需要从磁盘中加载数据页到mysql中了,如下图所示:
这个时候又有一个问题,既然现在我们已经知道磁盘中的数据页是加载到buffer pool缓冲池中的,
那么我们怎么样才能知道哪些缓存页是空闲的?
哪些缓存页是没有被加载过数据页信息的呢?
毕竟加载过的数据的缓存页和没加载过数据的缓存页混在一起,倘若此时想找一个空闲的缓存页肯定也是一件很麻烦的事。InnoDB存储引擎在设计时当然也考虑到了这点,
这里它引入了free链表这个数据结构,将那些还没有被使用的缓存页的描述信息用双向循环链表给组合在一起,需要用到时就卸一个节点出来存放数据页信息,如下图所示:
此时数据页被加载到缓存页了,缓存页中已经有数据了,相关的变动信息肯定也要回写到描述信息中,并且现在因为缓存页已经有数据,就不能再待在free链表中了,就需要将该缓存页对应的描述信息节点从free链表给摘掉,转移到了lru链表中,如下图所示:
lru链表实现的目的就是为让哪些被访问的缓存页能够尽量排到靠前位置,那么此时如果此时内存不够需要淘汰掉一些缓存页时,此时就可以到lru链表尾部,将哪些最近最少被访问的尾部节点给刷盘释放缓存页腾出内存来。
到这里为此,为了更新一个sql,我们已经把该sql所需要的数据、通过InnoDB存储引擎的各种底层机制,给加载到了Buffer Pool缓冲池中了,接下来就是在InnoDB中执行更新操作。