上篇文章说mysql5.6之后新增了系统变量optimizer_tance可以看到他的优化过程。
缓存的重要性
前面我们说过innoDB表示吧数据存在表空间,不管是用于存储表的系统数据,还是存储用户数据的索引(聚簇索引 和 二级索引),表空间实际是在磁盘上的。但我们不可能每次查询数据都从磁盘上查询,那样以磁盘的龟速,怎么能配的上快速的cpu呢。所以我们在磁盘访问数据的时候,会把整个页的数据全部都放入缓存中,即使只是访问一条,也是会把整个页都放入缓存,而且查询完之后,也不会立即释放缓存,而是缓存起来,可以省去磁盘I/O的开销。
InnoDB的Buffer Pool
什么是buffer pool,mysql服务器在启动的时候,会向操作系统申请一片连续的内存,这个名字就是buffer pool(缓冲池)。他是多大呢,默认是128m,最小是5M,你也可以设置,当设置小于5m的时候,默认就是5M。
Innodb_buffer_pool_size=268435456
这个值是字节,也就是我指定为256M。
Buffer pool内部组成
Buffer pool里的页和磁盘上的页大小都是16kb。为了管理缓存区这些缓存页,mysql为每个缓存页创建了一些控制信息,这些控制信息包括表空间编号,页号,缓存页在buffer pool中地址、链表节点信息、一些锁信息以及LSN信息(锁和LSN信息后面再讲),当然还有一些控制信息。
每个缓存也对应的控制信息大小是相同的,我们吧每个页对应的控制信息占用的内存块称为【控制块】,控制块和缓存块是一一对应的,控制块放在buffer pool最前面,缓存块放在buffer pool后面,但是中间还有个碎片区,每个控制块对应一个缓存页,那剩余空间不够分的时候怎么办呢,这就是碎片区。(需要注意的是控制块大概占缓存页大小的百分之5,而我们设置的innoDB_buffer_pool_size不包含这个内存,所以设置的内存大小实际总的要多百分之5)
Free链表管理
我们在启动mysql的时候,就会完成buffer pool的初始化过程,先想系统申请内存空间,在把他们划分成不同的控制块和缓存页。此时这些缓存页都是空闲的,后面只有随着程序的运行,数据不断从磁盘刷新到缓存页才有数据。那么从磁盘上刷到缓存页的数据放在哪个位子呢。哪些缓存页是空闲可用的呢?
这时候控制块作用就来了,我们吧空闲缓存页的控制块统一放在free 链表,所以刚开始初始化的时候,所有缓存页都在free 链表。
Free 链表有一个基节点,里面有个start和end节点,还有总的count,控制块里还有双向链表连接起来。
有了这个free链表就方便了,如果从磁盘刷新数据到buffer pool只要取个空闲页缓存页,然后把页号,表空间id,等信息填写上去,最后从free 链表移除。
缓存页的hash处理
我们知道了数据存储在缓存页,但如何访问呢,总不能一点点遍历,这时候就用到了hash表,吧空间id+页号为key,value就是缓存页,通过key找到对应的value缓存页。
所以刷新数据的时候,先通过key判断是否已经有存在的缓存页,有就直接使用,没有就从free链表申请一个新的缓存页。
Flush链表管理
加入我们修改了缓存页,这时候缓存页和磁盘上的数据就不一致了,这时候就是脏数据,这时候我们就需要吧缓存页的数据同步到磁盘上,但我们不能每次都I/O访问磁盘,这样会效率很低,会在统一的时间段同步。
但如果同步又不能吧256G的缓存页全部同步到磁盘,这样也会影响效率,于是在修改后的链表会存入和free链表一样的结构,方便查找和同步,就叫flush链表。