innodb架构图
5.5以上默认innodb,最大特性是支持事务。
innodb架构主要由两部分组成:in-memory & on-disk,即内存结构和磁盘结构。这里单独介绍一下内存结构。
架构图:
innodb内存结构
1.innodb内存结构由哪几部分组成?各自作用是什么?
由buffer pool(含buffer pool主要部分、change buffer、adaptive hash index)、log buffer。
-
buffer pool:缓冲池,缓存表和索引数据,减少磁盘io。innodb_buffer_pool_size也是最核心的参数。
-
change buffer:占用buffer pool一块区域,属于写缓冲区,针对二级索引(辅助索引)页变更的优化措施,5.5之前叫insert buffer(插入缓冲)。在后续通过其他读操作将这些页加载到缓冲池时被合并。innodb_change_buffer_max_size控制了占buffer pool的比例,默认是25,即占25%,最大是50。
-
adaptive hash index:无法手动创建自适应哈希索引,只能由innodb自动创建。如果innodb发现了通过B+树检索数据效率低且建立自适应哈希索引能优化的话,则会在buffer pool开辟一块区域建立自适应哈希索引。
-
log buffer:日志缓冲区,缓存将要落盘log文件(包括redo和undo)的数据,log buffer的内容会定期刷新到磁盘的log文件中。定期刷新的机制使得磁盘io次数变少,提高性能。一般log buffer满了会刷一次,或有大事务也会较频繁写入。可适当的调整innodb_log_buffer_size大小以优化,默认128M。
参考架构图左半部分。
2.什么是buffer pool?
缓冲池,缓存表和索引的数据,目的是减少磁盘io。
由两部分组成:
-
缓存页(page)
innodb以页为单位,对数据进行划分。以页作为磁盘和内存交互的基本单位,默认大小16KB。
在MySQL启动的时候会按配置申请一块连续的内存区域,会按照页大小将buffer pool内存区划分为一个一个的数据页。
包括:数据页、索引页、undo页、插入缓存页、自适应哈希索引、锁信息。 -
控制块
存储缓存页的描述信息:对应缓存页的表空间信息、对应缓存页在buffer pool的地址信息、页编号。
一个缓存页对应一个控制块,是一一对应的关系。
buffer pool默认大小128M,
page默认16K,
控制块一般为页的5%,约800字节。
当我们查询一条数据的时候,会将这条数据所在页从磁盘加载到buffer pool
innodb是如何判断一个页已经在buffer pool中缓存的?
在MySQL中有一个hash表的数据结构,是一个k-v形式,它使用表空间号+当前数据页编号作为一个key,value是缓存页对应的控制块。当我们需要访问某个页的数据时,先从hash表中根据表空间号+数据页编号判断是否有对应的缓存页。如果有,则直接使用;如果没有,则从free链表中选出一个空闲的缓存页,然后把磁盘中对应的页加载到该空闲缓存页的位置,即一次磁盘io。
3.buffer pool中如何管理page页?
buffer pool底层采用链表的方式管理page页。
参考:
https://blog.csdn.net/wangdamingll/article/details/107348038
page根据状态可分为3种类型:
- free page:空闲页,未被使用的
- clean page:被使用的,但数据是没有修改过的,即缓存页中数据和磁盘页上的数据是一致的
- dirty page:脏页,被使用的,并且数据被修改,且与磁盘数据还不一致
对于上述三种页,innodb使用3中链表结构进行管理:
- free list:表示空闲缓冲区,管理free page。
将所有空闲的缓存页对应的控制块,作为一个一个的节点存到链表中,这个链表就是free list。链表有一个基节点,基节点不记录缓存页信息,只记录链表头尾的地址信息和当前链表节点的个数。此后的节点就是控制块,存储page描述信息,如free page位置等,指向对应的page页。
磁盘加载页的流程:
1.从free list链表中取一个空闲的控制块,即得到对应的空闲缓存页;
2.把此缓存页对应的控制块信息补充完整(比如页所在的表空间,页号之类的信息);
3.将此控制块从free list链表中移除掉,表示该缓存页已被使用,不再空闲;
参考图例:
-
flush list:表示需要刷新到磁盘的缓冲区,管理dirty page。
其内部page是按照修改时间来排序的,用来存储脏页。因为innodb引擎不是每次修改都将缓存页刷到磁盘(log刷盘另说),而是在某特定时间点执行刷新操作,所以需要flush list链表。凡是被修改过的缓存页的控制块,都会作为节点加入flush list链表。
-
LRU list:表示正在使用的缓冲区,管理clean page和dirty page。
该缓冲区以midpoint为基点,之前的链表称为new列表区,也是热数据区,用来存放经常被访问的数据,占63%;之后的链表成为old列表区,存放使用较少的数据,占37%。
注意:脏页在flush list和LRU list都存在,两者互不影响。LRU链表负责管理page的可用性和释放,flush链表主要负责管理脏页的刷盘操作。
参考文章:
https://blog.csdn.net/fvdfsdafdsafs/article/details/137889775
4.MySQL为什么没有采用传统LRU算法?
普通的LRU算法
LRU(最近最少使用),新数据从链表头部加入,本来在内存中的page如果使用了则移动到头部;释放空间时从就从末尾淘汰。
普通的LRU链表的缺点
- buffer pool的污染:
当某个SQL扫描了大量数据时,就有可能把buffer pool中所有页(或大量热数据页)都替换出去,导致大量的热数据被淘汰。当这些热数据再次被访问的时候就会产生大量的磁盘io,影响mysql性能。 - 预读失效:
MySQL存在预读机制,因为大部分情况下局部性原理是成立的。普通的LRU算法会将预读页放在LRU链表的头部,淘汰尾部页。一旦这些预读数据没有使用到,相当于预读工作就白做了,还淘汰了尾部缓存页,就降低了缓存命中率,这就是预读失效的后果。
5.MySQL对LRU算法进行了哪些优化?
改进型LRU算法
链表分为new和old两个区域。加入元素时并不是从表头插入,而是从midpoint位置插入。
未完待续…