Buffer Pool
InnoDB存储引擎是基于磁盘存储的,并将其中的数据按照页的方式进行管理(缓冲页)。基于磁盘的数据库系统通常使用缓冲池技术来提高数据库的整体性能。
缓冲池中存储的数据页类型有: 索引页、数据页、undo页、插入缓冲(insert buffer)、自适应哈希索引(adaptive hash index)、innodb存储的锁信息(lock info)、数据字典等;
数据页、索引页占大部分缓冲池空间;
读数据页时,首先将数据读到缓冲池中,下次读写相同的页时,先去缓冲池中找;
对于数据库中页的修改操作,则首先修改在缓冲池中的页,然后再以一定的频率刷新到磁盘上,并不是每次页发生改变就刷新回磁盘,而是通过一种叫做checkpoint的机制把页刷新会磁盘。
InnoDB buffer默认为一个缓冲池实例,可以自行配置。每个页根据哈希值平均分配到不同的缓冲池实例中。这样做的好处是减少数据库内部的资源竞争,增加数据库并发处理能力。
缓冲池的大小直接影响着数据库的整体性能。
对于InnoDB存储引擎而言,其缓冲池的配置通过参数innodb_buffer_poolsize来
设置。
innodb_buffer_pool_instances: 设置有多少个缓冲池。通常建议把缓冲池个数设置为CPU的个数。
当需要访问某个页的数据时,就会把完整的页的数据全部加载到内存中。
也就是说即使我们只需要访问一个页的一条记录,那也需要先把整个页的数据加载到内存中。
buffer pool对内存的管理
Free List
free链表,该链表中记录了没有被使用的页,每次申请数据块都是要从free链表中取。
LRU List
LRU列表用来管理已经读取的页,当数据库刚启动时 LRU List是空的,此时页都存放在Free List中;
先从Free List中查找是否有可用的空闲页,有则从Free List中删除 并 加入到 LRU List中,若没有空闲页则根据LRU算法淘汰LRU List尾部的页;
自适应哈希索引(adaptive hash index)、innodb存储的锁信息(lock info)、数据字典等不在LRU List中;
了解
innodb支持页的压缩,即将16k的页压缩为1kb、2kb、4kb、8kb,对于非16kb的页用unzip_list列表进行管理到。unzip_lsit属于LRU list。
LRU(least recently used)算法
一般的LRU算法将使用最频繁的页总是在链表的前面,而最后的页就是要被释放掉的页;但是Innodb的LRU有所不同;
InnoDB的主要改进点在于每次将磁盘上读出的数据不是直接放到链表的头部,而是放在链表的midpoint处,只有在下次访问该页时,才会将该页移动到链表头部。
midpoint默认在LRU列表的5/8处,可由innodb_old_blocks_pct参数配置;
innodb_old_blocks_time ,控制数据页被读取到minpoint处 多久以后才能被加入到LRU列表的首部;
Flush List
fush列表中的页是脏页列表,即fush列表用来管理将页刷回磁盘;
注意,脏页 也存在于LRU list中;
Innodb关键特性
Insert Buffer插入缓冲
Insert Buffer
对于具有AUTO_INCREMENT属性的主键,对行记录的存放是顺序的,效率高,非自增的主键(例如uuid) 或者 辅助索引 则插入操作是随机的。
对于非聚集索引的 插入 或 更新 操作,不是每次直接插入索引页中,而是先判断要操作的非聚集索引页是否在缓冲池中,若在,则直接操作;若不在,则先放入Insert Buffer中,然后按一定频率将insert buffer 与 辅助索引叶子节点 进行合并merge操作;
insert buffer的使用条件;
1. 索引是辅助索引;
2. 索引不唯一;
只有满足上面2个条件,innodb才会使用insert buffer
insert buffer的缺点;
1. 若在合并之前宕机,恢复需要时间;
2. 在写密集的情况下,insert buffer会占用过多的buffer pool;
默认最大可以占用1/2的缓冲池内存,但是可以通过ibuf_pool_size_pre_max_size进行修改;
change buffer
change buffer 是对 Insert Buffer 的升级;
使用change buffer ,innodb可以对insert、delete、update都进行缓冲,分别是insert buffer、delete buffer、purge buffer;
通过innodb_change_buffering参数可以控制开启各种buffer;
通过innodb_change_buffer_max_size可以控制change buffer的最大内存;
insert buffer的内部实现
insert buffer的数据结构是一颗B+树,早期的insert buffer 每张表都有一棵B+树,现在全局只有一颗B+树,负责对所有表的辅助索引进行insert buffer;
space表示待插入记录所在的表空间ID,可以通过space ID得知是哪张表;
marker为了兼容老版本的insert buffer;
offset表示页所在的偏移量;
叶子节点中,space、marker、offset含义与之前相同;第五列开始是插入记录的字段;
double write 两次写
partial page write(部分写失效)问题
当数据库正在从内存向磁盘写一个数据页时,这个页只写了部分数据,比如16k的页,只写了前4k,之后发生了宕机,这种情况称为(partial page write)部分写失效。这时是无法通过重做日志恢复的,因为重做日志记录的是对页的物理修改,如果页本身已经损坏,重做日志也无能为力。
double write 由2部分组成
1)内存中的两次写缓冲(doublewrite buffer),大小为2MB
2)磁盘上 共享表空间 中连续的128页(2个区),大小也为2MB
double write原理
- 缓冲池的脏数据刷新时,先 通过menmcpy函数将脏页复制到内存中的doublewrite buffer,
- 然后由doublewrite buffer分2次,每次1M顺序写入磁盘上的共享表空间,
- 然后调用fsync函数将doublewrite buffer的数据同步磁盘数据文件;
其中,将doublewrite buffer中的数据写入共享表空间属于顺序写,开销小,将doublewrite buffer中的数据写入磁盘数据文件开销大
自适应哈希索引(Adaptive Hash Index,AHI)
innodb会监控表上各索引页的查询,如果观察到建立hash索引可以提升性能则建立哈希索引,称为AHI;
AHI由缓冲池的B+树构建而来,构建速度快;
缺陷
1、hash自适应索引会占用innodb buffer pool;
2、自适应hash索引只适合搜索等值的查询,如select * from table where index_col='xxx',而对于其他查找类型,如范围查找,是不能使用的;
刷新临近页
当刷新一个脏页时,innodb还会检测该页所在区(extent)的所有页,如果有脏页就一起刷新;