mysql技术内幕(二)InnoDB存储引擎介绍

InnoDB存储引擎介绍

本章将详细介绍lnnoDB 存储引擎的体系架构及其不同于其他存储引擎的特性。

2.1 InnoDB 存储引擎概述

2.2 lnnoDB 存储引擎的版本

2.3 lnnoDB 体系架构

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

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

2.3.1 后台线程

 &emsp InnoDB 存储引擎是多线程的模型,因此其后台有多个不同的后台线程,负责处理不同的任务。
1、Master Thread
   Master Thread 主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新、合并插入缓冲(INSERT BUFFER) 、UNDO 页的回收等。
2、IO Thread
  在InnoDB 存储引擎中大拭使用了AIO (Async IO) 来处理写IO 请求,而IO Thread 的工作主要是负责这些IO 请求的回调(call back)处理。InnoDB 1.0 版本之前共有4 个IO Thread, 分别是write 、read 、insert buffer 和logIO thread 。
3、Purge Thread
   事务被提交后,其所使用的undolog 可能不再需要,因此需要PurgeThread 来回收已经使用并分配的undo 页。

2.3.2 内存

1、缓冲池
   lnnoDB 存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。可将其视为基于磁盘的数据库系统 。在数据库系统中,由于CPU速度与磁盘速度之间的鸿沟,基于磁盘的数据库系统通常使用缓冲池技术来提高数据库的整体性能。
   缓冲池简单来说就是一块内存区域,通过内存的速度来弥补磁盘速度较慢对数据库性能的影响。在数据库中进行读取页的操作,首先将从磁盘读到的页存放在缓冲池中,这个过程称为将页"FIX" 在缓冲池中。下一次再读相同的页时,首先判断该页是否在缓冲池中。若在缓冲池中,称该页在缓冲池中被命中,直接读取该页。否则,读取磁盘上的页。
  对于数据库中页的修改操作,则首先修改在缓冲池中的页,然后再以一定的频率刷新到磁盘上。这里需要注意的是,页从缓冲池刷新回磁盘的操作并不是在每次页发生更新时触发,而是通过一种称为Checkpoint 的机制刷新回磁盘。
   具体来看,缓冲池中缓存的数据页类型有:索引页、数据页、undo 页、插入缓冲( insert buffer)、自适应哈希索引(adaptive hash index) 、InnoDB 存储的锁信息(lock info) 、数据字典信息(data dictionary) 等。不能简单地认为,缓冲池只是缓存索引页和数据页,它们只是占缓冲池很大的一部分而已。
在这里插入图片描述
2、LRU List 、Free List 和Flush List
  缓冲池是一个很大的内存区域,其中存放各种类型的页。
  通常来说,数据库中的缓冲池是通过LRU (Latest Recent Used, 最近最少使用)算法来进行管理的。即最频繁使用的页在LRU 列表的前端,而最少使用的页在LRU 列表的尾端。当缓冲池不能存放新读取到的页时,将首先释放LRU 列表中尾端的页。
  InnoDB 存储引擎对传统的LRU 算法做了一些优化、在InnoDB 的存储引擎中, LRU 列表中还加入了midpoint 位置。新读取到的页,虽然是最新访问的页,但并不是直接放入到LRU 列表的首部,而是放入到LRU 列表的midpoint位置。这个算法在lnnoDB 存储引擎下称为midpoint insertion strategy 。参数innodb_old_ blocks _pct 默认值为37, 表示新读取的页插入到LRU 列表尾端的37% 的位置(差不多3/8 的位置)。在InnoDB 存储引擎中,把midpoint 之后的列表称为old 列表,之前的列表称为new 列表。可以简单地理解为new列表中的页都是最为活跃的热点数据。(有些操作需要访问很多页,直接插在首部会把热点数据给挤下去)
  LRU 列表用来管理已经读取的页,但当数据库刚启动时, LRU 列表是空的,即没有任何的页。这时页都存放在Free 列表中。当需要从缓冲池中分页时,首先从Free 列表中查找是否有可用的空闲页,若有则将该页从Free 列表中删除,放人到LRU 列表中。 否则,根据LRU 算法,淘汰LRU 列表末尾的页,将该内存空间分配给新的页。当页从LRU 列表的old 部分加人到new 部分时,称此时发生的操作为page made young。
  在LRU 列表中的页被修改后,称该页为脏页( dirty page) , 即缓冲池中的页和磁盘上的页的数据产生了不一致。这时数据库会通过CHECKPOINT 机制将脏页刷新回磁盘,而Flush 列表中的页即为脏页列表。脏页既存在于LRU 列表中,也存在于Flush 列表中。LRU 列表用来管理缓冲池中页的可用性, Flush 列表用来管理将页刷新回磁盘, 二者互不影响。
3、 重做日志缓冲
  InnoDB 存储引擎首先将重做日志信息先放入到这个缓冲区,然后按一定频率将其刷新到重做日志文件。重做日志缓冲一般不需要设置得很大,因为一般情况下每一秒钟会将重做日志缓冲刷新到日志文件。
三种情况下会将重做日志缓冲中的内容刷新到外部磁盘的重做日志文件中。

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

4、额外的内存池
   InnoDB 存储引擎中,对内存的管理是通过一种称为内存堆Cheap)的方式进行的。在对一些数据结构本身的内存进行分配时,需要从额外的内存池中进行申请,当该区域的内存不够时,会从缓冲池中进行申请。

2.4 Checkpoint 技术

  为了避免事务数据库系统普遍都采用了Write Ahead Log 策略,即当事务提交时,先写重做日志,再修改页。当由于发生岩机而导致数据丢失时,通过重做日志来完成数据的恢复。这也是事务ACID 中D (Durability 持久性)的要求。
Checkpoint (检查点)技术的目的是解决以下几个问题:

  • 缩短数据库的恢复时间;(宕机后数据恢复的时间)
  • 缓冲池不够用时,将脏页刷新到磁盘;
  • 重做日志不可用时,刷新脏页。

  数据库发生右机时,数据库不需要重做所有的日志,因为Checkpoint 之前的页都已经刷新回磁盘。故数据库只需对Checkpoint 后的重做日志进行恢复。这样就大大缩短了恢复的时间。此外,当缓冲池不够用时,根据LRU 算法会溢出最近最少使用的页,若此页为脏页,那么需要强制执行Checkpoint, 将脏页也就是页的新版本刷回磁盘。
  checkpoint 所做的事情无外乎是将缓冲池中的脏页刷回到磁盘。不同之处在于每次刷新多少页到磁盘,每次从哪里取脏页,以及什么时间触发Checkpoint 。在InnoDB 存储引擎内部,有两种Checkpoint, 分别为:

  • Sharp Checkpoint
  • Fuzzy Checkpoint

  Sharp Checkpoint 发生在数据库关闭时将所有的脏页都刷新回磁盘,这是默认的工作方式,即参数innodb_fast_ shutdown = 1 。但是若数据库在运行时也使用Sharp Checkpoint , 那么数据库的可用性就会受到很大的影响。故在InnoDB 存储引擎内部使用Fuzzy Checkpoint 进行页的刷新,即只刷新一部分脏页,而不是刷新所有的脏页回磁盘。

2.5 Master Thread 工作方式

InnoDB 存储引擎的主要工作都是在一个单独的后台线程Master Thread 中完成的。

2.5.1 lnnoDB 1.0.x 版本之前的Master Thread

  Master Thread 具有最高的线程优先级别。其内部由多个循环(loop) 组成:主循环(loop) 、后台循环(backgroup loop) 、刷新循环(flush loop) 、暂停循环(suspend loop) 。

  Loop 被称为主循环,因为大多数的操作是在这个循环中,其中有两大部分的操作-每秒钟的操作和每10 秒的操作。loop 循环通过thread sleep 来实现,这意味着所谓的每秒一次或每10 秒一次的操作是不精确的。在负载很大的情况下可能会有延迟(delay)。
每秒一次的操作包括:

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

接着来看每10 秒的操作,包括如下内容:

  • 刷新100 个脏页到磁盘(可能的情况下);
  • 合并至多5 个插人缓冲(总是);
  • 将日志缓冲刷新到磁盘(总是);
  • 删除无用的Undo 页(总是);
  • 刷新100 个或者IO 个脏页到磁盘(总是)。

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

  • 删除无用的Undo 页(总是);
  • 合并20 个插人缓冲(总是);
  • 跳回到主循环(总是);
  • 不断刷新100 个页直到符合条件(可能,跳转到flush loop 中完成)。

若flush loop 中也没有什么事情可以做了, InnoDB 存储引擎会切换到suspend—loop, 将Master Thread 挂起,等待事件的发生。若用户启用(enable) 了InnoDB 存储引擎,却没有使用任何InnoDB 存储引擎的表,那么Master Thread 总是处千挂起的状态。

2.6 lnnoDB 关键特性

InnoDB 存储引擎的关键特性包括:

  • 插入缓冲(Insert Buffer)
  • 两次写(Double Write)
  • 自适应哈希索引(Adaptive Hash Index )
  • 异步IO (Async IO)
  • 刷新邻接页(Flush Neighbor Page )

2.6.1 插入缓冲

1. Insert Buffer
  是Insert Buffer 和数据页一样,也是物理页的一个组成部分。在InnoDB 存储引擎中,主键是行唯一的标识符。通常应用程序中行记录的插入顺序是按照主键递增的顺序进行插入的。因此,插入聚集索引(Primary Key) 一般是顺序的,不需要磁盘的随机读取。
B+ 树的特性决定了非聚集索引插入的离散性。
  InnoDB 存储引擎开创性地设计了Insert Buffer, 对千非聚集索引的插入或更新操作,不是每一次直接插入到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,若在,则直接插入;若不在,则先放入到一个Insert Buffer 对象中,好似欺骗。数据库这个非聚集的索引已经插到叶子节点,而实际并没有,只是存放在另一个位置。然后再以一定的频率和悄况进行Insert Buffer 和辅助索引页子节点的merge (合并)操作,这时通常能将多个插入合并到一个操作中(因为在一个索引页中),这就大大提高了对于非聚集索引插入的性能。
然而Insert Buffer 的使用需要同时满足以下两个条件:

  • 索引是辅助索引(secondary index);
  • 索引不是唯一( unique) 的。

2. Change Buffer
  可将其视为Insert Buffer 的升级。 lnnoDB 存储引擎可以对DML 操作—-INSERT 、DELETE 、UPDATE都进行缓冲,他们分别是: Insert Buffer 、Delete Buffer 、Purge buffer 。
  当然和之前Insert Buffer 一样, Change Buffer 适用的对象依然是非唯一的辅助索引。
对一条记录进行UPDATE 操作可能分为两个过程:

  • 将记录标记为己删除;
  • 真正将记录删除。

3. Insert Buffer 的内部实现
Insert Buffer 的使用场景,即非唯一辅助索引的插入操作。Insert Buffer 的数据结构是一棵B+ 树。全局只有一棵Insert Buffer B+ 树,负责对所有的表的辅助索引进行Insert Buffer 。而这棵B+ 树存放在共享表空间中,因此其也由叶节点和非叶节点组成。非叶节点存放的是查询的search key (键值)
在这里插入图片描述
search key 一共占用9 个字节,其中space 表示待插入记录所在表的表空间id, 在lnnoDB 存储引擎中, 每个表有一个唯一的space id, 可以通过space id 查询得知是哪张表。space 占用4 字节。marker 占用1 字节,它是用来兼容老版本的Insert Buffer 。offset表示页所在的偏移量,占用4 字节。
  当一个辅助索引要插入到页(space, offset) 时,如果这个页不在缓冲池中,那么lnnoDB 存储引擎首先根据上述规则构造一个search key, 接下来查询Insert Buffer 这棵B+ 树,然后再将这条记录插人到Insert Buffer B+ 树的叶子节点中。
  对于插入到Insert Buffer B+ 树叶子节点的记录(如图2-4 所示),并不是直接将待插入的记录插入,而是需要根据如下的规则进行构造:
在这里插入图片描述
metadata 占用4 字节,里面有个值用来排序每个记录进入Insert Buffer 的顺序。从Insert Buffer 叶子节点的第5 列开始,就是实际插入记录的各个字段了。因此较之原插入记录, Insert Buffer B+ 树的叶子节点记录需要额外13 字节的开销。
  因为启用Insert Buffer 索引后,辅助索引页C space, page_ no) 中的记录可能被插入到Insert Buffer B+ 树中,所以为了保证每次Merge Insert Buffer 页必须成功,还需要有一个特殊的页用来标记每个辅助索引页C space, page_ no) 的可用空间。这个页的类型为Insert Buffer Bitmap 。
  每个Insert Buffer Bitmap 页用来追踪16384 个辅助索引页,也就是256 个区(Extent) 。每个Insert Buffer Bitmap 页都在16384 个页的第二个页中。

4.Merge Insert Buffer
概括地说, Merge Insert Buffer 的操作可能发生在以下几种情况下:

  • 辅助索引页被读取到缓冲池时:
  • Insert Buffer Bitmap 页追踪到该辅助索引页已无可用空间时;
  • Master Thread 。

  第一种情况为当辅助索引页被读取到缓冲池中时,例如这在执行正常的SELECT 查询操作,这时需要检查Insert Buffer Bitmap 页,然后确认该辅助索引页是否有记录存放于Insert Buffer B+ 树中。若有,则将Insert Buffer B+ 树中该页的记录插入到该辅助索引页中。可以看到对该页多次的记录操作通过一次操作合并到了原有的辅助索引页中,因此性能会有大幅提高。
  第二种情况Insert Buffer Bitmap 页用来追踪每个辅助索引页的可用空间,并至少有1/32 页的空
间。若插入辅助索引记录时检测到插人记录后可用空间会小于1/32 页,则会强制进行一
个合并操作,即强制读取辅助索引页,将Insert Buffer B+ 树中该页的记录及待插入的记
录插人到辅助索引页中。
  还有一种情况在Master Thread 线程中每秒或每10 秒会进行一次Merge Insert Buffer 的操作,不同之处在千每次进行merge 操作的页的数量不同。

2.6.2 两次写
doublewrite (两次写)带给InnoDB 存储引擎的是数据页的可靠性。
  当发生数据库宒机时,可能lnnoDB 存储引擎正在写人某个页到表中,而这个页只写了一部分,比如16KB 的页,只写了前4KB, 之后就发生了岩机,这种情况被称为部分写失效(partial page write) 。
  doublewrite 由两部分组成, 一部分是内存中的doublewrite buffer, 大小为2MB, 另一部分是物理磁盘上共享表空间中连续的128 个页,即2 个区(extent), 大小同样为2MB。在对缓冲池的脏页进行刷新时,并不直接写磁盘,而是会通过memcpy 函数将脏页先复制到内存中的doublewrite buffer, 之后通过doublewrite buffer 再分两次,每次1MB 顺序地写入共享表空间的物理磁盘上,然后马上调用fsync 函数,同步磁盘,避免缓冲写带来的问题。在这个过程中,因为doublewrite 页是连续的,因此这个过程是顺序写的,开销并不是很大。在完成doublewrite 页的写入后,再将doublewrite buffer 中的页写入各个表空间文件中,此时的写入则是离散的。
在这里插入图片描述
2.6.3 自适应哈希索引
  InnoDB 存储引擎会监控对表上各索引页的查询。如果观察到建立哈希索引可以带来速度提升,则建立哈希索引,称之为自适应哈希索引,它是通过缓冲池的B+ 树页构造而来,因此建立的速度很快,而且不需要对整张表构建哈希索引。InnoDB 存储引擎会自动根据访问的频率和模式来自动地为某些热点页建立哈希索引。

2.6.4 异步IO
  当前的数据库系统都采用异步IO (Asynchronous IO,AIO) 的方式来处理磁盘操作。lnnoDB 存储引擎亦是如此。
  与AIO 对应的是Sync 10, 即每进行一次IO 操作,需要等待此次操作结束才能继续接下来的操作。但是如果用户发出的是一条索引扫描的查询,那么这条SQL 查询语句可能需要扫描多个索引页,也就是需要进行多次的IO 操作。在每扫描一个页并等待其完成后再进行下一次的扫描,这是没有必要的。用户可以在发出一个IO 请求后立即再发出另一个IO 请求,当全部IO 请求发送完毕后,等待所有IO 操作的完成,这就是AIO 。AIO 的另一个优势是可以进行IO Merge 操作(一个space下的多个连续页合成一个IO请求)。

2.6.5 刷新邻接页
  InnoDB 存储引擎还提供了Flush Neighbor Page (刷新邻接页)的特性。其工作原理为:当刷新一个脏页时, InnoDB 存储引擎会检测该页所在区(extent) 的所有页,如果是脏页,那么一起进行刷新。这样做的好处显而易见,通过AIO 可以将多个IO 写人操作合并为一个IO 操作,故该工作机制在传统机械磁盘下有着显著的优势。但是需要考虑到下面两个问题:

  • 是不是可能将不怎么脏的页进行了写入,而该页之后又会很快变成脏页?
  • 固态硬盘有着较高的IOPS, 是否还需要这个特性?

2.7 启动、关闭与恢复

  InnoDB 是MySQL 数据库的存储引擎之一,因此InnoDB 存储引擎的启动和关闭,更准确的是指在MySQL 实例的启动过程中对lnnoDB 存储引擎的处理过程。

2.8 小结

  本章对InnoDB 存储引擎及其体系结构进行了概述,先给出了InnoDB 存储引擎的历史、InnoDB 存储引擎的体系结构(包括后台线程和内存结构);之后又详细介绍了InnoDB 存储引擎的关键特性,这些特性使InnoDB 存储引擎变得更具“魅力”;最后介绍了启动和关闭MySQL 时一些配置文件参数对InnoDB 存储引擎的影响。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江北望江南

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值