《MySQL技术内幕-InnoDB存储引擎》整理1-MySQL及InnoDB简介

一、数据库与实例

  • 数据库:文件的集合,依照某种数据模型组织起来并存放于二级存储器中的数据集合
  • 数据库实例:数据管理软件,应用程序只有通过数据库实例才能和数据库打交道

数据库与数据库实例通常一一对应,但是在集群情况下肯能存在一个数据库被多个数据实例使用的情况。MySQL数据库实例在操作系统上的表现就是一个进程

二、InnoDB存储引擎

InnoDB存储引擎支持事务,特点是行锁设计,支持外键,支持非锁定读,支持全文索引。InnoDB存储引擎有多个内存块,这些内存块组成了一个大的内存池,负责维护进程和线程需要访问的多个内部数据结构,缓存数据等。

1、后台线程

InnoDB存储引擎是多线程的模型,因此后台有多个不同的后台线程,负责处理不同的任务

  • Master Thread:核心后台线程,负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性等。
  • IO Thread:负责Async IO请求的回调处理。包括write、read、insert buffer和log IO thread
  • Purge Thread:事务提交后所使用的undolog可能不再使用,因此PurgeThread用作回收已使用并分配的undo页
  • Page Cleaner Thread:单独负责脏页的刷新操作,减轻Master Thread的工作及对于用户查询线程的阻塞

2、内存

1、缓冲池简单来讲是一块内存区域,用来弥补磁盘速度较慢的影响。读取页时,会先将从磁盘中读取到的页放在缓冲池中,下一次读取相同的页时,会先判断该页是否在缓冲池中,因此其大小直接影响数据库的整体性能,可以通过innodb_buffer_pool_size来设置。

缓冲池中缓存的数据页类型有索引页、数据页、undo页、插入缓冲、自适应哈希索引、锁信息、数据字典信息等,而不仅仅是索引页和数据页。InnoDB允许有多个缓冲池实例,可以通过innodb_buffer_pool_instance来进行配置。目前可以通过information_schema架构下的表INNODB_BUFFER_POOL_STATS来查看缓冲池的状态

2、数据库中的缓冲池是通过LRU算法来管理的,即最频繁使用的页在LRU列表的前端,最少使用的页在LRU列表的尾端,当缓冲池不能存放新读取到的页时,将首先释放LRU列表中尾端的页。InnoDB存储引擎的LRU算法加入了midpoint位置,默认值为5/8,即新读取的页会插入到LRU列表尾端的37%的位置。midPoint之前的列表称为new列表,之后的位置称为old列表,new列表中的数据称为热点数据。不使用传统的LRU算法的目的是避免类似扫描操作将热点数据刷新出缓冲池。

InnoDB引擎使用innodb_old_blocks_time参数来管理LRU列表,该参数表示页读取到mid位置后需要等待多久会被加入到LRU列表的热端,可以通过设定该值来尽可能的保证热点数据不被刷出。此外可以通过设定innodb_old_blocks_pct来设定old数据的比例。LRU列表用来管理已经读取的页,数据库启动时LRU列表为空,此时所有页都放在Free列表中,当需要从缓冲池中分页时,首先从Free列表中查找是否有可用的空闲页,有则从Free列表删除,加入到LRU列表中,否则将淘汰LRU列表末尾的页将空间分配给新页。使用show engine innodb status可以看到过去某个范围内InnoDB存储引擎的状态。其中buffer pool hit rate表示缓冲池的命中率,通常应该在95%以上。

缓冲池中页的大小默认为16KB,但是可以进行压缩,变为1KB、2KB、4KB和8KB,对于非16KB的页是通过unzip_LRU列表进行管理的,其通过伙伴算法进行内存的分配。LRU列表中的页被修改后,称该页为脏页,即缓冲池中的页和磁盘上的页不一致,这时数据库会通过CheckPoint机制将脏页刷回磁盘,Flush页表中的页即为脏页列表,藏页既存在于LRU列表中,又存在于Flush列表中,二者互不影响。

3、重做日志缓冲:内存区域中还包括重做日志缓冲,按一定频率将其刷新到重做日志文件,其大小可以由配置参数innodnb_log_buffer_size控制,默认为8M。

4、额外的内存池:对于一些数据结构本身的内存进行分配时,需要从额外的内存池中进行申请。

3、CheckPoint技术

当前事务数据库普遍采用Write Ahead Log策略来避免刷新数据时宕机导致的数据丢失问题,即当事务提交时,先重做日志,再修改页,当数据丢失时则可以通过重做日志来完成数据的恢复。但是当数据库运行时间较长时发生宕机,重做日志的时间会非常久,此时恢复的代价也会非常大,CheckPoint技术的目的则用来解决下述的几点:①缩短数据库的恢复时间;②缓冲池不够用时,将脏盘刷新到磁盘;③重做日志不可用时,刷新脏页。当发生宕机时只需要重做CheckPoint后的日志来进行恢复。当缓冲池不够用时,也需要强制执行CheckPoint将脏页刷回磁盘。

InnoDB中有两种CheckPoint:Sharp CheckPoint和Fuzzy CheckPoint。Sharp CheckPoint发生在数据库关闭时将所有的脏页都刷新回磁盘,但是在运行时使用会极大的影响数据库的可用性,因此InnoDB内部使用Fuzzy CheckPoint进行页的刷新,即只刷新一部分脏页,通常有下述4种情况:

  • Master Thread CheckPoint:以每秒或每十秒的速度从缓冲池的脏页列表中刷新一定比例的页回磁盘,整个过程为异步
  • FLUSH_LRU_LIST CheckPoint:检查LRU列表中是否有可用空间操作发生在用户查询线程中,如果页中有脏页则进行CheckPoint
  • Async/Sync Flush CheckPoint:重做日志文件不可用时,强制将一些页刷新回磁盘,脏页是从脏页列表中选取的
  • Dirty Page too much CheckPoint:脏页太多导致InnoDB引擎强制进行CheckPoint,以保证缓冲池中有足够可用的页

4、Master Thread工作方式

1、InnoDB1.0.x之前的Master Thread

Master Thread具有最高的线程优先级别,其内部循环由多个循环组成,主循环、后台循环、刷新循环、暂停循环,Master会根据数据库运行的状态在这几个循环中进行切换。

主循环有两大部分的操作,每秒钟的操作和每10秒钟的操作。每秒钟的循环包括:①日志缓冲刷新到磁盘(即使事务没有提交)②合并插入缓冲(可能)③至多刷新100个InnoDB中缓冲池中的脏页到磁盘(可能)④如果当前没有用户活动,切换到后台循环(可能)。每10秒的操作包括以下内容:①刷新100个脏页到磁盘(可能)②合并至多5个插入缓冲(总是)③将日志缓冲刷新到磁盘(总是)④删除无用的Undo页(总是)⑤刷新100个或者10个脏页到磁盘(总是)

若当前没有用户活动或者数据库关闭,就会切换到后台循环,后台循环会执行以下操作:①删除无用的Undo页(总是)②合并20个插入缓存(总是)③跳回到主循环(总是)④不断刷新100个页知道符合条件(可能,跳转到刷新循环中完成)

2、InnoDB1.2.x之前的Master Thread

在先前的版本中InnoDB存储引擎限制最大只会刷新100个脏页到磁盘,合并20个缓冲插入。因此在1.0.x版本之后提供了参数innodb_io_capacity用来表示磁盘IO的吞吐量,默认值为200,并且刷新脏页的数量为该参数,合并插入缓冲的数量为其5%。另一个参数是innodb_max_dirty_pages_pct默认值,原先为90%,即脏页占用缓冲池90%时才刷新脏页,后续已调整为75%。另一个值是innodb_adaptive_flushing(自适应地刷新),该值影响每秒刷新脏页的数量,它会根据产生重做日志的速度来决定最合适刷新脏页数量,而不仅仅是上述的innodb_max_dirty_pages_pct。此外参数innodb_purge_batch_size用来控制每次full purge回收的Undo页的数量

3、InnoDB1.2.x版本的Master Thread

将每秒操作和每10秒操作分离,同时对于刷新脏页的操作,从Master线程分离到Page Cleaner Thread,减轻Master Thread的工作同时进一步提高了系统的并发性

5、InnoDB关键特性

插入缓冲(Insert Buffer),两次写(Double Write),自适应哈希索引(Adaptive Hash Index),异步IO(Async IO),刷新邻接页(Flush Neighbor Page)等特性为InnoDB存储引擎带来更好的性能以及更高的可靠性

1、插入缓冲

1、Insert Buffer

在多数情况下,一张表上有多个非聚集(数据的物理顺序与键的索引顺序不同)的辅助索引,且不唯一。对于非聚集索引的插入或更新操作,不是每一次直接插入到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,若在则直接插入,若不在则先放入到一个插入缓冲对象中,数据库这个非聚集的索引已经插入到了叶子节点,而实际并没有,只是存放在了一个位置,随后会以一定的频率和情况进行Insert Buffer和辅助索引叶子节点的合并操作,通过将多个插入操作合并到一个操作中,大大提高了非聚集索引的插入性能

Insert Buffer的使用需要满足哦两个条件,索引是辅助索引,索引不是唯一的。其缺点是应用程序进行大量的插入操作,并且涉及了不唯一的非聚集索引,此时Mysql宕机将会导致大量的Insert Buffer没有合并到非聚集索引中去,因而恢复时间会很长。

2、Change Buffer

InnoDB从1.0.x版本开始引入了Change Buffer,可以视其为Insert Buffer的升级版,InnoDB存储引擎可以对DML操作都进行缓冲,它们分别是Insert Buffer、Delete Buffer和Purge Buffer,它们的适用对象仍然是非唯一的辅助索引。

3、内部实现

Insert Buffer的数据结构是一颗B+树,其负责所有的表的辅助索引进行Insert Buffer,它存放在共享表空间中。它由叶子节点和非叶子节点组成,非叶子节点存放的是查询的键值(search key)。

键值一共占9个字节,space表示待插入记录所在表的表空间Id,在InnoDB存储引擎中,每个表有一个唯一的space id。space占用4个字节,marker占用1字节用来兼容老版本的Insert buffer,offset表示页所在的偏移量,占用4个字节。

对于插入到Insert Buffer B+树的叶子节点的记录,并不是直接插入,而是需要根据如下的规则进行构造,space、marker、offset和之前非叶子节点一样,后面的metadata占用4个字节:IBUF_REC_OFFSET_Count占用两个字节,用来排序每个记录进入Insert Buffer的顺序,其余的IBUF_REC_OFFSET_Type和IBUF_REC_OFFSET_Flag各占1个字节,从第五列开始插入实际的数据

启用Insert Buffer之后,辅助索引页的记录可能被插入到Insert Buffer B+树中,所以为了每次Merge成功,需要一个特殊的页用来标记每个辅助索引页的可用空间,特殊页的类型为Insert Buffer BitMap。每个Insert Buffer BitMap页可以用来追踪16384个辅助索引页。

4、Merge Insert Buffer

合并 Insert Buffer的操作发生于以下几种情况下:①辅助索引页被读取到缓冲池时;②Insert Buffer BitMap页追踪到辅助索引页没有可用空间时;③Master Thread

2、两次写

其确保数据页的可靠性。通过页的副本来还原该页,再进行重做即为doubleWrite。它由两部分组成:内存中的doubleWrite buffer,大小为2M,另一部分是物理磁盘上共享表空间中连续的128个页,即两个区,大小同样为2M。在对缓冲池中的脏页进行刷新时,并不直接写磁盘,而是先将脏页复制到内存的doubleWrite buffer,再通过doubleWrite buffer分两次写,每次1M顺序地写入共享表空间的物理磁盘上,然后同步磁盘,完成doubleWrite页的写入后,再将doubleWrite Buffer中的页写入各个表空间文件中。

3、自适应哈希索引

InnoDB存储引擎会监控对表上各索引的查询,如果建立哈希索引会带来速度提升,则建立哈希索引,即所谓的自适应哈希索引(AHI)。AHI通过缓冲池的B+树页构造而来,因此建立速度很快,不需要对整张表进行构建。AHI有一个要求,即对这个页的连续访问模式必须是一样的。访问模式指的是查询的条件一样。

4、异步IO

5、刷新临近页

刷新一个脏页时,InnoDB存储引擎会检测该页所在区的所有页,如果是脏页那么会一起进行刷新。其好处是通过异步IO将多个IO写入操作合并为一个IO操作。考虑到不怎么脏的页写入后又很快变成了脏页,可以通过innodb_flush_neighbors来控制是否启用该特性,建议传统机械键盘启用,固态硬盘关闭

6、启动、关闭与恢复

innodb_fast_shutdown参数会影响存储引擎InnoDB的行为,它可以设置为0、1、2,默认值为1。0表示MySql关闭时,InnoDB需要完成所有的full purge和merge insert buffer,并且将所有的脏页刷回磁盘,这会花费一定的时间甚至几个小时,通常会在升级InnoDB时开启;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表示不查看撤销日志,InnoDB会将为提交的事务视为以提交,为6表示不进行前滚操作。需要注意的是设置为大于0后可以进行Create、Drop和Select操作,但是无法进行DML操作

作者:Jscroop
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
原文地址:https://www.cnblogs.com/Jscroop/p/14534432.html