InnoDB中必须要了解的几个关键特性

这篇具有很好参考价值的文章主要介绍了InnoDB中必须要了解的几个关键特性。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

InnoDB引擎在设计中使用了很多技术思想。下面我们主要介绍一些InnoDB的关键特性,帮助你去更好了解 InnoDB。

1.预读

预读,我们从字面就可以知道是指:预先读取一些信息。为什么要预读?判断哪些数据可以提前读取到内存中,从而减少IO次数。我们知道硬盘、内存的IO速度差距极大,在内存充裕的条件下,可以极大减少IO时间。但是如果内存不足,预读的数据还没有被真正读取,就被淘汰,那就做了无用功。所以要正确认识预读
认识预读前,先了解 InnoDB数据的逻辑存储单元——表空间、段、区和页。
InnoDB中必须要了解的几个关键特性
上图可以看到这样的层级关系:表空间 -> 段 -> 区(64个页)-> 页(默认16KB)

(1)预读的两种算法

预读请求是指 预取缓冲池中的多个页面的异步IO请求,以预测这些页面即将出现的需求。请求在一个区段中引入所有页面。InnoDB使用两种预读算法来提高IO性能:

  • 线性预读(Line read-ahead):一种基于 按顺序访问的缓冲池中的页面来预测可能很快需要哪些页面的技术。通过配置参数innodb_read_ahead_threshold,触发异步读请求所需的顺序页访问次数,来控制InnoDB执行预读操作的时间。在此之前,InnoDB只会在读取当前extent的最后一页时,计算是否对整个下一个extent发出异步预取请求。
    例如,如果将该值设置为48,则只有在当前区段中有48个页面被连续访问时,InnoDB才会触发线性预读请求。
  • Random read-ahead(随机预读)是一种技术,可以根据缓冲池中已经存在的页面预测何时可能需要页面,而不管这些页面的读取顺序如何。如果在缓冲池中发现同一个区段的13个连续页面,InnoDB会异步发出一个请求来预取该区段的剩余页面。通过配置变量innodb_random_read_ahead来控制随机读的。

(2)在InnoDB中相关配置

对于预读参数有两个参数:

mysql> show variables like '%read_ahead%';
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| innodb_random_read_ahead    | OFF   |
| innodb_read_ahead_threshold | 56    |
+-----------------------------+-------+

innodb_random_read_ahead:开启随机预读技术,优化InnoDB I/O。默认关闭
innodb_read_ahead_threshold:控制InnoDB用于将页面预取到缓冲池中的线性预读的灵敏度

2.插入缓冲

2.1 Insert Buffer

先谈谈我们为什么要引入 Insert Buffer ?

在InnoDB引擎中,无论我们是否自定义主键(如果我们不定义唯一索引,引擎会帮我们建立隐藏的主键列)。因为InnoDB引擎必须要使用主键来构建 聚集索引。一般的主键都是自增的,这样在插入时是顺序插入的。并且聚集索引页也是顺序的,不容易造成频繁的页分裂。

但是我们一般不仅仅使用聚集索引,通常我们会设置非聚集索引不唯一的索引。那么在进行插入时,数据还是按主键来进行顺序存放的。但是对于非聚集索引页叶子节点的插入就不是顺序的了。这时就需要离散地访问非聚集索引页。由于随机写,就会导致插入操作性能下降。这是B+树的特性决定了非聚集索引插入的离散性。

提醒:也不是所有非聚集索引都是写都是完全离散或者说无序的。在某些情况下,非聚集索引的插入依然有序或者比较有序。例如:记录创建时间字段作为 索引列时,插入顺序是根据插入时间。此时插入就是 顺序写了

那么我们如何解决非聚集索引插入时的随机写。InnoDB引擎就开创性的设计了Insert Buffer,对于非聚集索引的插入或者更新操作,不是每次直接插入索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,若在,则直接插入。如果不在,则先放入一个 Insert Buffer 对象中,然后告诉 数据库已经将数据插入到叶子结点中(实际并没有)。选择放在 Insert Buffer中暂存,然后再以一定频率和情况进行 Insert Buffer 和 非聚集索引页子节点的 合并操作。这是通常可以使得多个插入 合并称一个操作。因为可能存在不少插入操作,其实操作的是同一个索引页中。这就大大提高了对于非聚集索引的插入性能。

但是并不是所有索引都可以使用 Insert Buffer,需要满足以下条件:

  • 索引是非聚集索引
  • 索引列不是唯一的

当满足以上两个条件时,InnoDB存储引擎会使用 Insert Buffer。第一个条件显而易见,聚集索引不需要使用Insert Buffer。那为什么 索引列值不能限定它是唯一的呢?因为在插入缓冲时,如果需要去查询索引页来判断插入的记录为唯一性。那么就会造成随机读索引页,也就失去设计 Insert Buffer 的必要了。

需要注意到:

  • 当进行大量插入操作(涉及到非聚集索引)后,如果此时数据库发生宕机,这时势必有大量的 Insert Buffer 并没有合并到实际的非聚集索引中去。因此此时进行恢复,可能会需要很长时间。
  • 在写密集的情况下,插入缓冲会占用过多缓存池内存(innodb_buffer_pool),默认最大可以占用到1/2的缓冲池内存。这对于其他的操作可能会产生一些影响。

2.2 Change Buffer

InnoDB从1.0.x版本后开始引入了 Change Buffer,可以看作 Insert Buffer的升级。从此,InnoDB存储引擎可以对 DML操作——INSERT、DELETE、UPDATE都进行缓冲,他们分别是:Insert Buffer、Delete Buffer、Purge Buffer。和之前的Insert Buffer一样,Change Buffer只适用于非唯一的非聚集索引。

2.3 Insert Buffer的内部实现

Insert Buffer 的数据结构是一颗B+树。在MySQL4.1之前,每张表都有一颗 Insert Buffer B+ 树。而现在的版本中,全局只有一颗 Insert Buffer B+树,负责对所有的表的非聚集索引进行 Insert Buffer。这颗树存放在共享表空间中,默认也就是 ibdata1中。因此,试图通过独立表空间ibd文件恢复表中数据时,往往会导致 CHECK TABLE失败。这是因为表的非聚集索引中的数据可能还在 Insert Buffer,也就是共享表空间中,所以通过 ibd 文件进行恢复后,还需要 REPAIR TABLE 操作来重建表上所有的非聚集索引。

Insert Buffer 是一颗B+树,因此其也是由叶子结点和非叶子结点组成。非叶子结点存放的是查询的 search key。
InnoDB中必须要了解的几个关键特性
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+树的叶子节点中。
InnoDB中必须要了解的几个关键特性
对于插入到叶子结点的记录,并不是直接把待插入的记录插入,而是需要根据一定规则构造。结构如上图。
space、marker、offset字段和之前的非叶子节点中的含义一样,一共占用9个字节。第四个字段 metadata 占用4字节,由三部分组成:

  • IBUF_REC_OFFSET_COUNT:
    占用2个字节,用来排序每个记录进入 Insert Buffer 的顺序。因为 InnoDB开始支持 ChangeBuffer,所以这个值同样被引入到 Insert Buffer中。通过这个字段来标记操作顺序,保证通过顺序回放能得到记录的正确值。
  • IBUF_REC_OFFSET_TYPE:操作类型
  • IBUF_REC_OFFSET_FLAGS:标志位,当前只有 IBUF_REC_COMPACT。

从 Insert Buffer 叶子节点的第5列开始,就是实际插入记录的各个字段了。因此较原插入记录,Insert Buffer B+树的叶子节点记录额外需要 9+4 字节的开销。

因为启用 Insert Buffer后,原先插入非聚集索引页中的记录可能会被插入到 Insert Buffer B+树中。所以我们需要保证每次 Merge Insert Buffer页必须成功。由此需要引入 Insert Buffer Bitmap,它是一个特殊的页,用来标记每个非聚集索引页的可用空间。这个页的类型是 Insert Buffer Bitmap。

Insert Buffer Bitmap 中 每个非聚集索引页占用4位(bit):

  • IBUF_BITMAP_FREE:占用2bit,枚举值:0-表示无可用剩余空间;1-表示剩余空间大于1/32页;2-表示剩余空间大于1/16页;3-表示剩余空间大于1/8页;
  • IBUF_BITMAP_BUFFERED:占用1bit,为1时表示该非聚集索引页有记录被缓存在 Insert Buffer B+树中
  • IBUF_BITMAP_IBUF:占用1bit,为1时表示该页为 Insert Buffer B+树的索引页

因此 每个 Insert Buffer Bitmap 页可以用来追踪16384个非聚集索引页,也就是256个区。每个 Insert Buffer Bitmap 页都在16384个页的第二个页中。

Merge Insert Buffer

前面说到的 Insert Buffer、Change Buffer 是一颗B+树。若需要实现插入记录的非聚集索引不在缓冲池中,那么需要将非聚集索引记录首先写到这颗B+树汇总,但是 Insert Buffer 中的记录什么时候 merge 到真正的 非聚集索引中呢?
可以概括,Merge Insert Buffer 的操作一般发生在以下几种时机:

  • 非聚集索引页被读取到缓冲池时
    当执行正常的 SELECT 查询操作时,需要检查 Insert Buffer Bitmap页,然后确认该辅助索引页是否有记录存放于 Insert Buffer B+树中。若有,则将 Insert Buffer B+树中该页的记录插入到该非聚集索引页中。可以看到对该页多次的记录操作通过一次操作合并到了原有的非聚集索引页中,因此性能有大幅提高。

  • Insert Buffer Bitmap 页追踪到该辅助索引页中已无可用空间时
    上面也提到过 Insert Buffer Bitmap 页是用来追踪每个辅助索引页的可用空间。若插入非聚集索引记录时检测到插入记录后可用空间会小于1/32页,则会强制进行一个合并操作。将 Insert Buffer B+树中该页的记录及待插入的记录一起插入到 非聚集索引页中。

  • Master Thread
    在 Master Thread 线程中每秒或每10秒回进行一次 Merge Insert Buffer 的操作,不同之处在于每次进行merge操作的页的数量不同。它是随机地选择 Insert Buffer B+树的一个页,读取该页中的space及之后所需要数量的页。

3.二次写(double write)

Insert Buffer 带给了 InnoDB存储引擎的是性能的提升,二次写带给 InnoDB 的是数据页的可靠性

InnoDB中页的默认是16KB,而Linux中页的大小是4KB。所以当数据库宕机时,如果InnoDB正在写入某个页到表中,那么这个页可能只写了一部分。这种情况叫做 部分写失效。这种失效,不能通过重做日志解决。因为重做日志中记录的是对原有页的物理操作,它是基于原来的页的,如果页数据已经损坏,重做也是无意义的。

所以我们需要一个副本,当发生写入失效的时候,通过页的副本来还原该页,再进行重做。这就是 double write。

double write 有两部分组成,一部分是处于内存中的 double write buffer,大小为2MB,另外一个部分是处于物理磁盘上共享表空间中连续的2个区(128个页),大小也为2MB。对缓冲池中的脏页进行刷新,并不是直接写磁盘,而是会通过memcpy函数将脏页数据先复制到内存中的 double write buffer中,之后通过 double write buffer 分两次,每次1MB顺序地写入到 共享空间的物理磁盘上,然后马上调用 fsync函数,同步到磁盘。

因为写入到 磁盘中的doulewrite页是连续的,所以这个过程是顺序写,速度很快。在完成 double write 页的写入后,再从 内存中 double write buffer中将脏页写入到 各脏页所在的表空间文件中。因为脏页之间不是连续的,是各个表空间的脏页,所以此时写是离散写,速度很慢。

通过参数 skip_innodb_doublewrite 可以禁止使用 二次写功能,提升较快速度,但是可能会发生写失效问题。

有些文件系统本身提供了部分写失效的防范机制,如ZFS文件系统。那么此时则没有必要开启 二次写 功能。

4.自适应哈希索引

哈希(hash)是一种非常快的查找方法,在一般情况下这种查找的时间复杂度O(1),即一般仅需要一次查找就能定位数据。而B+树的查找次数,取决了B+树的高度,一般B+树的高度为3~4层,即3-4次IO。

InnoDB 存储引擎会监控对表上各索引页的查询。如果观察到建立哈希索引可以带来速度提升,则建立哈希索引,称之为自适应哈希索引(Adaptive Hash Index,AHI)。AHI是通过缓冲池的B+树页构建而来,因此建立速度很快,而且不需要对整张表构建哈希索引。InnoDB存储引擎会自动根据访问的频率和模式来自动地为某些热点页建立哈希索引

AHI 是有要求的,只有页的连续访问模式是一样的,才会对该页构建AHI。例如:对联合索引页(a,b),其访问模式可以是:

  • where a = xxx
  • where a = xxx and b = yyy
    访问模式一样指的是查询的条件一样,若交替的执行上述另种查询,那么InnoDB不会对该页进行构建。除此之外还有如下要求:
  • 以该模式访问了至少100次
  • 页通过该模式访问了至少N次,其中N=页中记录* 1/16

官方文档中说过,启动AHI后,读取和写入速度可以提高2倍,辅助索引的连接操作性能可以提高5倍。AHI是很好的数据库自优化的模式。我们可以自己通过命令 SHOW ENGINE INNODB STATUS可以看到当前AHI的使用状况。

注意:哈希索引只适用于等值查询

5.异步IO

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

AIO的另外一个优势就是可以进行 IO Merge操作,也就是将多个IO合并成1个IO,这样可以提高 IOPS 的性能。例如用户需要访问页的(space,page_no)为:(8,6)、(8,7)、(8,8)。每个页的大小为16KB,那么同步IO需要进行3次IO操作。而AIO会判断到这三个页是连续的。因此AIO底层只会发送一个IO请求,从(8,6)开始读取48KB的页。

InnoDB1.1.x之前,AIO的实现是通过InnoDB存储引擎中的代码来模拟实现的。而从InnoDB1.1.x开始,提供了内核级别的AIO支持,称为Native AIO。

通过参数 innodb_use_native_aio控制是否启用 Native AIO,在Linux操作系统下,默认为ON。官方测试,启用 Native IO,速度可以提升75%。

InnoDB中,read ahead 方式的读取是由AIO完成,脏页的写入等磁盘写入操作也是由AIO完成。

6.刷新邻接页

InnoDB 存储引擎提供 刷新邻接页(Flush Neighbor Page)的特性。其工作原理为:当刷新一个脏页时,InnoDB存储引擎会检测该页所在区(extent)的所有页,如果是脏页,那么一起进行刷新。这样做的好处就是,通过AIO可以将多个IO写入操作合并成一个IO操作,该工作机制在传统机械硬盘下有显著的优势。但是机制会引出下面2个问题:

  • 是否会把不怎么脏的页 进行了写入,而该页之后又很快变成脏页?
  • 固态硬盘有很高的 IOPS,那么是否还需要这个机制?

使用参数 innodb_flush_neighbors控制是否开启

对于机械硬盘建议使用该特性文章来源地址https://www.toymoban.com/news/detail-427038.html

到了这里,关于InnoDB中必须要了解的几个关键特性的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Docker学习之前需要了解的几个概念

    1.1. 命名空间(Namespaces) 命名空间是Linux内核提供的资源隔离机制,它通过创建多个独立的 全局视图 ,使得在不同命名空间中的进程可以拥有各自独立且互不干扰的系统资源。每个命名空间提供了一种逻辑上的隔离效果,让进程看到的是一个受限或定制化的环境,而非整个

    2024年01月23日
    浏览(56)
  • “深入理解SpringBoot:从入门到精通的几个关键要点“

    标题:深入理解Spring Boot:从入门到精通 摘要:本文将深入探讨Spring Boot的关键要点,帮助读者从入门到精通。我们将从Spring Boot的基本概念开始,介绍自动配置、起步依赖、注解驱动开发等特性,并通过示例代码演示如何使用Spring Boot构建一个简单的Web应用程序。 正文: S

    2024年02月14日
    浏览(36)
  • Python3.12 新版本之f-string的几个新特性

    目录 概述  f-string表达式 1. 支持转义字符 2. 支持多行字符串 3. 支持重复使用的引号种类 4. 支持无限嵌套 附录 Python3.12新语法特性 Python 3.12在10月2日发布,新版本包含一系列对语言和标准库的改变。 库的改变主要集中在清理已弃用的 API、可用性和正确性等方面。 值得注意

    2024年02月04日
    浏览(30)
  • 去新加坡旅游,你必须要收藏了解的当地电商欺诈风险!

    2月9日,除夕,中国与新加坡免签正式生效。免签政策简化了持普通护照中国游客入境新加坡的程序,使通关更为便捷。根据协定,双方持普通护照人员可免签入境对方国家从事旅游、探亲、商务等私人事务,停留不超过30日,为两国旅客往来提供了更多便利。 据上海机场边

    2024年02月22日
    浏览(52)
  • 【Entity Framework】你必须要了解EF中数据查询之数据加载

    Entity Framework Core 允许在模型中使用导航属性来加载关联实体。有三种常见的O/RM模式可用于加载关联数据。 预先加载表示从数据库中加载关联数据,作为初始查询的一部分; 显示加载表示稍后从数据库中显示加载关联数据; 延迟加载表示在访问导航属性时,从数据库中以透

    2024年04月17日
    浏览(91)
  • InnoDB有哪些特性

    事务支持:InnoDB支持ACID(原子性、一致性、隔离性和持久性)事务,可以保证数据的完整性和一致性。它使用多版本并发控制(MVCC)来实现事务的隔离性,支持读已提交和可重复读两种隔离级别。 行级锁定:InnoDB使用行级锁定来实现并发控制,可以提高并发性能。它支持多

    2024年02月14日
    浏览(35)
  • 8.0 新特性 - innodb_ddl_threads

    MySQL 8.0.27 引入了一个新变量来控制 InnoDB 可用于创建(排序和构建)二级索引的最大并行线程数: innodb_ddl_threads 通过调整该参数,可以提升二级索引的创建速度。 1. innodb_ddl_threads 创建二级索引时,在排序和构建阶段,使用线程的个数,一定程度上可以加快索引的创建速度,

    2024年02月08日
    浏览(36)
  • 【MySQL】 深入了解InnoDB存储引擎的限制

    目录 列数限制 索引数限制 InnoDB的行格式和索引限制 示例和注意事项 **页大小对索引键前缀长度的影响 **对全列索引键的限制 多列索引限制 行大小限制 InnoDB log限制 表空间大小限制 表数量限制 操作系统限制 文件大小和日志文件大小 文件层级限制 随着数据库技术的不断发

    2024年01月24日
    浏览(50)
  • 一文带你了解MySQL之InnoDB表空间

    前言 通过前边的内容,相信大家都知道了表空间是一个抽象的概念,对于系统表空间来说,对应着文件系统中 一个或多个 实际文件;对于每个独立表空间来说,对应着文件系统中 一个 名为表名 .ibd 的实际文件。 大家可以把表空间想象成被切分为许多个页的池子,当我们想

    2024年02月08日
    浏览(37)
  • IO的几个模型

    说到I/O模型,都会牵扯到同步、异步、阻塞、非阻塞这几个词,以下讲解这几个词的概念。 阻塞和非阻塞 阻塞和非阻塞指的是一直等还是可以去做其他事。 阻塞(一直等水烧开)(blocking): 调用结果返回之前,调用者被挂起(当前线程进入非可执行状态,在这个状态,CPU不

    2024年02月12日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包