Innodb存储引擎

2019-12-03T12:20:14Z

innodb存储引擎是现在mysql的默认存储引擎,其特点是行锁设计、支持MVCC、支持外键、提供一致性非锁定读、同时被用来最有效的利用以及使用内存和CPU。

缓冲池

innodb维护了一个内存缓冲池名叫innodb_pool_buffer。当我们读一条数据的时候,先去缓冲池里面找存放改数据的页是不是在这个缓冲池中,如果在直接读取,如果不在那么先从硬盘上读取该页并放到缓冲池中供后续读取使用。写操作也是一样的,先写入缓冲池中的数据页,此时该页也叫脏页,然后再以一定的策略刷新到磁盘。缓冲池中存在的数据大致如下

缓冲池的大小非常重要。说的极端一点,如果我们可以把数据库的页全部缓存在缓冲池中,那么数据库中的数据就相当于缓存了。可以通过以下命令查看缓冲池的大小

show variables like 'innodb_buffer_pool_size'

现在innodb也支持同时启动多个缓冲池,数据页会根据哈希值被分配到不同的实例中去。可以通过以下命令查看缓冲池实例的个数

show variables like 'innodb_buffer_pool_instance'

缓冲池是一片连续的内存空间,innodb为每一个缓存页都创建了一些所谓的控制信息,这些信息包括该页所属的表空间编号、页号、页在缓冲池中的地址、一些锁信息以及LSN信息等。

每个缓存页的大小是一样的为16k,这个值是可以更改的。每个缓存页对应的控制信息占用的内存大小是相同的,我们就姑且把这些控制信息占用的一块内存称为一个控制块。控制块和缓存页是一对一的,都在缓冲池中。他们之间的关系大致是这样的

LRU List、Free List、Flush List

这三个list结构上很相似,都是双向链表,除了一些元信息之外还有就是指向控制块的指针。以free list为例(这个图是我能找到的最直观的图)

这三个列表虽然结构类似,但是功能却大不相同。

LRU List 是用来管理innodb的缓冲池的。缓冲池的目的就是尽量的把热点数据缓存在内存中,进而保证数据库的性能。LRU list的职责就是尽量把热点的数据页留在内存中,把非热点的数据页从内存中移除。LRU List的前端是最频繁使用的数据页,尾端是最少使用的。当一个数据页从硬盘加载到内存中的时候在LRU List的3/8处会更新一些原信息并将指针指向一个新的控制块,而这控制块又指向一个16kb大小的数据页,这个数据页里面存放的就是刚刚从硬盘里面读取出来的数据。这个数据页会在innodb_old_blocks_time之后移动到LRU List的头部。之所以这么做是因为防止类似于扫描全表的操作污染了LRU List。当LRU List的一个节点(比如该节点叫node_lru)将要被移除的时候,Free List中会增加一个节点(比如该节点叫node_free),node_free指向的控制块其实就是之前node_lru指向的控制块该控制块中所指向的数据页被LRU List认为是非热点数据了,这块地方要“腾出来存放别的热点数据了”

Free List 是用来管理空闲的数据页的。当数据库刚刚启动的时候肯定还没有任何的数据页加载到缓冲池中,这个时候Free List的节点的数目应该和控制块的的数目相等的。慢慢的当有数据页缓存到缓冲池中的时候Free List这个双向链表的节点数会慢慢变少。当一个从硬盘读取的数据页想要进入缓冲池中的时候是通过Free List来完成的。

Flush List 用来管理脏的数据页。写操作和读操作一样,也是先把数据页加载到内存中然后修改内存中的数据。一旦一个数据页变成了脏页那么Flush List这个链表里面就会有一个结点指向这个数据页所对应的控制块,意为这个数据页是脏的需要刷新回磁盘。同时这个数据页也一定会出现在LRU列表中,如果这个数据页将要从LRU列表中移除时那么肯定会把这个数据页同步到磁盘,这个数据页在Flush列表中的相应的节点也会被删除,这个是我们后面要将的checkpoint的技术。

需要强调的是当数据库实例启动的时候控制块和缓存页就已经初始化好了。就像酒店里的房间,你住不住人房间都在那里。没有人住房间就是空的,同一个房间在不同的时间段内可能是不同的人在居住。而这三个list中的节点是会增加会减少会改变的。

checkpoint

把缓冲池中的脏页刷新回磁盘是一件很重要的事情。checkpoint所做的就是这个重要的事情。innodb中的checkpoint如下

Sharp Checkpoint 数据库关闭的时候把所有的脏页刷新回磁盘 FUZZY Checkpoint

  • Master Thread Checkpoint 主线程以一个固定的时间间隔异步的刷新脏页回磁盘
  • FLUSH_LRU_LIST Checkpoint 当LRU列表要移除数据页时并且这个数据页还标记在在Flush列表中的时候要强行的刷新这个数据页回磁盘
  • Async/Sync Flush Checkpoint 重做日志文件将要被覆盖的时候
  • Dirty Page too much Checkpoint 脏页的数量太多

关键特性

插入缓冲

一般innodb的数据表都会有一个自增的id。因此每次插入一条数据的时候很容易找到待插入的位置,因为数据是存在主键索引的叶子节点上的。但是这有可能导致非主键索引插入的离散性。插入缓冲其实就是解决这个问题的。innodb对于非聚集索引的插入或者更新操作,不是每一次直接插入到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,若在则直接插入;若不在先放到一个插入缓冲中,然后再以一定的频率和情况进行插入缓冲和辅助索引页子节点的合并操作,这时通常能将多个插入合并到一个合并操作中,大大的提高了效率。

两次写

缓冲池中有2MB的内存叫做doublewrite buffer。同样硬盘上也有2M的“二次写”空间。第一次写是将内存的doublewrite buffer里的数据写到硬盘上的“二次写”空间,这个过程是顺序写,开销不大。第二次写是将doublewrite buffer里的数据写到表空间中,这个过程是离散的。如果在第二次写的过程中宕机,那么在“二次写”的硬盘空间中还备份了副本,可以用这个副本恢复数据页。

自适应哈希索引

Innodb存储引擎会监控对表上各索引页的查询。如果观察到建立哈希索引可以带来速度的提升,则建立哈希索引,成为自适应哈希索引。貌似我们对这个特性什么都做不了,是innodb自己完成的。

异步IO

脏页刷新回磁盘的操作是异步的,这样不会阻塞用户线程。

刷新邻接页

如果innodb决定刷新一个脏页回磁盘的时候,会检测该页所在的区的所有页需不需要一起刷新。

关于

我叫Skyler是一个喜欢足球的飞行员!

骗你的啦,⚽️和✈️只是我的理想啦,我目前是个喜欢🍺的程序员!我目前在帝都,但是很喜欢青岛和深圳,很感谢你能看完About Me !