InnoDB存储引擎

InnoDB体系架构

InnoDB有多个内存块,可以认为这些内存块组成了一个大的内存池,负责如下工作:

  • 维护所有进程/线程需要访问的多个内部数据结构。
  • 缓存磁盘上的数据,方便快速的读取,并且在对磁盘文件的数据进行修改之前在这里缓存。
  • 重做日志缓冲。
    在这里插入图片描述
    后台线程的主要作用是 1. 负责刷新内存池中的数据,保证缓冲池中的内存缓存的是最近的数据。此外,将 2. 已经修改的数据文件刷新到磁盘文件,同时 3. 保证在数据库发生异常情况下InnoDB能恢复到正常运行的状态

后台线程

默认情况下,InnoDB存储引擎的后台线程有7个——4个IO线程,1个Master线程,1个锁监控线程,1个错误监控线程。
在这里插入图片描述
4个IO线程分别是insert buffer thread、log thread、read thread、write thread。

内存

InnoDB存储引擎内存由以下几个部分组成:缓冲池(buffer pool)、重做日志缓冲池(redo log buffer)、额外的内存池。以下显示了一台MySQL数据库服务器,他将InnoDB存储引擎的缓冲池、重做日志缓冲池以及额外的内存池分别设置为2.5G、8M和8M。
在这里插入图片描述
缓冲池是占最大块的内存,用来存放各种数据的缓存。因为InnoDB的存储引擎的工作方式总是将数据库文件按页(每页16K)读取到缓冲池,然后按最近最少使用算法来保留在缓冲池中的缓存数据。如果数据库文件需要修改,总是首先修改在缓冲池中的页(发生修改后,该页即为脏页),然后在按照一定的频率将缓冲池的脏页刷新到文件。

使用show engine innodb status来查看innodb_buffer_pool的具体使用情况。
在这里插入图片描述
buffer pool size表明一共有多少个缓冲帧,每个帧大小为16K,所以这里一共分配了65536*16K=1G内存的缓冲池。Free buffers表明当前空闲的缓冲帧,Database pages表明已经使用的缓冲帧。Modified db pages表明脏页的数量。

具体来看,缓冲池中缓存的数据页类型有 索引页、数据页、undo页、插入缓冲、自适应哈希索引、InnoDB存储的锁信息、数据字典信息等。
在这里插入图片描述
日志缓冲将重做日志信息先放入这个缓冲区,然后按照一定频率将其刷新到重做日志文件,该值不需要很大,因为一般情况下每秒钟就会将重做日志缓冲刷新到日志文件,因为我们只需要保证每秒产生的事务量在这个缓冲大小之内即可。

master thread

InnoDB存储引擎的主要工作都是在单独的后台线程master thread中完成的。

master thread源码分析

master thread的线程优先级别最高。其内部由几个循环组成:主循环、后台循环、刷新循环、暂停循环。master thread会根据数据库运行的状态在 主循环、后台循环、刷新循环、暂停循环中进行切换。
loop称为主循环,因为大多数的操作都在这个循环中,其中有 两大部分组成:每秒钟的操作和每10秒的操作。 伪代码表示如下:
在这里插入图片描述
loop循环通过thread sleep来实现,意味着每秒一次和每10秒一次的操作是不精确的。在负载很大的情况下可能会有延迟。

每秒一次的操作包括
  • 日志缓冲刷新到磁盘,即使这个事务还没有提交(总是)
  • 合并插入缓冲(可能)
  • 至多刷新100个InnoDB的缓冲池中的脏页到磁盘(可能)
  • 如果当前没有用户活动,切换到background loop(可能)

合并缓冲并不是每秒都发生,InnoDB会判断当前一秒内发生的IO次数是否小于5次,如果小于5次,InnoDB会认为当前的IO压力很小,可以执行合并插入缓冲的操作。

刷新100个脏页也不是每秒都在发生,InnoDB存储引擎通过判断当前缓冲池中脏页的比例是否超过了配置文件中innodb_max_dirty_pages_pct这个参数(默认为90,代表90%),如果超过了这个阈值,InnoDB存储引擎认为需要做磁盘同步操作,将100个脏页写入磁盘。

每10秒一次的操作,包括如下内容
  • 刷新100个脏页到磁盘(可能)
  • 合并至多5个插入缓冲(总是)
  • 将日志缓冲刷新到磁盘(总是)
  • 删除无用的Undo页(总是)
  • 刷新100个或者10个脏页到磁盘(总是)

在以上的过程中,InnoDB存储引擎会先判断过去10s内磁盘的IO操作是否小于200次,如果是,InnoDB存储引擎认为当前有足够的磁盘IO操作能力,因此将100个脏页刷新到磁盘。

接着,InnoDB存储引擎会合并插入缓冲。不同于每秒操作时可能发生的合并插入缓冲,这次的合并插入缓冲一定会进行。

之后,InnoDB存储引擎会在执行一次将日志缓冲刷新到磁盘的操作,这与每秒发生的操作一样。

接着,InnoDB存储引擎会执行一步full purge操作,即删除无用的undo页。对表执行update、delete这类操作时,但是因为一致性读的关系,需要保留这些行版本信息,但是在full purge过程中,InnoDB存储引擎会判断当前事务系统中已被删除的行是否可以删除,如果可以,InnoDB会立即将其删除。

然后InnoDB存储引擎会判断缓冲池中脏页的比例,如果超过70%,则刷新100个脏页到磁盘;如果小于70%,则只需刷新10%的脏页到磁盘。

什么是插入缓冲
InnoDb存储引擎的插入缓冲是一种优化技术,旨在提高对非聚集索引插入操作的效率。在InnoDB中,数据存储在一个聚集索引中,同时可以有多个非聚集索引(也称为辅助索引)。当向表中插入数据时,除了要修改聚集索引外,还可能需要更新多个非聚集索引。

插入缓冲的工作原理如下:

  • 延迟写操作:当插入或者更新非聚集索引时,InnoDB不会立即查找并修改对应的索引页,如果该索引页不在缓冲池中,InnoDB会将这些操作记录在一个称为“插入缓冲”的区域,而不是直接执行昂贵的磁盘操作。
  • 批量处理:随着时间推移和更多插入操作的发生,插入缓冲累积了多个非聚集索引页的修改,之后,InnoDB会在合适的时机,将这些累积的操作合并并一次性的应用到实际的非聚集索引页上。这样,多次的随机IO转化为较少的顺序IO。
  • 条件应用:插入缓冲主要适用于非唯一且非聚集的索引。这是因为对于唯一索引,需要立即检查以防止重复键值,而对于聚集索引,其插入通常是顺序的,因此直接插入效率已经很高。

什么是Undo页
InnoDB存储引擎使用Undo页作为多版本并发控制(MVCC)和事务回滚机制的一部分。Undo页专门用于存储Undo日志,这些日志记录了事务对数据库所做的修改前的数据状态。

  • 多版本并发控制:Undo页帮助实现非锁定读,即事务可以看到在他开始前提交的任何数据的快照视图。当一个事务需要查看一个记录的历史版本时,InnoDB可以从Undo页中找回该记录在事务开始时的状态。
  • 事务回滚:如果事务需要回滚,InnoDB会利用Undo页中的信息撤销所有已经执行的更改。
  • 崩溃恢复:在数据库崩溃后重启时,InnoDB使用Redo Log和Undo 页来恢复未提交的事务和回滚他们的影响。
background loop

若当前没有用户活动(数据库空闲时)或者数据库关闭时,就会切换到这个循环。这个循环会执行如下操作:

  • 删除无用的undo页(总是)
  • 合并20个插入缓冲(总是)
  • 跳回到主循环(总是)
  • 如果不空闲,跳转回到主线程,否则进入flush loop,循环刷新100个脏页。

如果flush loop中也没有什么事情可以做了,InnoDB引擎会切换到suspend_loop,将master thread 挂起,等待事件的发生。
在这里插入图片描述
在这里插入图片描述

master thread潜在问题

从前面的伪代码看,无论何时,InnoDB存储引擎最多只会刷新100个脏页到磁盘,合并20个插入缓冲。如果是在密集写的应用程序中,每秒钟可能会产生大于100个的脏页,或者大于20个插入缓冲。即使磁盘能在1秒内处理多余100个页的写入和20个插入缓冲的合并,但是对于hard coding。master thread也只会选择每秒钟刷新100个的脏页,或者产生大于20个插入缓冲。同时,发生宕机需要恢复时,由于很多数据还没有刷新回磁盘,所以可能会导致恢复需要很久,尤其是对于insert buffer来说。

InnoDB Plugin提供了一个参数,用来表示磁盘IO的吞吐量,参数为innodb_io_capacity,默认值为200。对于刷新到磁盘的数量,会按照innodb_io_capacity的百分比来刷新相对数量的页。规则如下:

  • 在合并插入缓冲时,合并插入缓冲的数量为innodb_io_capacity数值的5%。
  • 在从缓冲区刷新脏页时,刷新脏页的数量为innodb_io_capacity。

如果使用了SSD类的磁盘,或者将几块磁盘做了RAID,当你的存储拥有更高的IO速度时,完全可以将innodb_io_capacity设置的更高,直到符合你的磁盘IO的吞吐量。

另一个问题是参数innodb_max_dirty_pages_act的默认值,在MySQL5.1版本之前,该值默认为90,意味着脏页占缓冲池的90%。该值“太大”了,因为,InnoDb存储引擎在每1秒刷讯缓冲池和flush loop时,会判断这个值,如果大于innodb_max_dirty_pages_act,才会刷新100个脏页。因此,如果有很大的内存时,脏页的刷新速度可能会降低。同样,在数据库的恢复阶段可能需要更多的时间。

当把这个值调到20时,会增加磁盘的压力。因此,目前默认值被修改为75.既可以加快刷新脏页的频率,也能保证磁盘IO的负载。

innodb_adaptive_flushing(自适应刷新),该值影响每1s刷新脏页的数量,原来的刷新规则是:如果脏页在缓冲池所占比例小于innodb_max_dirty_pages_act,不刷新脏页。而innodb_adaptive_flushing的引入,InnoDB存储引擎会通过一个名为buf_flush_get_desired_flush_rate函数来判断需要刷新脏页最合适的数量。因此,当脏页的比例小于innodb_max_dirty_pages_act,也会刷新一定量的脏页。

上述master_thread修改 每秒刷新脏页修改
在这里插入图片描述

InnoDB Plugin较之以前的InnoDB存储引擎在性能方面有了极大的提高,与master thread的改动是密不可分的。

关键特性

包括 插入缓冲、两次写、自适应哈希索引。这些特性为InnoDB存储引擎带来了更好的性能和更高的可靠性。

插入缓冲

主键是行唯一的标识符,在应用程序中 行记录的插入顺序是按照主键递增的顺序进行插入的。因此,插入聚集索引一般是顺序的,不需要磁盘的随机读取

比如:
在这里插入图片描述
id列是自增的,这意味着当执行插入操作时,id列会自动增长,页中的行记录按Id执行顺序存放。一般情况下,不需要随机读取另一页执行记录的存放。因此,在这样的情况下,插入操作一般很快就能完成。但是,不可能每张表上只有一个聚集索引,在更多的情况下,一张表上有多个非聚集的辅助索引,在这样的情况下产生了一个非聚集的并且不是唯一的索引。在执行插入操作时,数据页的存放还是按照id顺序存放,但是对于非聚集索引,叶子结点的插入不再是顺序的了。这时就需要离散的访问非聚集索引页,插入性能在这里变低了。因为B+树的特性决定了非聚集索引插入的离散型

InnoDB存储引擎开创性的设计了插入缓冲,对于非聚集索引的插入或者更新操作。不是每一次直接插入索引页中。而是先判断插入的非聚集索引页是否在缓冲池中。如果在,则直接插入;如果不在,则先放入一个插入缓冲区中,然后再以一定的频率执行插入缓冲和非聚集索引页子节点的合并操作,这时通常能将多个插入合并到一个操作中,大大提高了聚集索引插入和修改操作的性能

插入缓冲的使用需要满足以下两个条件

  • 索引是辅助索引
  • 索引不是唯一的
    当满足以上两个条件时,InnoDB存储引擎会使用插入缓冲。

两次写

插入缓冲带给InnoDB存储引擎的是性能,两次写带给InnoDB存储引擎的是数据的可靠性。当数据库宕机时,可能发生数据库正在从缓存中写入数据到一个页面的情况,而这个页只写了一部分(比如16K的页,只写前4K),我们称之为部分写失效。

doublewrite体系架构
在这里插入图片描述

doublewrite由两部分组成:一部分是内存中的doublewrite buffer,大小为2M;另一部分是物理磁盘上的共享表空间中连续的128个页,即两个区,大小同样为2MB。

当缓冲池的脏页刷新时,并不直接写磁盘,而是通过memcpy函数将脏页拷贝到内存中的doublewrite buffer,之后通过doublewrite buffer再分两次,每次写入1MB到共享表空间的物理磁盘上。然后马上调用fsync函数,同步磁盘。在这个过程中,因为doublewrite页是连续的,因此这个过程是顺序写的,开销不是很大。在完成doublewrite页写入后,再将doublewrite中的页写入各个表空间文件中,此时的写是离散的。

进一步总结

  • 第一次写:当InnoDB准备将缓冲池中的脏页刷新到磁盘时,首先将这些数据页写入到位于共享表空间中的一个连续的128页的大小的双写缓冲区域,这个操作是连续写入,由内存到内存的复制。
  • 第二次写:完成第一次写后,InnoDB会将双写缓冲中的数据分两个阶段写入最终的目标位置,首先是写入到数据文件的预分配空间(通常为两个连续的物理磁盘块),然后再从这个预分配空间复制到实际的数据文件和索引文件中对应的位置。

如果操作系统在将页写入磁盘过程中崩溃了,在恢复过程中,InnoDB存储引擎可以从共享表空间中找到该页的一个副本,将其拷贝到表空间文件,在应用重做日志进行恢复。

自适应哈希索引(AHI)

InnoDb存储引擎会监控对表上索引的查找,如果观察到建立哈希索引可以带来更高的速度,则建立哈希索引,所以称之为自适应的。

工作原理

InnoDB监控对表上索引的查询模式,当他检测到某些索引页被频繁访问时(即热点数据),InnoDB会在内存中为这些热点数据创建哈希索引。这个哈希索引是基于B-Tree索引之上的附加层,他存储在缓冲池中,而非磁盘上。对于用户,这个过程是透明的

优势

  • 提高查询速度
  • 自动优化:自适应这个过程是自动的,InnoDb根据实际运行时的访问模式动态调整。
  • 内存效率:哈希索引仅存在于内存中,不占用额外的磁盘空间,构建速度快,因此她直接基于已经缓存的数据页。

启动、关闭与恢复

在关闭时,参数innodb_fast_shutdown影响着表的存储引擎为innodb的行为。该参数的可取值为0、1、2.默认值为1.

  • 0 表示在MySQL数据库关闭时,InnoDB需要完成所有的full purge和merge insert buffer。并且将所有的脏页都写回磁盘。这需要一些时间,如果在进行InnoDB升级时,必须将这个参数设置为0,然后在关闭数据库。
  • 1 表示不需要完成上述的full purge和merge insert buffer,但是在缓冲池中的一些脏页数据需要刷新回磁盘。
  • 2 表示不完成full purge和merge insert buffer,也不将脏页写回磁盘。而是将日志写入日志文件,这样做不会有任何事务的丢失,但是下次MySQL数据库启动时,会进行恢复操作。

参数innodb_force_recovery影响了innodb存储引擎恢复的状况,默认值为0,代表当发生需要恢复时,执行所有的恢复操作,当不能进行有效恢复时,MySQL数据库可能发生宕机,并把错误写入错误日志中。

  • 16
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值