MySQL有一个最大的缓冲区 Buffer Pool(默认大小128M),用来缓存 页 ,将查询到的页放到 Buffer Pool
这时有一个 free链表,头节点存储了当前链表的元素个数以及下一个链表在哪里,后续的链表存储 Buffer Pool里哪些区域是空闲的,当空闲的Buffer Pool的区域被使用的时候,相应的free链表的节点也被删除
同时又有一个 lru链表,头节点也是存储了当前链表的元素个数,来将在free链表中删除的节点加入到 lru链表中的头节点后面,使用最近最少使用算法,最近被使用节点也会放到头节点后面,
当再一次更新中,Buffer Pool 中所有的数据全部换掉, 这一行为被称作换血
为了避免换血这种情况发生,MySQL对 lru 链表进行了升级,前 5/8 的部分用来存储热数据,也就是经常访问的数据,后 3/8 的部分用来存储冷数据,也就是不是经常被访问的数据,而且冷数据也可以升级成为热数据,当一个冷数据被连续访问,而且两次访问时间相差大于1s,t2 - t1 > 1s,避免一个SQL语句连续访问多次冷数据,这时候这页冷数据就升级成为了热数据,同时最后一条热数据被淘汰掉成为冷数据。
前 1/4 的热数据没有必要交换,也就是没必要看谁是最长被访问的数据,因为会造成前面的热数据进行多次没有意义的交换,本质是:这是淘汰的机制,而不是找出最热的机制,没有必要浪费时间
Buffer Pool里面肯定有一些页是被修改的,我们称之为脏页,MySQL内置了线程 定时地更新脏页
类似free链表,当Buffer Pool中有页被update 更新修改之后,会放在 flush链表头节点后面,之后内置定时更新地线程 会在链表里面找相应的地址来进行更新
对于更新脏页,MySQL有两个 Redo Log日志,专门来存储脏页相关地日志,当两个日志全都存储满了之后或者后台线程定时地 就开始更新脏页进行持久化,这就完成了从随机IO到顺序IO地转变
但是,有可能在进行事务的时候两个日志满了必须进行持久化,就会让事务滞后,这时我们可以进行对Read Log 的配置,让空间变大或者数量变多。但是同样有弊端,当MySQL因为一些原因挂掉的话,需要依靠 Read Log 日志进行数据的恢复,就回导致打开MySQL的速度变慢
双写缓冲区————脏页有16kb,但是操作系统每次写只能写入4kb,所以要分4次写入磁盘进行持久化,但是有可能写入了1、2、3次而中途挂掉,最终导致没有一次性写完
在磁盘里面开辟一个新的空间,让Buffer Pool中脏页的数据进行持久化写入到这个新开辟的空间中,如果写入成功,就根据这个新数据再写入磁盘中原有的老数据,如果这时候程序挂掉,可以再根据新数据对比老数据进行继续持久化写入,如果没挂掉就成功了。如果在第一次写入的到新开辟的空间的时候中途挂掉,老数据区域可以根据Redo Log 进行持久化写入。
但是现在有一些的硬盘是支持源自写入的,意味着要么将16kb的数据全部写完,要么16kb数据全部都不写入。
双写缓冲区示意图