第二章 InnoDB 存储引擎

  1. InnoDB 体系架构

 

  1.1 后台线程

  后台线程的主要做用是负责刷新内存池中的数据,保证缓冲池中缓存的是最近的数据。此外,还会将已修改的数据文件刷新到磁盘文件,同时保证在数据库发生异常的状况下InnoDB能恢复到正常运行的状态。前端

  InnoDB存储引擎是多线程的模型,其后台有多个不一样的后台线程,负责处理不一样的任务mysql

  1) Master threadios

  负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页(因为磁盘的读写速度远赶不上内核的读写速度,系统把读写频繁的数据放在内存中,称为高速缓存。当进程修改了高速缓存中的数据时,该页被内核标为脏页)的刷新、合并插入缓冲、undo页的回收等。算法

  Master thread具备最高线程优先级。内部由多个循环(loop)组成:主循环(loop)、后台循环(background loop)、刷新循环(flush loop)、暂停循环(suspend loop)。Master Thread会根据数据库运行状态在不一样循环之间切换。其操做伪码以下:sql

void master_thread(){
    goto loop;
loop:
    for(int i = 0; i<10; i++){
    thread_sleep(1)  // sleep 1 second
    do log buffer flush to disk    // 日志缓冲刷新到磁盘
    if (last_one_second_ios < 5% innodb_io_capacity(磁盘吞吐量))   // 当前一秒发生的IO次数
        do merge 5% innodb_io_capacity insert buffer              // 合并插入缓冲
    if (buf_get_modified_ratio_pct (缓冲池中脏页比例) > innodb_max_dirty_pages_pct)  
        do buffer pool flush 100% innodb_io_capacity dirty page
    else if enable adaptive flush
        do buffer pool flush desired amount dirty page
    if ( no user activity )
        goto background loop
    }
    
    if (last_ten_second_ios < innodb_io_capacity)
        do buffer pool flush 100% innodb_io_capacity dirty page
    do merge 5% innodb_io_capacity insert buffer
    do log buffer flush to disk
    do full purge                                                 // 删除无用的undo页 对表进行update delete操做时,原先的行被标记为删除
                                                                  // 因为一致性读的关系须要保留这些行版本信息。但在full purge时,会判断
                                                                  // 这些标记为删除的行是否能够删除(有时,会有查询操做还须要读以前版本的
                                                                  // undo信息)
    if ( buf_get_modified_ratio_pct > 70% )
        do buffer pool flush 100% innodb_io_capacity dirty page
    else 
        do buffer pool flush 10% innodb_io_capacity dirty page
    goto loop
    
    background loop:
    do full purge
    do merge 100% innodb_io_capacity insert buffer
    if not idle:
    goto loop:
    else:
        goto flush loop
        
    flush loop:
    do buffer pool flush 100% innodb_io_capacity dirty page
    if ( buf_get_modified_ratio_pct>innodb_max_dirty_pages_pct)
        goto flush loop
    goto suspend loop
    
    suspend loop:
    suspend_thread()
    waiting event
    goto loop;
    }

  2) I/O thread数据库

  InnoDB存储引擎中大量使用AIO(Async IO)来处理写IO请求,以提升数据库性能缓存

  3) Purge thread多线程

  事务提交后,所使用的undo log(用于事务回滚,将数据库恢复到修改前的样子,在第七章 事务会详细介绍)可能不在须要了,所以须要Purge Thread来回收这些undo 页架构

  4) Page Cleaner thread并发

  为了减轻Master Thread 的工做以及用户查询线程的阻塞,1,2.x版本将脏页的刷新操做放到单独的线程(Page Cleaner thread)来完成

  1.2 内存

  InnoDB内存数据对象

      

  1) 缓冲池

  InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理,即基于磁盘的数据库系统。

  一般将部分或所有的页备份到缓冲池(一块内存区域)中,在读取页时,先判断缓冲池中是否有须要的页,若是有,称为缓存命中;不然,从磁盘中读取相应的页。

  而对于数据库中页的修改,首先修改缓冲池中的页,而后再经过checkpoint技术(本章第二节介绍)将页刷新会磁盘中。

  2) 缓冲池内存管理

  LRU list

  数据库中的缓冲池经过LRU(least recently used)算法进行管理。最频繁使用的的页在LRU列表的前端,最少使用的页在尾端。当缓冲池不能存放新读到的页时,将首先释放LRU列表中尾端的页。InnoDB也对LRU算法作了改进,新读取到的页放到了LRU列表的midpoint位置(距列表尾端3/8的位置)。这是为了防止进行诸如索引或数据扫描等,须要访问表中不少页的操做,在插入到首部时,将本来的热点数据挤出LRU列表,这种操做访问的数据一般仅在本次查询中用到,并不是热点数据。在下次查询时,InnoDB须要再次访问磁盘将热点数据拷贝回缓冲池。

  innodb_old_blocks_time用于表示页读取到mid位置后,须要多久才会被加入到LRU列表的热端。当页从LRU列表的old部分(midpoint以后的列表)加入到new部分时,称为page made young.

  Free list

  数据库刚启动时,LRU列表是空的。这时页都存在Free list中。当须要从缓冲池中分页时,首先从Free list查找是否有可用的空闲页,如有则将该页从free list中删除,并放入到LRU list中。不然,根据LRU算法,淘汰LRU列表末尾的页,将该内存空间分配给新页。

  Flush list

  flush list 用来管理将脏页刷新回磁盘。

  1.3 重作日志缓冲

  存储引擎通常先将重作日志信息放到重作日志缓冲(redo log buffer),而后按必定频率刷新到重作日志文件:

  • Master thread 每一秒将重作日志缓冲刷新到重作日志文件
  • 每一个事务提交时会将重作日志缓冲刷新到重作日志文件
  • 当重作日志缓冲池剩余空间小于1/2时,将重作日志缓冲刷新到重作日志文件

  1.4 额外的内存池

  缓冲池的帧缓冲还有对应的缓冲控制对象(记录了LRU、锁、等待等信息)存储在额外内存池中。在申请了很大的InnoDB缓冲池时,也应考虑相应增长额外内存池的大小

  2. Checkpoint 技术

  Checkpoint 技术解决如下几个问题:

  • 缩短数据库恢复时间
  • 缓冲池不够用时,将脏页刷新到磁盘
  • 重作日志不可用时,刷新脏页(事务数据库广泛采用Write Ahead Log策略,先写重作日志,再修改页,避免宕机形成的数据丢失)

  1) InnoDB存储引擎确保LRU list有默认1024个页可用,若是没有,根据LRU算法会溢出最近最少使用的页,若此页为脏页,会执行checkpoint,将脏页刷新回磁盘

  2) 重作日志不可用的状况出现是由于当前事务数据库系统对重作日志的设计都是循环使用的,而不是容许其无限增大。当重作日志不可用时须要强制将一些页刷新回磁盘

  3) Master thread 中发生的checkpoint,每秒或每十秒从缓冲池中的脏页列表刷新必定比例的页回磁盘

  4) 缓冲池中脏页占75%时,强制进行checkpoint 

  3. InnoDB关键特性

  3.1 插入缓冲 (insert buffer / change buffer(MySQL 5.5以后版本的叫法))

  该部份内容亦参考了博客 https://blog.csdn.net/qq_36652619/article/details/89460786    http://mysql.taobao.org/monthly/2015/07/01/

  插入汇集索引(clustered index)通常是顺序的,不须要磁盘的随机读取。二级索引(secondary index,亦称非汇集索引)一般不是惟一的,顺序相对随机,删除和更新可能会影响不在索引树中相邻的二级索引页。

  对于非汇集索引的插入或更新操做,不是每一次直接插入到索引页中,而是先判断插入的非汇集索引页是否在缓冲池中,若在则直接插入;若不在,则先放入到一个Insert buffer对象中,以此减小二级索引的随机IO,并达到操做合并的效果。Merge Insert/Change Buffer可能发生在如下几种状况:

  • 辅助索引页被读取到缓冲池中 (执行SELECT操做将页缓冲到缓冲池中,须要检查该辅助索引页是否有记录存放在Insert Buffer B+树中,有则一次性更新操做到辅助索引页中)
  • Insert Buffer Bitmap 页追踪到该辅助索引页已无可用空间(至少有1/32的可用空间)
  • Master Thread (1.1 中master thread介绍过,每秒或每十秒进行一次merge insert buffer) 

  change buffer是一颗B+树,ibuf btree经过三列(space id(每张表有惟一的ID), page no(表中页的偏移量), counter)做为主键来惟一决定一条记录,其中counter是一个递增值,目的是为了维持不一样操做的有序性,例如能够经过counter来保证在merge时执行以下序列时的循序和用户操做顺序是一致的:INSERT x, DELETE-MARK x, INSERT x

  

  3.2 两次写

  若是说Insert Buffer带给InnoDB存储引擎性能上的提高,doublewrite带给InnoDB存储引擎的是数据页的可靠性。

  但数据库宕机时,可能InnoDB存储引擎正在写入某个页到磁盘中,好比,16KB的页,只写了前4页,以后就发生了宕机,这种状况称为部分写失效。

   

  由上图可知,doublewrite由两部分组成,一部分是内存中的doublewrite buffer,大小为2MB,另外一部分是物理磁盘上共享表空间中连续的128个页,即两个区,大小一样为2MB。在对缓冲池的脏页进行刷新时,并不直接写磁盘,而是会经过memcpy函数将脏页先复制到内存中的doublewrite buffer,以后经过doublewrite buffer分两次,每次1MB顺序写入共享表空间的物理磁盘上(doublewrite页是连续的,因此写开销并非很大),在完成doublewrite页的写入后,再将doublewrite buffer中的页写入各个表空间文件中。若是操做系统在将页写入磁盘的过程当中发生了崩溃,在恢复的过程当中,InnoDB存储引擎能够从共享表空间中的doublewrite中找到该页的一个副本,将其复制到表空间文件,再应用重作日志。

  3.3 自适应哈希索引

  B+ 树中,哈希查找的次数取决于B+树的高度。InnoDB存储引擎会监控表上各索引页的查询,并根据如下两种状况创建自适应哈希索引(adaptive hash index):

  • 以某一模式连续访问了100次
  • 页经过该模式访问了N次,N=页中记录/16

  须要注意的是,哈希索引只能用来搜索等值查询,如 SELECT * FROM table WHERE index_col = XXX,不能用于范围查询。

  3.4 异步IO

  在同步I/O状况下,查询线程将I/O请求放入队列,innodb后台线程会遍历请求队列,每次处理一个请求。并行处理的请求个数受到后台线程的数量控制(参数innodb_read_io_threads)。AIO状况下,查询线程直接将I/O请求分发给操做系统,从而避免的后台线程数量对并发数的控制。innodb后台线程只须要等待操做系统对IO请求的处理反馈信息。另外一个优势是IO Merge,即将多个IO合并为一个IO。例如:用户访问的页(space,page_no)为:(8,6)、(8, 7)、(8,8),每一个页大小为16KB,同步IO须要进行3次IO操做。而AIO会判断这三个页是连续的。所以发送一个IO请求,从(8,6)开始,读取48KB的页。

  Read ahead(预读),脏页的刷新(磁盘的写入操做)都是经过AIO完成的。

  3.5 刷新邻接页

  当刷新一个脏页时,InnoDB存储引擎会检测该页所在区的全部页,若是是脏页,那么经过AIO一块儿进行刷新。但也存在两个问题:

  • 有些刷新到磁盘的脏页,可能很快又变成脏页
  • 固态磁盘有着较高的IOPS(input/output operations per second),不太须要该特性(通常机械磁盘建议开启该特性, innodb_flush_neighbors)
相关文章
相关标签/搜索