博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Innodb 存储引擎(转)

Posted on 2016-11-03 15:55  moss_tan_jun  阅读(206)  评论(0编辑  收藏  举报

第一部分:线程

Innodb是一个多线程的,各个线程负责不同的任务。主要的线程有:Master Thread、IO Thread、Purge Thread、Page Cleaner Thread

一,Master Thread :刷写数据、回收undo、回收脏页、合并插入缓冲

      具有最高的优先级别,内部有多个循环(loop)组成:主循环(loop)、后台循环(background loop)、刷新循环(flush loop)、暂停循环(suspend loop),根据数据库的运行情况会在这些循环中切换。

      该线程主要负责刷写数据,包括缓冲池的数据异步刷写到磁盘,脏页的刷写、合并插入缓冲、UNDO页的回收等。在主循环(loop)中有2大部分的操作:每1秒和每10秒的操作。

build-in Innodb

每一秒的操作:

1)日志缓冲刷写到磁盘,即使这个事务还没有提交(总是)。这解释了再大的事务提交的时间也很短。

2)合并最多5个插入缓冲(可能)。先判断前1秒的IO次数是否小于5次,小于则进行合并插入缓冲的操作。

3)刷写100个脏页到磁盘(可能)。先判断缓冲池的比例是否超过了innodb_max_dirty_pages_pct的值,如果超过则刷写100个脏页到磁盘。

4)如果没有用户活动则切换后台循环线程。

每十秒的操作:

1)刷写100个脏页到磁盘(可能)。先判断前10秒的IO次数是否小于200,小于则刷写100个脏页到磁盘。

2)合并最多5个插入缓冲(总是)。

3)日志缓冲刷写到磁盘(总是)。

4)删除最多20个无用的UNDO页(总是)。

5)刷写100或10个脏页到磁盘(总是)。先判断缓冲池的脏页比例是否大于70%,大于则刷写100个脏页到磁盘,小于则刷写10个脏页到磁盘。

通过每一秒和每十秒的信息看出:Master Thread对IO的操作都有限制,影响到了存储引擎的性能,为了提高性能,在Plugin innodb上做了提升:

Plugin Innodb

      参数:innodb_io_capacity 表示磁盘IO的吞吐量,控制Innodb checkpoint时的IO能力,默认是200。对于刷新到磁盘的数量,会按照该参数的百分比进行刷写,而不是固定100、10等限制的大小刷写。

如之前的:合并5个插入缓冲 变成 合并5%*innodb_io_capacity的插入缓冲;

               刷写100个脏页   变成 合并 100%*innodb_io_capacity的脏页;

               刷写10个脏页    变成 合并 10%*innodb_io_capacity的脏页;

innodb_io_capacity设置为500,

则刷写100个脏页大小为:100*16/1024 = 1.5625M;按照innodb_io_capacity的大小设置则脏页大小为:500*16/1024 = 7.8125M。

接着看后台循环(background loop):没有用户活动,数据库空闲或则关闭时,就会切换到这个循环,该循环下做如下事情:

1)删除无用的UNDO页(总是)。

2)合并20个插入缓存(总是)。

3)跳回主循环。如果不空闲了则跳回主循环,否则跳到flush loop。

接着看刷新循环(flush loop):

1)不断刷写100个脏页到磁盘。先判断缓冲池里脏页比例是否大于innodb_max_dirty_pages_pct,大于则不断刷新只到小于为止。

flush loop 没有事可做,则切换到暂停循环(suspend loop),将Master Thread挂起,等待事件发生。若用户开启innodb 存储引擎,但是没用innodb表,则Master Thread总是挂起。

二,IO Thread :可以用参数innodb_read_io_threads和innodb_write_io_threads 来控制read和write。

      Innodb存储引擎大量使用了AIO(异步IO),提高了磁盘的操作性能。IO Thread 主要负责这些异步IO的回调。built-innodb有4个IO Thread:write、read、insert buffer、log。

三:Purge Thread:回收UNDO。5.5之后的参数,从Master Thread独立出来

      回收无用的UNDO页,在MySQL 5.5之前是在Master Thread中完成的。5.5之后,独立到了单独的线程中完成,减轻了Master Thread线程的工作。提升CPU的使用率和存储引擎的性能。

通过参数innodb_purge_threads 来开启,在5.5中只能也只有1可以设置。5.6可以设置大于1。另一个参数:innodb_purge_batch_size 来控制每次回收UNDO页的数量,在5.5之前默认是20(写死),5.5之后可以根据情况调整该参数。

四:Page Clean Thread:刷写脏页,5.6之后的参数,从Master Thread独立出来

      5.6里开始支持,脏页的刷写线程。从Master Thread里面独立出来。减轻了Master Thread 的工作,和对用户查询的阻塞。进一步提高Innodb 存储引擎的性能和并发。

 

第二部分:内存

      Innodb的内存数据对象包括:缓冲池即innodb_buffer_pool(数据页、索引页、插入缓冲、自适应哈希索引、锁信息、数据字典)、重做日志缓冲即redo log buffer(innodb_log_buffer_size)、额外内存池(innodb_additional_mem_pool_size)。5.5开始有多个缓冲池实例(innodb_buffer_pool_instances),根据哈希值进行分配,好处是减少了数据库内部的资源竞争,提高并发处理能力。只有配置BP大于1G的时候,多实例BP才能生效。默认为1。

一:缓冲池即innodb_buffer_pool     

      Innodb Buffer Pool是缓存数据和索引的缓冲区,负责管理free list(初始化空闲页等),flush list(缓冲池产生的脏页),LRU list(通过LRU算法管理页面交换的,LRU List分为2块:LRU_new、LRU_old。LRU_old为链表长度的3/8。页读取先进入old,访问时候从old进入到new。)即数据库的缓冲池(BP)可以看成一个LRU列表,根据最近最少使用算法进行管理,最频繁使用的在顶端,最少使用的在末端。当不能存放读取到的新页时,最先释放末端的页,将新页存放到顶端。Innodb在该LRU算法上进行了改进,加了一个midpoint的位置,新读取到的页不是放到顶端,而是放到midpoint的位置。因为某些SQL会使老的LRU算法出现问题:全索引、全表、mysqldump等类似的扫描多个页甚至是全部页的一次性SQL,会让缓冲池中真正的热点数据被刷出,影响缓冲池的效率。

      midpoint位置在LRU列表的5/8处,由参数innodb_old_blocks_pct控制。midpoint 之前的位置称为new(young)即活跃的热点数据,之后的位置称为old。该参数默认值为37(尾端开始的3/8处),要是热点数据很多,则可以设置该参数,如20(尾端开始的1/5处)。

      另一个参数Innodb_old_blocks_time :等到该时间后,再读取该页则会进入到new端,有效的避免了对于上述SQL对BP的污染。默认是0,单位是毫秒。如设置为1000则表示:读到该页到midpoint的位置,要再等1秒之后读取该页才能进入new列表。而0则表示读取到该页则会直接被放入到new列表。

      page made young:页从LRU的old列表加入到new列表。

      page not made young:由于Innodb_old_blocks_time的设置(非0),导致页没有从LRU的old列表加入到new列表。

上面可以通过 show engine innodb status 查看到。

二:重做日志缓冲即Innodb_log_buffer_size(事务日志缓存)

      Innodb存储引擎首先将重做(事务)日志信息放入到这个缓冲区,再按照一定频率(innodb_flush_log_at_trx_commit)刷写到重做(事务)日志文件中。日志缓冲大小设置为每秒产生的事务量即可。因为上面提过了Master Thread线程会每秒刷写日志缓冲到日志文件。还有其他的情况也会刷写:如每次事务提交和重做日志缓冲空间小于1/2的时候,当redo log buffer不够大的时候,会产生等待。可以通过show global status like 'Innodb_log_waits' 查看redo log buffer的等待状态。

三:额外的内存池(innodb_additional_mem_pool_size)

      对数据结构本身的内存分配,还有锁、等待以及LRU等信息。

      

第三部分:Checkpoint

      事务提交时,先写重做日志,再修改页。满足日志先行的原则。这样即使刷写数据到磁盘发生宕机也可以通过redo log 进行恢复,符合ACID中的D:持久性。事务日志(redo log)还可以提高事务的提交(顺序IO)和崩溃时候的恢复。

Checkpoint技术的目的:

①缩短数据库的恢复时间。数据库宕机,不需要做所有的redo log,Checkpoint之前的页都已经刷写到磁盘,只需要对其之后的redo log 进行恢复。

②缓冲池(BP)不够,刷写脏页。当缓冲池不够,则根据LRU算法会溢出最近最少使用的页,若该页是脏页则需要强制执行Checkpoint进行刷写。

③重做日志(redo log)不可用,则刷写脏页。因为redo log 是循环使用的,当新的事务需要写到日志文件时,而日志文件里的事务还没有来得及应用,不能被新事务覆盖使用时,需要强制执行Checkpoint进行刷写,将缓冲池中的页刷写到重做日志的位置。

Checkpoint做的事情就是把缓冲池中的脏页刷写到磁盘。触发刷写的操作的时机有:

数据库关闭,innodb_fast_shutdown(Innodb外部)

Master Thread每秒/每10秒刷写(Innodb内部)

③Flush_LRU_LIST,LRU列表需要有100个页空闲给用户查询使用,若没有则会阻塞查询操作,会把LRU列表的末端移除,若为脏页则需要Checkpoint。

④redo log不可用,需要进行强制的刷写操作。未刷写的页:lsn - checkpoint 。如redo log为2G。

* 未刷写的页<75%*2G,不需要刷写

* 75%*2G<未刷写的页<90%*2G,需要刷写,直到满足未刷写的页<75%*2G

在5.6之前,③和④过程会阻塞用户查询,并等待脏页刷写完成为止。5.6后有专门的Page Cleaner Thread,不会阻塞。

⑤缓冲池中的脏页太多,超过了innodb_max_dirty_pages_pct的值,会进行刷写。

 

第四部分:Innodb关键特性

      Innodb存储引擎的关键特性包括:插入缓存(Insert Buffer -> Change Buffer)、两次写(Double Write)、自适应哈希索引(Adaptive Hash Index)、异步IO(Async IO)、刷写邻居页(Flush Neighbor Page)

一,插入缓冲(Insert Buffer/Change Buffer):提升插入性能

      InnoDB为了避免更新数据时更新索引损失太多性能,使用了这种称为Insert Buffer的方法来缓冲索引更新。

      只对于非聚集索引(非唯一)的插入和更新有效,对于每一次的插入不是写到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,如果在则直接插入;若不在,则先放到Insert Buffer 中,再按照一定的频率进行合并操作。这样通常能将多个插入合并到一个操作中,提升插入性能
按照Master Thread的调度规则来合并非唯一索引和索引页中的叶子结点,这样经常能减少更新索引的代价。使用插入缓冲的条件:

* 非聚集索引

* 非唯一

插入缓冲最大使用空间为1/2的缓冲池大小,不能调整大小,在plugin innodb中,升级成了Change Buffer。不仅对insert,对update、delete都有效。其参数是:

innodb_change_buffering,设置的值有:inserts、deletes、purges、changes(inserts和deletes)、all(默认)、none。

可以通过参数控制其使用的大小:

innodb_change_buffer_max_size,默认是25,即缓冲池的1/4。最大可设置为50。在5.6中被引入。

上面提过在一定频率下进行合并,那所谓的频率是什么条件?

1)辅助索引页被读取到缓冲池中。正常的select先检查Insert Buffer是否有该非聚集索引页存在,若有则合并插入。

2)辅助索引页没有可用空间。空间小于1/32页的大小,则会强制合并操作。

3)Master Thread 每秒和每10秒的合并操作。

二,两次写(DoubleWrite)

      提高数据安全,当页写入失效时,先通过页的副本(共享表空间中)来还原,再通过redo log 来重做。由2部分组成,一部分是内存中double write buffer,2M大小;另一部分是磁盘上共享表空间的连续的128页,即2个区(extent),2M大小的doublewrite。

      缓冲池刷写脏页时,并不是直接写磁盘,而是先写到内存中的DoubleWrite中,之后再通过DoubleWrite顺序的写到共享表空间的DoubleWrite中,最后刷写磁盘。这样意味这刷写数据都要多写一份,增加了IO。但DoubleWrite是顺序的,所以开销不大。相比之下,牺牲一点点开销来提升安全是很有必要的。
参数:

Innodb_dblwr_pages_written:写的页数

Innodb_dblwr_writes:写的次数

Innodb_dblwr_pages_written:Innodb_dblwr_writes 要是小于64:1则写压力不高。大于则表示压力高

Innodb_dblwr_pages_written和Innodb_buffer_pool_flushed (从缓冲池刷写到磁盘的页的数量)应该是一致的,因为缓冲池的刷写都会先存放到doublewirte中,即可以通过他们看出数据库的写入量。

参数skip_innodb_doublewrite 禁止双写。双写缓存的更多信息可以看关于innodb中两次写的探索

三,自适应哈希索引:提高查询效率

四,异步IO:提高磁盘的操作性能

      5.5之前,是通过代码实现的(不能修改),5.5之后新增了参数:innodb_use_native_aio来控制是否启用,默认值得为ON。启用恢复数据提升75%。

五,刷新邻接页

      工作原理:刷写一个脏页时,会检测该页所在的区(extent:64页,1M)的其他页是否也有脏页,有则一起刷写。5.6可以通过参数来控制是否刷写:

innodb_flush_neighbors,机械磁盘建议开启,固态硬盘建议设置为0,即关闭。