揭秘 MySQL InnoDB 存储引擎

2021年3月28日 9点热度 0条评论 来源: 庸人庸

文章目录

前言

  • 本文讲述InnoDB存储引擎的诸多功能和特性,即InnoDB的体系结构和工作原理。

一、InnoDB存储引擎

InnoDB存储引擎是什么?
  • InnoDBMySQL的默认事务型引擎,也是最重要、使用最广泛的存储引擎。如果要学习存储引擎,InnoDB是一个非常好的值得花最多时间去深入学习的对象,受益肯定比将时间花在每个存储引擎的学习上高的多。
InnoDB存储引擎的版本
  • InnoDB存储引擎被包含于所有MySQL数据库的二进制发行版本中。早期版本随着MySQL数据库的更新而更新。从MySQL5.1版本时,MySQL数据库允许存储版本开发商以动态的方式加载引擎。这样存储引擎的更新不受MySQL数据版本的限制。
  • 所以MySQL5.1中,可以支持两个版本的InnoDB,一个是静态编译的InnoDB版本,可以将视为老版本的InnoDB,另一个是动态加载的InnoDB版本,官方称为InnoDB Plugin,可以将他视为InnoDB 1.0x 版本。后来的MySQL5.6版本中InnoDB升级为1.2版本
  • 下图中显示了各个版本中InnoDB存储引擎的功能。

二、InnoDB体系结构

  • 如下图:显示了InnoDB存储引擎的整体体系架构,从图可见,InnoDB存储引擎有多个内存块,可以认为是这些内存块组成了一个大的内存池,负责如下工作:
    • 维护了所有进程/线程需要访问的多个内部数据结构。
      -缓存磁盘上的数据,方便快速地读取,同时在对磁盘文件的数据修改之前在这里缓存。
      重做日志(redo log)缓冲。

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

2.1 后台线程

  • InnoDB存储引擎是多线程的模型,因此其后台有多个不同的后台线程,负责处理不同的任务。
Master Thread
  • Master Thread是一个非常核心的后台线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新、合并插入缓冲(INSERT BUFFER)、undo页的回收等。本文下节会详细介绍各个版本中Master Thread的工作方式。
IO Thread
  • 在InnoDB存储引擎中大量使用了AIO(Async IO)来处理IOC请求,这样可以极大提高数据库的性能。而IO Thread的工作主要是负责这些IO请求的回调(call back)处理。
    • InnoDB1.0 版本之前共有4个IO Thread,分别是writereadinsert bufferlog IO thread。在Lunix平台下,IO Thread的数量不能进行调整,但是在Windows平台下可以通过参数innodb_file_io_therads来增大IO Tread。
    • 从InnoDB 1.0x 版本开始,read threadwrite thread 分别增大到了4个,并且不再使用 innodb_file_io_threads参数。而分别使用inno_read_io_theradsinnodb_wirite_io_threads参数进行设置,如:


  • 可以通过命令SHOW ENGINE INNODB STATUS 来观察InnoDB中的IO Thread:

  • 可以看到IO Thread 0为 insert buffer thread 。IO Thread 1为 log thread。 之后就是根据参数 innodb_read_io_threads及innodb_write_io_threads来设置的读写线程,并且读线程的id总是小于写线程。
Purge Thread
  • 事务被提交后,其所使用的的undolog可能不再被需要,因此需要Purge Thread来回收已经使用并分配的undo页。InnoDB 1.1版本之前,purge操作需要在InnoDB存储引擎的Master Thread中完成。
  • 从InnoDB 1.1 版本开始,purge操作可以独立到单独的线程中进行,以此来减轻Master Thread的工作,从而提高CPU的使用率以及提升存储引擎的性能。
  • 用户可以在MySQL 数据库的配置文件中添加如下命令来启用独立的Purge Thread

2.2 内存

缓冲池
  • InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可将其视为基础磁盘的数据库系统(Disk-base Database)。在数据库系统中,由于CPU速度与磁盘速度之间的鸿沟,基于磁盘的数据库系统通常使用缓冲池技术来提高数据库的整体性能。
  • 缓冲池简单来说就是一块内存区域,通过内存的速度来弥补磁盘速度较慢对数据库性能的影响。在数据库中进行读取页的操作,首先从磁盘读到的页存放在缓冲池中,这个过程称为将页“FIX”在缓存池中。下一次再读取相同的页时,首先判断该页是否在缓冲池中。若在缓冲池中,称该页在缓冲池中被命中,直接读取该页。否则,读取磁盘上的页
  • 对于数据库中页的修改操作,则首先修改在缓冲池中的页然后在以一定的频率刷新到磁盘上。这里需要注意的是,页从缓冲池刷新回磁盘的操作并不是在每次页发生更新时触发,而是通过一种称为Checkpoint的机制刷新回磁盘。同样,这也是为了提高数据库的整体性能。
  • 综上所述缓冲池的大小直接影响着数据库的整体性能。对于InnoDB存储引擎而言,其缓冲池的配置通过参数innodb_buffer_pool_size来设置。下面显示一台MySQL数据库服务器,其将InnoDB存储引擎的缓冲池设置为15GB.
  • 具体来看,缓冲池中缓存的数据页类型有: 索引页数据页undo页插入缓冲(insert buffer)自适应哈希索引(adaptive hash index)InnoDB存储的锁信息(lock info)数据字典信息(data dictionnary)等。不能简单地认为,缓冲池只是缓存索引页和数据也,他们只是占缓冲池很大的一部分而已。下图很好的显示了InnoDB存储引擎中内存的结构情况。
  • 从InnoDB1.0x版本开始,允许有多个缓冲池实例。每个页根据哈希值平均分配到不同缓冲池实例中。这样做的好处是减少数据库内部的资源竞争,增加数据库的并发处理能力。可以通过参数innodb_buffer_pool_instantces来进行配置,该值默认为1。
  • 在配置文件中innodb_buffer_pool_instantces设置为大于1的值就可以得到多个缓冲池实例。再通过命令SHOW ENGINE INNODB STATUS 可以观察到如下的内容:

  • 这里参数innodb_buffer_pool_instances设置为2,即数据库用户拥有两个缓冲池实例。通过命令SHOW ENGINE INNODB STATUS 可以观察到每个缓冲池实例对象运行的状态,并且通过类似–-BUFFER POOL 0的注释来表明是哪个缓冲池实例。
  • 从MySQL5.6版本开始,还可以通过information_schema架构下的表INNODB_BUFFER_POOL_STATUS来观察缓冲的状态,如运行下列命令可以看到各个缓冲池的使用状态:
LRU List、Free List 和 Flush List
  • 前一小节我们知道了缓冲池是一个很大的内存区域,其中存放各种类型的页。那么InnoDB存储引擎是怎么对这么大的内存区域进行管理的? 这是本节要告诉大家的。
  • 通常来说,数据库的缓冲池是通过LRU(Latest Recent Used,最近最少使用)算法来进行管理的。即最频繁使用的页在LRU列表的前端,而最少使用的页在LRU列表的尾端。当缓冲不能存放新读取到的页时,将首先释放LRU列表中尾端的页
  • 在InnoDB存储引擎中,缓冲池中页的大小默认为16KB,同样使用LRU算法对缓冲池进行管理。稍有不同的是,InnoDB列表中还加入了midpoint位置。新读取到的页,虽然是最新访问的页,但并不是直接放入到LRU列表的首页,而是放入到LRU列表的midpoint位置。这个算法在InnoDB存储引擎下称为midpoint insertion strategy。在默认配置下,该位置在LRU列表长度的5/8处。midpoint 位置可由参数innodb_old_blocks_pct控制,如:
  • 从上面的例子可以看到,参数innodb_old_blocks_pct默认值为37,表示新读取的页插入到LRU列表尾端37%的位置。在InnoDB存储引擎中,把midpoint之后的列表称为old列表,之前的列表称为new列表,可以简单理解为new列表的页都是最为活跃的热点数据。
  • 为什么不采用朴素的LRU算法 ?,直接将读取的页放入到LRU列表的首部呢?这是因为若直接将读取到的页放入到LRU的首部,那么某些SQL操作可能会使缓冲池中的页被刷出,从而影响缓冲池的效率。常见的这类操作为索引或数据的扫描操作。这类操作需要访问表中的许多页,甚至是全部的页,而这些页通常来说又仅在这次查询操作中需要,并不是活跃的热点数据。如果页被放入LRU列表的首部,那么非常可能所需要的热点数据页从LRU列表中移除,而在下一次需要读取该页时,InnoDB存储引擎需要再次访问磁盘。
  • 为了解决这个问题,InnoDB存储引擎引入了另一个参数来进一步管理LRU列表,这个参数是innodb_old_blocks_time用于表示页读取到mid位置后需要等待多久才会被加入到LRU列表的热端。因此当需要执行上述所说的SQL操作时,可以通过下面的方法尽可能使LRU列表中热点数据不被刷出。

    - LRU类表用来管理已经读取的页,但当数据库刚启动时,LRU列表是空的,即没有任何的页。这时页都存放在Free列表中,当需要从缓冲池分页时,首先从Free列表中查找是否有可用的空闲页,若有则将该页从Free列表中删除,放入到LRU列表中。否则,根据LRU算法,淘汰LRU列表末尾的页,将该内存空间分配给新的也。当页从LRU列表的old部分加入到new部分时,称此时发生的操作为 page made young, 而因为innodb_old_block_time的设置而导致页没有从old部分移动到new部分的操作称为page not made young。可以通过命令 SHOW ENGINE INNODB STATUS来观察LRU列表及FREE列表的使用情况和运行状态。

  • 通过命令可以看到: 当前Buffer pool size共有327 679个页,即327679*16K,总共5GB的缓冲池。Free Buffers表示当前Free列表中的数量,Database pages表示LRU列表中的页的数量。可能的情况是Free buffers 与 Database pages 的数据之和不等于 Buffer pool size 。正如图片所示的那样,因为缓冲池中的页还可能会被分配给自适应哈希索引、Lock信息、Insert Buffer等页,而这部分页不需要LRU算法进行维护,因此不存在与LRU类表中。
  • pages made young 显示了LRU列表中页移动到前段的次数,因为服务器在运行阶段没有改变innode_old_blocks_time的值,因此not yount 为 0 。
重做日志缓存

  • 图中可以看出,InnoDB存储引擎的内存区域除了有缓冲池外,还有重做日志缓冲(redo log buffer)。InnoDB存储引擎首先将重做日志信息先放入到这个缓冲区,然后按一定的频率将其刷到重做日志文件。重做日志缓冲一般不需要设置的很大,因为一般情况下,每一秒钟会将重做日志缓存冲刷到日志文件,因此用户只需要保证每秒产生的事务量在这个缓冲大小之内即可,该值可有配置参数innodb_log_buffer_size控制,默认为8MB :
  • 通常情况下,8MB的重做日志缓冲池足以满足绝大部分的应用,因为重做日志在下列三种情况下会将重做日志缓冲中的内容刷新到外部磁盘的重做日志文件中。
    • Master Thread每一秒将重做日志缓冲刷新到重做日志文件
    • 每个事务提交时会将重做日志缓冲刷新到重做日志文件
    • 当重做日志缓冲池剩余空间小于1/2时,重做日志缓冲刷新到重做日志文件。
额外的内存池
  • 在InnoDB存储引擎中,对内存的管理是通过一种称为内存堆(heap)的方式进行的。在对一些数据结构本身的内存进行分配时,需要从额外的内存池中进行申请,当该区域的内存不够时,会从缓冲池中进行申请。

2.3 CheckPoint 技术

  • 前面讲到了缓冲池,缓冲池的设计目的是为了协调CPU速度与磁盘速度的鸿沟。因此页的操作首先都是在缓冲池中完成的。如果一条DML语句,如Update或Delete改变了页中的记录,那么此时页时脏的,即缓冲池的页的版本要比磁盘的新。数据库需要将新的版本的页从缓冲池刷新到磁盘。
  • 倘若每一次页发生变化,就将新页的版本刷新到磁盘,那么这个开销是非常大的。若热点数据集中的某几个页中,那么数据看的性能将变得非常差。同时,如果在从缓冲池将页的新版本刷新到磁盘时发生了宕机,那么数据就不能恢复了。为了避免发生数据丢失的问题,当前数据数据库系统普遍采用了Write Ahead Log策略,即事务提交时,先写重做日志,在修改页。当由于发生宕机而导致数据丢失时,通过重做日志来完成数据的恢复。这也是事务ACID 中 D ( Durability 持久性 ) 的要求 。
CheckPoint作用
  • CheckPoint技术的目的是解决一下几个问题
    • 缩短数据库的恢复时间
    • 缓冲池不够用时,将脏页刷新到磁盘
    • 重做日志不可用时,刷新脏页
  • 当数据库发生宕机时,数据库不需要重做所有的日志,因为CheckPoint之前的页都已经刷新回磁盘。故数据库只需对CheckPoint后的重做日志进行恢复。这样缩短了恢复的时间。
  • 此外,当缓冲池不够用时,根据LRU算法会溢出最近最少使用的页,若此页为脏页,那么需要强制实行CheckPoint,将脏页也就是页的新版本刷回磁盘。

三、InnoDB 关键特性

  • InnoDB存储引擎的关键特性包括:
    • 插入缓冲(Insert Buffer)
    • 两次写 (Double Write)
    • 自适应哈希索引(Adaptive Hash Index)
    • 异步 (Async IO)
    • 刷新邻接页 (Flush Neithbor Page)
  • 上述这些特性为InnoDB 存储引擎带来更好的性能以及更高的可靠性。

3.1 插入缓存

Insert Buffer
  • Insert Buffer 可能是InnoDB 存储引擎关键特性中最令人激动与兴奋的一个功能。不过这个名字可能会让人认为插入缓存是缓冲池中一个组成部分。其实不然,InnoDB缓存池中有Insert Buffer信息固然不错,但是Insert Buffer和数据页一样,也是物理页的一个组成部分。
  • 在InnoDB 存储引擎中,主键是行唯一的标识符。通常应用程序中记录的插入顺序是按照主键递增的顺序进行插入的。因此,插入聚集索引(Primary Key)一般是顺序的,不需要磁盘的随机读取。比如按下列SQL定义表:
    • 其中a列时自增长的,若对a列插入NULL值,则由于其具有 AUTO_INCREMENT属性,其值会自动增长。同时页中的行记录按a的值进行顺序存放。在一般情况下,不需要随机读取另一个页中的记录。因此对于这类情况下的插入操作,速度是非常快的。
    • 但是不可能每张表上只有一个聚集索引,更多情况下,一张表上有多个非聚集的辅助索引(secondary index)。比如,用户需要按照b这个字段进行查找,并且b这个字段不是唯一的,即表时按照如下的SQL语句定义的:
  • 在这样的情况下产生了一个非聚集的且不是唯一的索引。在进行插入操作时,数据页的存放还是按照主键a进行顺序存放的,但是对于非聚簇索引叶子节点的插入不再是顺序的了,这时就需要离散地访问非聚簇索引页,由于随机读取的存在而导致插入的性能下降。当然这并不是这个b字段上索引的错误,而是因为B+树的特性决定了非聚簇索引插入的离散性。
  • 需要注意的是,在某些情况下,辅助索引的插入依然是顺序的,或者说比较顺序的,比如用户购买表中的时间字段。在通常情况下,用户购买的时间是一个辅助索引,用来根据时间条件进行查询。但是在插入时却根据时间的递增而插入的,因此插入也是“较为”顺序的。
  • InnoDB存储引擎开创性地设计了Insert Buffer,对于非聚集索引的插入或更新操作,不是每一次直接插入到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,若在,则直接插入若不在,则先放入到一个Insert Buffer对象中,好似欺骗。数据库这个非聚集的索引已经插入到叶子节点,而实际并没有,只是存放在另一个位置,然后再以一定的频率和情况进行Insert Buffer和辅助索引页子节点的merge (合并)操作,这是通常能多个插入合并到一个操作中(因为在一个索引页中),这就大大提高了对于非聚集索引插入的性能
  • 然而Insert Buffer的使用需要同时满足以下两个条件
    • 索引是辅助索引(secondary inde );
    • 索引不是唯一(unique)的。
  • 当满足以上两个条件时,InnoDB存储引擎会使用Insert Buffer,这样就能提高插入操作的性能了。不过考虑这样一种情况: 应用程序进行大量的插入操作,这些都涉及了不唯一的非聚集索引,也就是使用了Insert Buffer。若此时MySQL数据库发生了宕机,这时势必有大量的Insert Buffer并没有合并到实际的非聚集索引中去。 因此这时恢复可能需要很长的时间,在极端情况下甚至需要几个小时。
  • 辅助索引不能是唯一的,因为在插入缓冲时,数据库并不去查找索引页来判断插入的记录的唯一性。如果去查找肯定又会有离散读取的情况发生,从而导致Insert Buffer失去意义。
  • 用户可以通过命令 SHOW ENGINE INNODB STATUS 来查看插入缓冲的信息 :

  • seg size显示了当前Insert Buffer的大小为11136*16KB, 大约为177MB;free list len代表了空闲列表的长度;size代表了已经合并记录页的数量。 而黑体部分的第2行可能是用户真正关系的,因为显示了插入性能的提高。 Inserts代表了插入的记录数;merged recs代表了合并的插入记录数量;merges代表合并的次数,也就是实际读取页的次数**。merges:merges recs**大约为1:3,代表了插入缓冲将对于非聚集索引页的离散 IO 逻辑请求大约降低2/3 。
Insert Buffer的内部实现
  • 通过前面小节,大家应该知道了Insert Buffer的使用场景,即非唯一辅助索引的插入操作。但是对于Insert Buffer具体是什么,以及内部实现可能依然模糊,接下来带着大家来熟悉
  • 其实,Insert Buffer的数据结构时一颗 B+树。 MySQL 4.1 之前的版本中每张表有一颗 Insert Buffer B+ 树,而在现在的版本中,全局只有一颗Insert Buffer B+树,负责对所有的表的辅助索引进行 Insert Buffer。而这颗B + 树存放在共享表空间中,默认也就是ibdatal 中。因此,试图通过独立表空间ibd文件恢复表中数据时,往往会导致CHECK TABLE失败。这是因为表的辅助索引中的数据可能还在Insert Buffer中,也就是共享表空间中,所以通过ibd文件进行恢复后,还需要进行REPAIR TABLE操作来重建表上的所有辅助索引。
    -Insert Buffer是一颗B +树,因此其也由叶节点和非叶节点组成。非叶节点存放的是查询的search key(键值),其构造器如图2-3 所示。
  • search key 一共占有9个字节,其中space表示待插入记录所在表空间id,在InnoDB存储引擎中,每个表有一个唯一的space id,可以通过space id 查询得知是哪张表space 占用4字节marker占用1字节,他是用来兼容老版本的Insert Buffer。offset表示页所在的偏移量,占用4个字节
  • 当一个辅助索引要插入到页(space,offset)时,如果这个页不在缓冲池中,那么InnoDB存储引擎首先根据上述规则构造一个search key,接下来查询Insert Buffer这颗B+ 树,然后在将这条记录插入到 Insert Buffer B+树的叶子节点中。

3.2 两次写

  • 如果说Insert Buffer带给InnoDB存储引擎的是性能上的提升,那么doublewrite(两次写)带给InnoDB存储引擎的是数据页的可靠性
  • 当发生数据库宕机时,可能InnoDB存储引擎正在写入某个页到表中,而这个页只写了一部分,比如16KB的页,只写了前4KB,之后就发生了宕机,这种情况下称为部分写失效(partial page write)。在InnoDB 存储引擎未使用doublewrite技术前,曾经出现过因为部分写失效而导致数据丢失的问题
  • 也许,有经验的大神会说,如果发生写失效,可以通过重做日志进行恢复。这是一个办法。但是必须清楚地认识到,重做日志中记录的是对页的物理操作,如偏移量800,写‘aaa’记录。如果这个页本身已经发生了损坏。再对其进行重做是没有意义的。这也就是说,在应用(apply)重做日志前,用户需要一个页的副本,当写入失效发生时,先通过页的副本来还原该页,在进行重做,这是doublewrite。在InnoDB存储引擎中doublewrite的体系架构如下图所示。
  • doublewrite由两部分组成,一部分是内存中的doublewrite buffer,大小为2MB ,另一部分是物理磁盘上共享表空间中连续的128个页,即2个区(extent),大小同样为2MB 。在对缓冲池的脏页进行刷新时,并不直接写磁盘,而是会通过memcpy函数将脏页先复制到内存中doublewrite buffer,之后通过doublewirite buffer再分两次,每次1MB顺序写入共享表空间的物理磁盘上,然后马上调用fsync喊出,同步磁盘,避免缓冲写带来的问题。在这个过程中,因为double页时连续的,因此这个过程是顺序写的,开销并不是很大。在完成doublewrite页的写入后,再将doublewrite buffer中的页写入各个表空间文件中,此时的写入则是离散的。可以通过以下命令观察到doublewrite运行的情况。
    - 可以看到,doublewrite一共写了6325194个页,但实际的写入次数为100399,基本上符合64:1。

3.3 自适应哈希索引

  • 哈希(hash)是一种非常快的查找方法,在一般情况下这种查找的时间复杂度为O(1) ,即一般仅需要因此查找就能定位数据 。而 B +树的查找次数,取决于B +树的高度,在生产环境中,B+ 树的高度一般为 3~ 4层,故需要3~4次的查询。
  • InnoDB存储引擎会监控对表的各索引页的查询 。如果观察到建立哈希索引可以带来速度提升,则建立哈希索引,称之为自适应哈希索引(Adaptive Hash Index,AHI)。AHI是通过缓冲池的B+树叶构造而来,因此建立的速度很快,而且不需要对整张表构建哈希索引。InnoDB存储引擎会自动根据访问的频率和模式来自动地为某些热点页建立哈希索引
  • AHI有一个要求,即对这个页的连续访问模式必须是一样的。例如对于(a ,b)这样的联合索引页,其访问模式可以是以下情况:
    • WHERE a=xxx
    • WHERE a=xxx and b=xxx
  • 访问模式一样指的是查询的条件一样,若交替进行上述两种查询,那么InnoDB存储引擎不会对该页构造AHI 。此外AHI还有如下的要求
    • 该模式访问了100次
    • 页通过该模式访问了N次,其中N=页中记录* 1/16
  • 根据InnoDB存储引擎官方的文档显示,启用AHI后,读取和写入速度可以提高2倍,辅助索引的连接操作性能可以提高5倍。 毫无疑问,AHI是非常好的优化模式,其设计思想是数据库自优化的(self-tuning),即无需DBA对数据库进行人为调整。
  • 通过命令 SHOW ENGING INNODB STATUS 可以看到当前AHI的使用状态:
  • 现在可以看到AHI 的使用信息了,包括AHI的大小使用情况每秒使用AHI搜索的情况。值得注意的是,哈希索引只能用来搜索等值的查询,如SELECT * FROM table WHERE index_col=‘xxx’。而对于其他查找类型,如范围查找,是不能使用哈希索引的。因此出现了non-hash seraches/s的情况。通过hash searches:non-hash searches可以大概了解使用哈希索引后的效率。
  • 由于AHI 是由 InnoDB存储引擎控制的,因此这里的信息只供用户参考。不过用户可以通过SHOW ENGING INNODB STATUS的结果及参数 innodb_adaptive_hash_inde来考虑是禁用或启动此特性,默认AHI为开启状态。

3.4 异步 IO

  • 为了提高磁盘操作性能,当前的数据库系统都采用异步IO(Asyncchronous IO,AIO)的方式来处理磁盘操作 。InnoDB存储引擎亦是如此
  • 与AIO对应的是Sync IO,即每进行一次IO操作,需要等待此次操作结束才能继续接下来的操作。但是如果用户发出的是一条索引扫描的查询,那么这条SQL查询语句可能需要扫描多个索引页,也就是需要进行多次的 IO 操作。在每扫描一个页并等待其完成后再进行下一次的扫描,这是没有必要的。用户可以再发出一个 IO请求后立即再发出另一个IO 请求,当全部 IO请求发送完毕后,等待所有IO操作的完成,这就是 AIO
  • AIO 的另一个优势可以进行IO Merger操作,也就是多个IO合并成1个IO,这样可以提高IOPS的性能,例如用户需要访问页(space,page_no)为:
    (8,6)(8,7)(8,8)
  • 每个页的大小为16KB,那么同步IO需要进行3次IO操作。而AIO会判断到这三个页是连续的(显然可以通过(space,page_no)得知)。因此AIO底层会发送一个IO请求,从(8,6)开始,读取48KB的页。
  • 若通过Linux操作系统下的iostat命令,可以通过观察rrqm/s和wrqms/s,例如:
  • 在InnoDB1.1x之前,AIO的实现通过InnoDB存储引擎中的代码来模拟实现。而从InnoDB1.1x开始(InnoDB Plugin不支持),提供了内核级别AIO的支持,称为Native AIO 。因此在编译或者运行该版本MySQL时,需要libaio库的支持。若没有则会出现如下的提示:
  • 参数innodb_use_native_aio用来控制是否启用Native AIO,在Linux操作系统下, 默认值为ON:
  • 用户可以通过开启和关闭Native AIO功能来比较Inno DB性能的提升。官方的测试显示,启用Native AIO ,恢复速度可以提高75%
  • 在InnoDB 存储引擎中,read ahead方式的读取都是通过AIO 完成,脏页的刷新,即磁盘的写入操作全部由 AIO完成 。

3.5 刷新邻接页

  • InnoDB 存储引擎还提供了 Flush Neighbor Page(刷新邻接页)的特性。 其工作原理为: 当刷新一个脏页时,InnoDB存储引擎会检测该页所在区(extent)的所有页,如果是脏页,那么一起进行刷新。这样的好处是,通过AIO可以将多个IO写入操作合并为一个IO操作,故该工作机制在传统机械磁盘下有着显著的优势。但是需要考虑以下两个问题:
    • 是不是可以将不怎么脏的页进行写入,而该页之后又会很快变成脏页?
    • 固态银盘有着较高的IOPS,是否还需要这个特性?
  • 为此,InnoDB存储引擎从1.2x版本开始提供了参数 innodb_flush_neighbors用来控制是否启用该特性。对于传统机械硬盘建议启用该特性,则对于固态银盘有着超高IOPS性能的硬盘,则建议将该参数设置为0,即关闭此特性。

MySQL 第一篇: 深入理解 MySQL 索引底层的数据结构

最后,感谢读者的阅读,文章如有错误,评论或私信我,会及时加以改正,大家共同进步

    原文作者:庸人庸
    原文地址: https://blog.csdn.net/weixin_44624375/article/details/114487683
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系管理员进行删除。