1.InnoDB存储引擎是多线程的模型
Master Thread
老大哥核心线程。主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页刷新、合并插入缓冲、UNDO页的回收等
IO Thread
负责IO请求的回调
Purge Thread
1.1后单独线程处理回收(之前是在master thread处理)已经使用并分配的undo页(事务被提交后,其所使用的undolog可能不再需要)
Page Cleaner Thread
1.2.x后,单独线程处理脏页的刷新操作
2.内存
缓冲池
由于cpu与磁盘之间存在较大速度差,需要运用缓冲池提高性能,缓冲池本质就是使用到内存。
在数据库读取页,首先将从磁盘读到的页FIX到缓冲池中,下次再读到相同的页时,首先判断是否在缓冲池中,如果命中,则直接读取该页,否则,读取磁盘上的页。
在数据库修改页,首先修改在缓冲池中的页,然后再以一定的频率(CheckPoint机制)刷新到磁盘上(就是master thread干的活)。
缓冲池的大小直接影响数据库的整体性能。
1.0.x开始,可以有多个缓冲池实例。每个页根据hash值平均分配到不同的缓冲池实例中。
LRU List、Free List、Flush List
在InnoDB存储引擎中,缓冲池中页的大小默认为16KB,使用LRU算法(优化后,加入midpoint位置)进行管理,默认配置下,在LRU列表长度的5/8处。新读取的页不直接插入到头部,而是插入到midpoint的位置。在midpoint之后的列表称为old列表,之前的列表成为new列表。
LRU管理页的过程:刚启动时缓冲池都是空的,页都在free列表中,当需要从缓冲池中分页时,首先从free列表中查找有没有可用的空闲页,如果有则从free列表删除,放到LRU列表中。否则,淘汰LRU末尾的页,将该内存空间分配给新的页,当页从LRU列表的old加入到new时,称为page made young,如果因为innodb_old_blocks_time而没有发生移动时称为page not made young
对于非16KB的页,采用unzip_LRU列表进行管理,通过伙伴算法进行内存的分配。例如需要从缓冲池中申请页为4KB的大小,过程为:
- 检查4KB的unzip_LRU列表,检查是否有可用的空闲页
- 若有,则直接使用
- 否则,检查8KB的unzip_LRU列表
- 若能够得到空闲页,将页分成2个4KB页,存放到4KB的unzip_LRU列表
- 若不能得到空闲页,从LRU列表中申请一个16KB的页,将页分为1个8KB的页,2个4KB的页,分别存放到对应的unzip_LRU列表中
在LRU列表中的页被修改后,该页称为脏页,即缓冲池中的页和磁盘上的页数据不一致。脏页同时存在于LRU列表和Flush列表中。
LRU列表用来管理缓冲池中页的可用性,Flush列表用来管理将页刷新回磁盘,二者互不影响。
重做日志缓冲
innoDB存储引擎首先将重做日志信息先放入到这个缓冲区,然后按一定频率将其刷新到重做日志文件。一般每一秒会将重做日志缓冲刷新到日志文件,所以缓存不用设置这么大。
重做日志在下列三种情况会将缓冲中的内容刷新到磁盘
- Master Thread每一秒将重做日志缓冲刷新到重做日志文件
- 每个事务提交时会将重做日志缓冲刷新到重做日志文件
- 当重做日志缓冲池剩余空间小于1/2时,重做日志缓冲刷新到重做日志文件
额外的内存池
在InnoDB存储引擎中,对内存的管理是通过堆的方式进行,在对一些数据结构本身的内存进行分配时,需要从额外的内存池中进行申请,当该区域的内存不够时,会从缓冲池中进行申请
3.Checkpoint技术
目的是解决以下几个问题:
- 缩短数据库的恢复时间
- 缓冲池不够用时,将脏页刷新到磁盘
- 重做日志不可用时,刷新脏页
在InnoDB内部,有两种Checkpoint,分别为Sharp Checkpoint、Fuzzy Checkpoint
Sharp Checkpoint发生在数据库关闭时将所有的脏页都刷新回磁盘,这是默认的工作方式,在运行时不用这个
在InnoDB内部使用Fuzzy Checkpoint进行页的刷新,即只刷新一部分脏页,而不是刷新所有的脏页回磁盘
Fuzzy Checkpoint分为下面几种情况
Master Thread Checkpoint:差不多以每秒或每十秒的速度从缓冲池的脏页列表刷新一定比例的页到磁盘(异步)
FLUSH_LRU_LIST Checkpoint:单独使用Page Cleaner线程进行,为了保证LRU列表至少有100个空闲页可使用,不足时会将尾端的页移除
Async/Sync Flush Checkpoint:重做日志不可用时,从脏页列表中选择强制将一些页刷新回磁盘,单独使用Page Cleaner Thread进行
Dirty Page too much Checkpoint:脏页数量太多,导致InnoDB存储引擎强制进行Checkpoint,保证缓冲池中有足够可用的页
4.Master Thread工作方式
由多个循环组成:主循环、后台循环、刷新循环、暂停循环。
主循环
每秒操作:1、日志缓冲刷新到磁盘,即使这个事务还没有提交 2、合并插入缓冲(后来更新成根据innodb_io_capacity的值的5%) 3、至多刷新100个InnoDB的缓冲池中的脏页到磁盘(后来更新成根据innodb_io_capacity的值) 4、如果当前没有用户活动,切换到后台循环
每十秒操作:1、刷新100个脏页到磁盘 2、合并至多5个插入缓冲 3、将日志缓冲刷新到磁盘 4、删除无用的Undo页 5、刷新100个或者10个脏页到磁盘
后台循环
数据库空闲或者数据库关闭时就会切换到这个循环,会执行以下操作:1、删除无用的Undo页 2、合并20个插入缓冲 3、跳回到主循环 4、不断刷新100个页直到符合条件
5.InnoDB关键特性
- 插入缓冲(提高性能)
插入缓冲和数据页一样,也是物理页的一个组成部分。对于非聚集索引的插入和更新操作,不是每一次直接插入到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,若在,则直接插入,若不在,则先放入到一个Insert Buffer对象中。然后再以一定的频率和情况进行Insert Buffer和辅助索引页子节点的合并操作。Insert Buffer的使用需要同时满足两个条件:1、索引是辅助索引 2、索引不是唯一的。Insert Buffer是一棵B+树,负责对所有的表的辅助索引进行Insert Buffer,这棵树存在共享表空间中。
启用Insert Buffer索引后,辅助索引页中的记录可能被插入到Insert Buffer B+树中,为了保证每次Merge Insert Buffer页必须成功,还需要有一个特殊的页用来标记每个辅助索引页的可用空间。这个页就是Insert Buffer Bitmap。每个Insert Buffer Bitmap页用来追踪16384个辅助索引页,就是256个区。每个Insert Buffer Bitmap页都在16384个页的第二页中。 - 两次写(提高数据可靠性)
在应用重做日志前,用户需要一个页的副本,当写入失效发生时,先通过页的副本来还原该页,再进行重做。doublewrite由两部分组成,一部分是内存中的doublewrite buffer,大小为2MB,另一部分是物理磁盘上共享表空间中连续的128个页,即两个区,大小同样为2MB。在对缓冲池的脏页进行刷新时,并不直接写磁盘,而是会通过memcpy函数将脏页先复制到内存中的doublewrite buffer,之后通过doublewrite buffer再分两次,每次1MB顺序地写入共享表空间的物理磁盘上,然后马上调用fsync函数,同步磁盘,避免缓冲写带来的问题。在完成doublewrite页的写入后,再将doublewrite buffer中的页写入各个表空间文件中,此时的写入则是离散的。 - 自适应哈希索引(快速查找方法)
InnoDB存储引擎会自动根据访问的频率和模式来自动地为某些热点页建立哈希索引。AHI有三个要求,即对这个页的连续访问模式必须是一样的、以该模式访问了100次、页通过该模式访问了N次,其中N=页中的记录*1/16 - 异步IO(提高磁盘性能)
用户可以在发出一个IO请求后立即再发出另一个IO请求,当全部IO请求发送完毕后,等待所有IO操作的完成,这就是AIO。AIO的另一个优势是可以进行IO Merge操作,将多个IO合并为1个IO,这样可以提高IOPS的性能。 - 刷新临接页(合并IO操作)
当刷新一个脏页时,InnoDB存储引擎会检测该页所在区的所有页,如果是脏页则一起刷新。
6.启动、关闭与恢复
在关闭时,参数innodb_fast_shutdown影响这表的存储引擎为InnoDB的行为。
- 0:表示在mysql数据库关闭时,InnoDB需要完成所有的full purge和merge insert buffer,并且将所有的脏页刷新回磁盘。
- 1:表示不需要完成上述的full purge和merge insert buffer操作,但是在缓冲池中的一些数据脏页还是会刷新回磁盘。
- 2:表示不完成full purge和merge insert buffer操作,也不将缓冲池中的数据脏页写回磁盘,而是将日志都写入日志文件。
在恢复时,参数innodb_force_recovery影响了整个InnoDB存储引擎恢复的状况。
- 0:表示当发生需要恢复时,进行所有的恢复操作
- 1:忽略检查到的corrupt页
- 2:组织Master Thread线程的运行
- 3:不进行事务的回滚操作
- 4:不进行插入缓冲的合并操作
- 5:不查看撤销日志
- 6:不进行前滚的操作