当前位置: 首页 > news >正文

InnoDB详解2

InnoDB详解2

  • 一.行结构
    • 1.结构图
    • 2.InnoDB支持的数据行格式
      • 1)查看当前数据库或表的行格式
      • 2)指定行格式
      • 3)DYNAMIC 格式的组成
    • 3.数据区存储真实数据方式
    • 4.行的额外(管理)信息区
    • 5.头信息区域
      • 1)删除一行记录时在InnoDB内部执行的操作
    • 6.Null列表和其在列表的值
    • 7.变长字段的作用和其列表的值
      • 1)记录变长字段的长度
      • 2)读取长度时处理粘包问题
    • 小结
    • 8.其他的行格式与DYNAMIC的区别
      • 1)REDUNDANT 冗余格式
      • 2)COMPRESSED 压缩格式
      • 3)COMPACT 紧凑格式
  • 二.InnoDB 内存结构
    • 1.InnoDB存储引擎中内存结构的主要组成部分
    • 2.需要内存结构的原因
    • 3.缓冲池-Buffer Pool
      • 1)缓冲池的作⽤
      • 2)缓冲池是组成数据的方式
        • (1)缓冲池的结构
        • (2)缓冲池中页与页之间是建立连接的方式
        • (3)内存中的数据页与磁盘上的数据页的关系
        • (4)设置Buffer Pool的大小
        • (5)Buffer Pool中Instances的数量如何确定
        • (6)Chunk的作用
        • (7)Instances中Chunk的数量的确定
        • (8)控制块与Page的初始化的过程
        • (9)通过缓冲池配置来提升性能
      • 3)缓冲池中页的管理
        • (1)内存中有这么多数据页如何快速找到目标页?
        • (2)缓冲池中的数据放不下了怎么办?
      • 4)缓冲池采用哪种淘汰策略?是如何实现的?
        • (1)为什么要把页插⼊到中间而不是直接插入到新子列表的头部?
      • 5)怎么查看当前缓冲池的信息?
    • 4.变更缓冲区 - Change Buffer
      • 1)变更缓冲区的作用?
        • (1)为什么是⼆级索引?
        • (2)Merge的触发时机有哪些?
      • 2)变更缓冲区的主要配置项都有哪些?
      • 3)怎么查看当前变更缓冲区的信息?
    • 5.⾃适应哈希索引
      • 1)⾃适应哈希索引的作⽤?
        • (1)为什么要创建自适应哈希索引?
        • (2)自适应哈希索引的Key - Value如何设置 ?
        • (3) 自适应哈希索引在保存在哪⾥?
      • 2)关于⾃适应哈希索引有哪些配置项?
      • 3) 怎么查看⾃适应哈希索引的信息?
    • 6 ⽇志缓冲区
      • 1) ⽇志缓冲区的作⽤?
      • 2)⽇志不通过Log Buffer直接写⼊磁盘不⾏吗?
  • 三.InnoDB 磁盘文件
    • 1.InnoDB存储引擎包含哪些磁盘⽂件?
      • 1)什么是表空间?
    • 2.系统表空间 - System Tablespace
      • 1)系统表空间的作⽤
      • 2)系统表空间⽂件保存在哪⾥?
      • 3)系统表空间都有哪些可以配置的选项
      • 4)修改系统表空间配置后什么时候⽣效?
    • 3. 独⽴表空间 - File-Per-Table Tablespace
      • 1)独⽴表空间的作⽤?
      • 2)独⽴表空间⽂件保存在哪⾥?
      • 3) 每个表都对应⼀个独⽴表空间吗?
      • 4)独⽴表空间的优点和缺点?
    • 4. 撤销表空间 - Undo Tablespaces
      • 1)撤销表空间的作⽤?
      • 2)在使⽤MySQL时并没有⼿动创建撤销表空间,它是什么时候被创建的?
        • (1)默认的撤销表空间名称和路径是什么?
      • 3)可以⼿动创建撤销表空间吗?
        • (1)什么时候需要⼿动创建撤销表空间?
        • (2)使⽤⾃⼰创建的撤销表空间需要注意什么?
      • 4)如何删除撤销表空间?
        • (1)删除撤销表空间的示例
        • (2)撤销表空间被置为不活动并且已被截断为初始大小,这时不想删除了是否可以重新启⽤?
      • 5)如何查看撤销表空间的状态?
    • 5.撤销日志 - Undo Log
      • 1) 什么是撤销日志?
        • (1)撤销日志的写入时机?
      • 2)撤销日志在撤销表空间中的组织形式是怎样的?
      • 3)撤销日志的格式是怎样的?
        • (1)在事务中不同的DML操作对应的撤销⽇志是否不同?
        • (2)不同操作对应的撤销⽇志如何区分?
      • 4)撤销日志是如何组织在⼀起的?
        • 事务提交后Undo Log是否就可以删除了?
      • 5)撤销⽇志如何分类?
      • 6)InnoDB最大⽀持并发读写事务的数量如何计算?
      • 7)如何理解Undo链?
      • 8)撤销⽇志为什么需要落盘?
        • (1)撤销日志在内存中如何记录?
        • (2)撤销⽇志的写⼊过程是怎样的?
        • (3) 撤销日志的回滚过程是怎样的?
        • (4) 撤销⽇志的清理过程是怎样的?
    • 6.双写缓冲区 - Doublewrite Buffer
      • 1)双写缓冲区的作⽤?
        • 双写缓冲区中的数据保存在哪⾥?
      • 2)如何配置双写缓冲区?
    • 7.重做日志 - Redo Log
      • 1)重做日志的作用?
        • (1)为什么要⽤Redo Log,而不是直接写磁盘?
        • (2) Redo Log的写入时机?
      • 2)Redo Log的格式是怎样的?
        • (1)RedoLog中需要记录哪些内容?
      • 3)RedoLog的类型分为哪些?
      • 4)不同日志类型对应了哪些操作?
        • 如果⼀个DML操作修改了表中的多个字段,日志如何表示?
      • 5)什么是Mini-Transaction?
        • (1) DML操作会对数据页产生什么样的影响?
        • (2) 在记录RedoLog时服务器崩溃了导致日志不完整怎么办?
        • (3) Mini-Transaction的定义
        • (4) 如何标识⼀组RedoLog属于同⼀个MTR?
        • (5)如果⼀个MTR中只有⼀条⽇志是否可以优化?
        • (6) 事务与Mini-Transaction是什么关系?
      • 6)RedoLog的是如何写入缓冲区的?
        • (1)⽤来组织RedoLog的数据结构是什么?
        • (2) Log Block Header和Log Block Trailer都记录了哪些信息?
        • (3) Redo Log Block在Log Buffer中是如何组织的?
        • (4) 从日志缓冲区写RedoLog时从内存中的哪个地址开始写?
        • (5) 不同的事务在并发执⾏时如何记录RedoLog?
      • 7) Redo Log的刷盘时机?
        • (1) 刷盘策略可以进⾏配置吗?
        • (2) 不同的刷盘策略有什么影响?
      • 8)Redo Log对应磁盘上的文件是什么?
        • (1)这么多日志文件日志写到哪个文件中?
        • (2) 什么是LSN?
      • 9)Redo Log日志文件的格式?
        • (1) Log Buffer中的Redo Log Block与磁盘中的Redo Log Block有哪些不同?
        • (2)重做日志文件管理区包含哪些信息?
        • (3)管理区中具体管理了什么信息?
      • 10)什么是CHECKPOINT-检查点?
        • (1) 哪些RedoLog可以被覆盖?
        • (2) 如何记录可以覆盖的⽇志⽂件位置?
        • (3)如果没有小于 checkpoint_lsn 的⽇志时如何处理?
      • 11)重做⽇志还有哪些主要的配置项?
      • 12)如何查看重做⽇志的状态?
      • 13)如何根据RedoLog进行崩溃恢复?
        • (1) 如何确定哪些⽇志需要恢复?
        • (2) 如何获取最新的 checkpoint_lsn 和恢复的起点?
        • (3) 如何确认恢复的终点?
        • (4) 如何进⾏恢复?
        • (5)如何确定哪些⽇志在崩溃前已经落盘?
  • 四.附录
    • 1.通⽤表空间 - General Tablespace
      • 1)通⽤表空间的作⽤和特性?
      • 2)怎么创建通⽤表空间?
        • (1)创建通⽤表空间的⽰例
        • (2)创建通⽤表空间时要注意什么?
      • 3)如何向通⽤表空间中添加表?
      • 4)怎么删除通⽤表空间?
      • 5)使⽤通⽤表空间时要注意什么?
    • 2. 临时表空间 - Temporary Tablespaces
      • 1)什么是临时表?
        • (1) 除了系统⾃动创建的临时表,可以手动创建临时表吗?
      • 2) 什么是外部临时表?
      • 3)什么是内部临时表?
      • 4)临时表都有哪些设置?
      • 5)临时表中的数据存在哪⾥?
        • (1) 会话临时表空间的作用?
        • (2) 会话临时表空间的数据存在哪里?
        • (3) 全局临时表空间的作⽤?
        • (4) 全局临时表空间的数据存在哪⾥?
      • 6)怎么查看全局临时表空间的信息和大小?
        • (1)全局临时表空间数据文件的大小可以设置吗?

一.行结构

1.结构图

真实的数据在表空间以数据行的形式存储,也就是说每⼀条数据都对应着表中的⼀行,数据行在页
中的位置如下图所示:
在这里插入图片描述

2.InnoDB支持的数据行格式

InnoDB支持四种行格式,分别是: REDUNDANT 冗余格式, COMPACT 紧凑格式, DYNAMIC 动态
格式和 COMPRESSED 压缩格式,默认是 DYNAMIC 格式。

1)查看当前数据库或表的行格式

在这里插入图片描述在这里插入图片描述在这里插入图片描述

2)指定行格式

可以通过全局变量设置行格式,也可以在创建表中通过 ROW_FORMAT 子句指定行格式:
在这里插入图片描述

3)DYNAMIC 格式的组成

一个 DYNAMIC 格式的数据行会被分为两部分,一个部是存储真实数据的区域,一部分是存储额外信
息的区域

3.数据区存储真实数据方式

数据区在数据行中的位置如下图所示:

在这里插入图片描述

  • 从分隔线向右第一个字段存储真实数据的主键值,对于主键值有以下几种情况:
    • 如果表中定义了主键,则直接存储主键的值;
    • 如果是复合主键会根据列定义的顺序依次排列在这⾥;
    • 如果没有主键,会优先使用第一个不允许为NULL的 UNIQUE 唯一列作为主键;
    • 如果既没有主键也没有唯一键,那么InnoDB会构建一个6字节的字段 DB_ROW_ID 作为行的唯一标识,存储在真实数据的头部
  • 紧接着是在事务运行中两个非常重要的固定字段
    • 6字节的事务ID字段 DB_TX_ID ,记录创建或最后一次修改该记录的事务ID
    • 7字节的回滚指针字段 DB_ROLL_PTR ,如果在事务中这条记录被修改,指向这条记录的上一个版本
  • 接下来就是除了主键和值为NULL的列之外,其他列的真实数据,按照顺序从左到右依次排列
  • ⾄于为什么不存储NULL值,原因很简单,就是为了节少空间,所有允许为NULL的列都会在行额外信息区的NULL值列表中进行标识。在这里插入图片描述

4.行的额外(管理)信息区

额外信息区在数据行中的位置如下图所示在这里插入图片描述

额外信息区从右向左分别为:头信息,Null值列表,变长字段列表。

5.头信息区域

分隔线向左是额外信息区,第⼀个是固定占5Byte即40个Bit的头信息区域,头信息区由右向左主要
包含以下信息:

在这里插入图片描述

  • 下一行地址偏移量: next_record 占16bit,通过这个信息将所有的⾏链接成⼀个单向链表
  • 行类型: record_type 占3bit,包括四种类型:
    • 0:普通数据行
    • 1:索引目录行
    • 2:页内最小行infimun
    • 3:页内最大行supremun
  • 行在整个页中的位置: heap_no 占13bit;
  • 分组的行数: n_owned 占4bit,只在该行是分组最后一行才有值,这样就可以快速查询行数,而
    不用一条条的累加了
  • B+树索引树每层最小值标记: min_rec_flag 占1bit,如果当前行的类型是目录行也就record_type=1 ,同时也是B+索引树某层的最小值,则会置为1,会在索引查询时用到。
  • 删除标记: delete_mask 占 1bit ,从页中删除数据行时,并不会直接移除,而是修改这个删除标记为 1
  • 预留区:占2bit

1)删除一行记录时在InnoDB内部执行的操作

从页中删除数据行时,并不会直接移除,而是修改 delete_mask 这个删除标记为 1 ,并将next_record 改为 0 ,同时将上一行的 next_record 指向后续的⾏,从而把该行从链表中断开,如果执行事务提交后,则将这行的 next_record 指向⼀个被称为垃圾链表的区域,这个链表会被用在事务回滚中。

在这里插入图片描述

6.Null列表和其在列表的值

  • 头信息区再向右就是NULL值列表的可变区域,用来存储数据行中所有列允许为Null的值从而节省
    空间,具体的实现方式是,用1BIT的大小来表示行中某一列是否为空,这样空列就不需要记录在真
    实数据区域中了
  • 为每个没有定义 NOT NULL 约束也就是可以为NULL的列在NULL值列表中都安排了一个bit位,按
    列序号从小到大的顺序从右至左依序安排,这就是常说的逆序排列,NULL值列表最小1字节即8bit,如果没有那么多可以为NULL的列,则会⽤0补满8bit,如果为值为NULL的列超过8个,则新开辟1字节的空间,依此类推;
  • 如果某列为空,则NULL值列表中对应的bit设置为1,这样只用了⼀bit就存储了NULL列,非常节省
    空间
    在这里插入图片描述

7.变长字段的作用和其列表的值

  • 查看编码集所占的字节数(Maxlen是字节单位)
    在这里插入图片描述在这里插入图片描述

  • 以下是一个建表的SQL语句

CREATE TABLE test_student (
`id` bigint NOT NULL AUTO_INCREMENT,
`sn` char(10) NOT NULL,
`name` varchar(50) NOT NULL,
`age` int NOT NULL,
`mail` varchar(100) NOT NULL,
`remark` varchar(255) NULL,
PRIMARY KEY (`id`)
);
  • 行结构的最左侧是变长字段列表,也叫可变字段长度列表,在这个列表中记录了数据行中所有变长字段的实际长度,这样做的目的,是为了在真实数据区域,可以根据列的长度进行列与列之间的分割;
  • 需要记录的变长字段类型常见的有varchar、varbinary、text、blob,以及当使用了例如utf-8、gbk等变长字符集的char类型,当char类型的字节数可能超过768个字时,比如使用utf8mb4字符集时定义了char(255),这个字段的最大字节数是4*255=1020
  • 每个变长字段分配1 ~ 2个字节来存放这些字段的真实大小,放置顺序也是按表中字段的顺序从右至左逆序排列;

在这里插入图片描述

  • 2个字节最大可以表示65535个字节,按照最大长度字符串,比如 utf8mb4,⼀个字符占用最多4个字节计算,2个字节最多可以表示65535/4=16383个字符,列数据类型varchar的长度上限16383就是根据这个计算来的;
  • 需要特别说明的是,如果text、blob存储的内容过大,一个页已经不够放了,就会把这个列放入一
    个叫"溢出页"的独立空间中,在这个数据行对应的真实数据处,只使用20个字节来标记这个溢出页的位置信息在这里插入图片描述

1)记录变长字段的长度

  • 不同的字符集在处理字符对应的最⼤字节⻓度不同,以如 ascii 最⼤1个字节, utf8mb3 最⼤3
    个字节, utf8mb4 最⼤4个字节,如下所示在这里插入图片描述
  • 当使⽤varchar( M )指定一个字段的最大字符数时,该字段真实使⽤的字节数与建表时指定的字符
    集有关,如果指定的字符集单个字符最大占 W 个字节,从理论上讲,该列最多使用的字节数 M *
    W ,如果 M * W <= 255 则⽤⼀个字节记录这个变长字段的长度就足够了
  • 如果 M * W > 255 可能分为两种情况,假设当前变⻓字段实现占用了 L 个字节:
    • L <= 127 用⼀个字节表示长度
    • L > 127 用两个字节表示长度

2)读取长度时处理粘包问题

  • 也就是说在读取变长字段长度时,如何确定读取一个字节还是两个字节?
  • 在任何时候都是先读⼀个字节,然后判断这个字节的高位是否为0,如果是0则表⽰当前⽤一个字节表示长度,如果是1则表示当前用两个字节表示长度
  • 为1时再读⼀个字节,然后合并在一起进行解析得到该字段真实的使⽤的字节数,而且第⼆个BIT位
    表示是否使⽤溢出页

默认数据页大小为16KB,数据页中⼀个数据行的大小最大为8KB
在这里插入图片描述

小结

  1. InnoDB支持四种行格式,分别是:
    a. REDUNDANT 冗余格式
    b. COMPACT 紧凑格式
    c. DYNAMIC 动态格式(默认)
    d. COMPRESSED 压缩格式
  2. DYNAMIC 格式的数据行由两部分组成,分别是真实数据区和额外信息区
  3. 真实数据区存储的是真实数据,有三个隐藏字段分别是:
    a. DB_ROW_ID 作为行的唯一标识
    b. DB_TX_ID 事务ID字段
    c. DB_ROLL_PTR 回滚指针字段
  4. 额外信息区从右向左分别为:
    a. 头信息
    b. Null值列表
    c. 变长字段列表。

8.其他的行格式与DYNAMIC的区别

1)REDUNDANT 冗余格式

已被淘汰,之所以存在是为了与旧版本 MySQL 兼容,不建议使⽤。

2)COMPRESSED 压缩格式

⾏结构与 DYNAMIC 完全相同,只是会对数据进⾏压缩,以减少对空间的占⽤。

3)COMPACT 紧凑格式

在结构上与 DYNAMIC 相同,只是对超长字段的处理上有些区别,它不会把所有超长数据都放在溢出
页中,而是会在本行中保留前768个字节的数据,多出的部分放在溢出页中,溢出页的地址额外用20个
字节表示,那么在本行的列中就会占用768+20个字节。在这里插入图片描述

二.InnoDB 内存结构

1.InnoDB存储引擎中内存结构的主要组成部分

  • 从官⽹给出的InnoDB架构图中可以找到答案
  • InnoDB存储引擎架构链接

解答问题:

  • InnoDB存储引擎中内存结构主要分为:
    • Buffer Pool 缓冲池、
    • Change Buffer 变更缓冲区
    • adaptive_hash_index ⾃适应哈希索引
    • Log Buffer ⽇志缓冲区

在这里插入图片描述

2.需要内存结构的原因

这个问题在InnoDB架构章节已经做了解释,再来回顾一下:

  • 从MySQL实现的角度来思考这个问题,数据库的作用就是保存数据,用户的真实数据最终都会保存在磁盘上,在查询数据的过程中,如果每次都从磁盘上读取会严重影响效率,为了提高数据的访问效率,InnoDB会把查询到的数据缓存到内存中,当再次查询时,如果目标数据已经存在于内存中,就可以从内存中直接读取,从而大幅提升效率。
  • 也就是说磁盘结构中的文件是用来保存数据实现数据持久化的,内存结构是用来缓存数据提升效率
    的。

3.缓冲池-Buffer Pool

1)缓冲池的作⽤

  • 缓冲池在内存结构中的位置,如下图所示:在这里插入图片描述
  • 缓冲池主要用来缓存被访问的InnoDB表和索引数据页,是主内存中的一片区域,允许直接从内存
    访问频繁使用的数据从而提高效率。在专用数据库服务器上,通常会将多达80%的物理内存分配给
    缓冲池。
    -其次缓冲池不仅缓存了磁盘的数据页,也存储了锁信息、Change Buffer信息、Adaptive hash index、Double write buffer等信息。如上图所示

2)缓冲池是组成数据的方式

前置知识

  • 缓冲池组织数据的方式也可以说是缓冲池⽤到的数据结构,在这之前回顾一下InnoDB表空间的存
    储结构。
  • 每个InnoDB表空间在磁盘上对应⼀个 .ibd 文件,其中包含了叶子节点段和非叶⼦节点段等逻辑
    段,段中包含了区组,区组中管理着区,区别包含数据页,数据页中包含数据行,每分别对着不同
    的数据结构目的就是便于数据的管理与⾼效访问在这里插入图片描述
(1)缓冲池的结构
  • 从缓冲池的概念了解到它是主内存中的一片区域,在专⽤服务器上会将多达80%的物理内存分配给
    缓冲池,在这么大的内存空间中如何保证效率就是要解决的问题
  • 缓冲池也采⽤与表空间类似的方式对数据进行组织,如下图所示:
    • 缓冲池中包含至少⼀个 Instances 实例, Instances 是真正的缓冲池的实例对象,内存操
      作都是在 Instances 中进行的;
    • 每个 Instances 中包含至少⼀个 Chunk 块, Chunk 是在服务器运⾏状态下动态调缓冲池
      进行大小时操作的块大小;
    • 每个块中包含和管理若干个从磁盘加载到内存的 Page 数据页在这里插入图片描述
  • 可以看出缓冲池通过定义不同的数据结构,但最终管理的是每个数据页,这些数据页是从磁盘中加
    载到内存的,也就是说磁盘中的数据页加载到内存中之后,对应的就是内存中的数据页,并且页与
    页之间用链表连接。
  • 那么这时就有⼀个问题,我们知道磁盘中的数据页大小默认是16KB,并且通过头信息中的next_record 记录下⼀行地址偏移量,在页的结构定义中并没有一个字段用来表示内存中下一页的地址,那么在内存中如何为每个页建立连接呢?
(2)缓冲池中页与页之间是建立连接的方式
  • 由于数据页中没有一个字段用来表示内存中下一页的地址,为了每个数据页在内存中实现链表连
    接,InnoDB定义了一个叫"控制块"的数据结构,"控制块"中有三个重要的信息分别是:
    • 指向数据页的内存地址
    • 前一个控制块的内存地址
    • 后一下控制块的内存地址
  • 之后再用一个双向链表管理每个控制块,如下图所示在这里插入图片描述
  • 为了确定控制块链表的超始位置,专门定义了一个头节点,头节点中包含了三个主要的信息,如图
    中所示:
    • 第⼀个控制块的内存地址
    • 最后⼀个控制块的内存地址
    • 链表中控制块的数量
  • 通过遍历控制块链表就可以遍历内存中的数据页

解答问题

  • 缓冲池中主要缓存的是磁盘中的数据页,由于数据页中没有⼀个字段用来表示内存中下一页的地
    址,InnoDB定义了"控制块"的数据结构,控制块中有一个指向数据页内存地址的指针,实现"控制
    块"与数据页的一一对应,并且把每个控制块连接成一个双向链表,用一个单独的头节点记录链表
    的第一个和最后一个节点,这样通过遍历控制块链表就可以遍历内存中的数据页。
(3)内存中的数据页与磁盘上的数据页的关系

磁盘上的数据页加载到内存中后,在缓冲池中都有⼀个内存页与它对应,只不过内存中管理的是控制
块组成的链表,控制块有⼀个指针指向了内存中真实的数据页

(4)设置Buffer Pool的大小
  • 可以通过系统变量 innodb_buffer_pool_size 进行设置,设置时以字节为单位:默认值为 134217728 字节,即 128MB ;最大值取决于CPU架构和操作系统,在 32 位系统上最大值为 4294967295 (2^32 -1),在 64 位系统上最⼤值为 18446744073709551615 (2^64 -1)
  • 这⾥需要注意的是, InnoDB 为"控制块"分配额外的内存空间,也就是说"控制块"并不会占⽤
    Buffer Pool的内存空间,所以实际分配的内存总空间⽐指定的缓冲池大小大约10%左右。
  • 缓冲池设置的值越大,在多次访问相同表数据时,磁盘I/O就会越少,因为数据都已经缓存在内存
    中,所以效率也就越高,但是服务器启动时初始化时间会比较长
  • 查看缓冲池大小可以使用下面的SQL语句 在这里插入图片描述
(5)Buffer Pool中Instances的数量如何确定
  • 通过系统变量 innodb_buffer_pool_instances 可以设置缓冲池实例的个数,默认是 1 ,最大值 64 ;
  • 当缓冲池的大小小于 1GB 时,无论指定 innodb_buffer_pool_instances 数是多少都会⾃动调整为 1 ;
  • 当缓冲池的大小大于 1GB 时, innodb_buffer_pool_instances 默认值为 8 ,也可以指定大于 1 的值来设置 Instances 的数量,多个 Instances 可以提升服务器的并发性;
  • 为了获得最佳的效率,通过指定 innodb_buffer_pool_instances 和innodb_buffer_pool_size 为每个缓冲池实例设置⾄少为 1GB 的空间;
  • 查看Instances的数量可以使⽤下⾯的SQL语句 在这里插入图片描述
(6)Chunk的作用
  • Chunk 是在服务器运行状态下动态调缓冲池进行大小时操作的块大小,为避免在调整大小操作期
    间复制所有缓冲池中的数据页,调整操作以 "块"为基本单位执行; 在这里插入图片描述
  • ⽐如在服务器运⾏时想要调整缓冲池的大小可以通过以下SQL语句在这里插入图片描述
  • 注意:启动调整大小操作时,在所有活动事务完成后操作才会开始。⼀旦调整大小操作开始,新的
    事务和操作必须等到调整大小操作完成才可以访问缓冲池。
(7)Instances中Chunk的数量的确定
  • Chunk 大小可以通过系统变量 innodb_buffer_pool_chunk_size 进行设置,默认为134217728 字节即 128MB ;在设置大小时可以 以 1048576 字节即 1MB 为单位增加或减少;块中包含的数据页数取决于 innodb_page_size ;
  • 更改 innodb_buffer_pool_chunk_size 的值时注意以下条件:
    • 如果 innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances 大于当前缓冲池大小,innodb_buffer_pool_chunk_size 将被截断为innodb_buffer_pool_size / innodb_buffer_pool_instances 。
    • 缓冲池⼤⼩必须始终等于或倍数于 innodb_buffer_pool_chunk_size *
      innodb_buffer_pool_instances 。如果修改了innodb_buffer_pool_chunk_size 的值,导致不符合这个规则,那么在缓冲池初始化时innodb_buffer_pool_size 会⾃动四舍五⼊为等于或者倍数于innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances 的值。

❗ 注意:
innodb_buffer_pool_chunk_size 的设置可能会影响到缓冲池大小
innodb_buffer_pool_size 的值,所以要调整
innodb_buffer_pool_chunk_size 的值之前⼀定要计算好

(8)控制块与Page的初始化的过程
  • 前⾯介绍了 Chunk 中管理的是具体的数据页,当缓冲池初始化完成时会把每个数据页所占用的内
    存空间和对应的控制块分配好,只不是没有从磁盘加载数据时,内存中的数据页是空的而已。

  • 当缓冲池初始化的过程中,会为 Chunk 分配置内存空间,此时"控制块"会从 Chunk 的内存空间
    从左向右进行初始化,数据页所占的内存会从 Chunk 的内存空间从右向左进行初始化,当所剩的
    内存空间不够⼀组"控制块"+数据页所占的空间时,就会产生碎片空间,如果适好够用则不会出现
    碎片空间,如下图所示:在这里插入图片描述

  • 内存初始化完成之后,建⽴控制块与内存中缓冲数据⻚之间的关系,从左开始第⼀个控制块指向第
    ⼀个缓冲数据⻚的内存地址在这里插入图片描述

  • 当前从磁盘中加载数据⻚时,就可以在把数据缓存在内存中的空闭数据⻚中

(9)通过缓冲池配置来提升性能

当然可以,通过配置以下关于缓冲池的系统变量来提⾼性能,其中包括:

  • 配置缓冲池大小
  • 配置多个缓冲池实例
  • 防止缓冲池扫描
  • 配置缓冲池预取(预读)
  • 配置缓冲池刷新策略
  • 保存和恢复缓冲池状态
  • 从核心文件中排除缓冲池页
  • 这些变量可以通过以下语句查看

在这里插入图片描述

3)缓冲池中页的管理

分析过程

  • 当缓冲池初始化完成后,缓冲池中的数据页只是被分配了内存空间,并没有真实的数据,当用户进
    行数据查询时真实的数据从磁盘加载到内存中并分配⼀个内存中的数据页,这时内存中数据页的状
    态从空间变成了有实际的数据;当用户修改数据时,并不是直接修改磁盘中的数据页,而是修改内
    存中数据页中的数据页,这时内存中数据页的状态从有实际数据变成了被修改。
  • 在缓冲池中采用三个链表维护内存页,这三个链表也对应着内存中页的三种状态,分别是:
    • Free 未使⽤的页,也可以称做空闲页;
    • Clean 已使⽤但未修改的页,也可以称做⼲净页;
    • Dirty 已修改的页,也可以称做脏页。
  • 对应的三个链表分别是 Free List 、 LRU List 和 Flush List :
    • Free List :只管理Free页
    • LRU List :管理Clean页和Dirty页
    • Flush List :只管理Dirty页
  • 如下图所示:在这里插入图片描述
  • Free List :管理着空闲的也就是没有被使用的内存页,当执行查询操作时,如果对应的页已经在 buffer pool 中则直接返回数据,如果没有且 Free List 不为空,则从磁盘中查询对应的数据并存到 Free List 的某⼀页中,然后把这个页从 Free List 中移除并放入 LRU List 中。
  • LRU List :管理所有从磁盘中读取的数据页,包括未被修改的和已被修改的数据页,并根据LRU算法对链表中的页节点进行维护与淘汰。当数据库刚启动时 LRU List 是空的,这时从内存中申请到的页都存放在 Free List 中,当数据从磁盘读取到缓冲池时,⾸先从 Free List 中查找是否有可⽤的空闲页,如果有则把该页从 Free List 中删除并加⼊到 LRU List ;如果没有,则根据 LRU 算法淘汰 LRU List 末尾的页,并将该内存空间分配给新数据页;
  • Flush List :当 LRU List 中的页被修改后会被标识为脏页(Dirty page),并把脏页加⼊到Flush List中,在这种情况下,数据库会通过刷盘机制把 Flush List 中的脏页刷回磁盘; Flush List 是⼀个专门用来管理脏页的列表。脏页既存在于 LRU List 中,也存在于 Flush List 中, LRU List ⽤来管理缓冲池中页的可⽤性, Flush List ⽤来管理要被刷回磁盘的页,⼆者互不影响。 Flush List 中的脏页在执行了刷盘操作后会将空间还给 FreeList 。

解答问题

  • 每个缓冲池都采用三个链表维护内存页,这三个链表也对应着内存中页的三种状态,分别是:
    • Free 未使用的页,也可以称做空闲页;
    • Clean 已使用但未修改的页,也可以称做⼲净页;
    • Dirty 已修改的页,也可以称做脏页。
(1)内存中有这么多数据页如何快速找到目标页?
  • ⾸先第⼀种办法是通过遍历,这种做法显示不能满⾜性能要求
  • InnoDB采用的是 Page Hash 的方式,也就是每当把磁盘中数据页加载到内存时,用数据页的表
    空间Id和页号做为Key,当前页在内存中的地址做为Value保存起来,每次查询时就可以通过Key快
    速定位到目标页,如果内存中没有目标页,则从磁盘中获取。
(2)缓冲池中的数据放不下了怎么办?
  • InnoDB根据根据自身的实际场景,使⽤淘汰策略来淘汰相应的数据页,从而释放出内存空间,以
    便新的数据页加载到内存中

4)缓冲池采用哪种淘汰策略?是如何实现的?

分析过程

  • 缓冲池淘汰策略采用变形的最近最少使用(LRU)算法(在原来LRU算法的基础做了修改),以下出现的LRU算法指的是LRU变形算法。
  • 缓冲池使用LRU算法管理链表,当有新页⾯添加到缓冲池时,最近最少使用的页将被淘汰,并将新
    ⻚添加到列表的中间,这种中点插⼊策略将列表视为两个子列表:
    • 链表头部,是存放最近访问的新页(年轻页)子列表;
    • 链表尾部,是存放最近较少访问的旧页子列表。
      如下图所示:
      在这里插入图片描述
  • 经常使用的页保存在新⼦列表中,较少使用的页保存在旧子列表中,随着时间的推移,旧⼦列表中
    的页将会逐渐被淘汰。默认情况下,算法的执行过程如下:
    • 缓冲池总容量的 5/8 用于新子列表,3/8用于旧子列表;
    • 列表的中间插⼊点是新子列表的尾部与旧子列表头部的交界;
    • 当⼀个页被读⼊缓冲池时,⾸先插⼊到中点做为旧⼦列表的头节点;
    • 当访问的页在旧子列表中时,把被访问的页移动到新⼦列表的头部,使其成为 “新” 页;
    • 数据库运行的过程中,缓冲池中被访问页⾯的位置不断更新,未访问的页面向列表的尾部移动,从而逐渐"变老" ,最终超出缓冲池容量的页从旧⼦列表的尾部被淘汰。

解答问题

  • 缓冲池淘汰策略采⽤变形的最近最少使⽤(LRU)算法
(1)为什么要把页插⼊到中间而不是直接插入到新子列表的头部?
  • 因为InnoDB在读取页时,可能会发生"预读",预读的意思是InnoDB根据当前访问的记录自动推断
    后⾯可能会访问哪个页,并把他们提前加载到内存中,从而提高以后查询的效率,预读的页以并不
    一定会被真正的读取,从中间点插⼊可以使其尽快被淘汰。

5)怎么查看当前缓冲池的信息?

解答过程

  • 通过使⽤ SHOW ENGINE InnoDB STATUS 访问 InnoDB 标准监视器输出中 BUFFER POOL
    AND MEMORY
    部分查看有关缓冲池的指标。缓冲池指标位于InnoDB标准监视器输出的缓冲池和内
    存部分: 在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
  • InnoDB缓冲池指标如下,以供参考:在这里插入图片描述在这里插入图片描述

4.变更缓冲区 - Change Buffer

  • 变量缓冲区在内存中的位置在这里插入图片描述

1)变更缓冲区的作用?

分析过程
变更缓冲区占用Buffer Pool的⼀部分空间,具体如图所示: 在这里插入图片描述

  • 变更缓冲区⽤来缓存对⼆级索引数据的修改,是⼀个特殊的数据结构,当使用 INSERT 、UPDATE 或 DELETE 语句修改⼆级索引对应的数据时,如果对应的数据页在缓冲池中则直接更新,如果不在缓冲池中,那么就把修改操作缓存到变更缓冲区,这样就不⽤⽴即从磁盘读取对应的数据页了,当之后的读操作将对应的数据页从磁盘加载到缓冲池中时,变更缓冲区中缓存的修改操作再批量合并到缓冲池,从而达到减少磁盘I/O的目的。执行流程如图所示: 在这里插入图片描述
    解答问题
  • 变更缓冲区用来缓存对二级索引数据的修改,当数据页没有被回载到内存中时先把修改 缓存起来,等到其他查询操作发生时数据页被加载到内存后,再直接修改内存中的数据页,从而达到减少磁盘I/O的目的。
(1)为什么是⼆级索引?
  • 关于索引在数据库初阶已经做了介绍,我们知道索引分为聚集索引(主键)和⼆级索引(自定义)
  • 由于聚集索引具有唯⼀性,我们分析⼀下聚集索引为什么不能被放⼊变更缓存,假设表中有⼀个主键( ID ),现在有两条 INSER 语句,都在插⼊数据时ID的值相同 (id=1) ,那么在变更缓冲区中就存在两个修改操作,如果以后要合并到缓冲池中,这时就会出现重复的主键值,所以聚集索引的修改不能被加⼊到变更缓冲区;
  • 与聚集索引不同,⼆级索引通常是不唯⼀的,并且向⼆级索引中插⼊数据时由于数据列不同,所以位置相对随机,同样对于删除和更新操作可能会影响不相邻的⼆级索引页,如果每次都从磁盘读取数据就会发⽣大量的随机I/O,以变更缓冲区的方式先将修改缓存起来,当真正的读取数据时再把修改合并到缓冲池中可以提升效率。
(2)Merge的触发时机有哪些?
  • 读取对应的数据页时;
  • 当系统空闲或者 Slow Shutdown 时,主线程发起 merge
  • Change buffer 的内存空间即将耗尽时;
  • Redo Log 写满时

2)变更缓冲区的主要配置项都有哪些?

分析过程

  • 主要的配置项有缓冲类型和更改缓冲区的最大大小
  1. 缓冲类型
    在修改⼆级索引数据时变更缓冲区可以减少磁盘I/O从而提高效率,但是变更缓冲区占用了缓冲池的⼀部分空间,从而减少了可⽤于缓存数据页的内存,如果业务场景读多写少,或者表中的⼆级索引相对较少,那么可以考虑禁⽤更改缓冲从而提高缓冲池空间。

    可以通过选项文件或 SET GLOBAL 语句对系统变量 innodb_change_buffering 进行设置,来控制变更缓冲区对于插入、删除操作(索引记录被标记为删除)和清除操作(当索引记录被物理删
    除时)的开启或禁⽤:

删除操作:索引记录被标记为删除
清除操作:索引记录被物理删除时
更新操作:是插⼊和删除操作的组合

  • all : 默认值,缓存插⼊、删除标记操作和清除
  • none :不缓存任何操作
  • inserts :只缓存插⼊操作
  • deletes :只缓存删除标记操作
  • changes :缓存插⼊和删除标记操作
  • purges :缓存发⽣在后台的物理删除操作在这里插入图片描述
  1. 更改缓冲区的最大大小
  • 通过 innodb_change_buffer_max_size 系统变量可以设置更改缓冲区的最大大小,默认为25,最大为50,表示更改缓冲区占缓冲池内存总大小的百分比。
  • 在有大量插入、更新和删除的业务场景中,可以考虑增加innodb_change_buffer_max_size 的值,在⼤部分是读多写少,比如用于报表的静态数据场景中考虑减⼩ innodb_change_buffer_max_size 的值
  • 需要注意的是,如果更改缓冲区占了缓冲池太多的内存空间,会导致缓冲池中的数据⻚更快地淘
    汰。
    在这里插入图片描述

解答问题

  • 主要的配置项有缓冲类型和更改缓冲区的最大大小

3)怎么查看当前变更缓冲区的信息?

  • 通过使用SHOW ENGINE InnoDB STATUS 访问 InnoDB 标准监视器输出中 INSERT BUFFER AND ADAPTIVE HASH INDEX 部分查看有关更改缓冲区状态的信息。在这里插入图片描述

5.⾃适应哈希索引

  • ⾃适应哈希索引在内存中的位置在这里插入图片描述

1)⾃适应哈希索引的作⽤?

分析过程

  • 自适应哈希索引可以使InnoDB存储引擎在不牺牲事务特性和可靠性以及缓冲池空间足够的前提下
    提升效率,使用 起来更像是一个内存数据库,哈希索引根据经常访问的索引页自动构建;
  • 根据InnoDB内部的监控机制,如果监控到某些查询通过建立哈希索引可以提高性能,则⾃动对这
    个页创建哈希索引,这个过程称为自适应,所以叫自适应哈希索引;
  • 如果表完全放在内存中,则哈希索引可以通过直接查找任何元素来加快查询速度

解答过程

  • ⾃适应哈希索引的主要作⽤就是提升查询效率
(1)为什么要创建自适应哈希索引?
  • InnoDB存储引擎的数据存储于B+树中,B+树通常只有3到5层,但从根节点到叶节点的寻路涉及到
    多层页⾯内记录的比较,即使所有路径上的页面都在内存中,也非常消耗CPU的资源
  • InnoDB对寻路的开销进行了优化,比如寻路结束后将cursor缓存起来方便下次查询复用;尽可能的避免单词寻路开销,Adaptive hash index(AHI)便是为此而设计,可以理解为B+树的索引
  • 本质上是通过缩短寻路路径(Search Path)从⽽提升MySQL查询性能的一种方式
(2)自适应哈希索引的Key - Value如何设置 ?
  • 以查询条件为key,B+树页的地址为value的Hash Index
(3) 自适应哈希索引在保存在哪⾥?
  • 自适应哈希索引会占⽤缓冲池⼀部分内存区域,在缓冲池初始化后被初始化,为了避免AHI的锁竞
    争压力,AHI⽀持分区,可以使⽤ innodb_adaptive_hash_index_parts 参数配置分区个数,默认为 8 。

注意:⾃适应哈希索引是InnoDB内部的优化⽅式,外部不能⼲预

2)关于⾃适应哈希索引有哪些配置项?

解答问题

  • 通过设置系统变量 innodb_adaptive_hash_index 开启或关闭⾃适应哈希索引
    • 在选项文件中设置系统变量 innodb_adaptive_hash_index=[1|0] 实现开启与关闭
    • 通过命令行选项 --skip-innodb-adaptive-hash-index 也可以关闭⾃适应哈希索引
  • 每个⾃适应散列索引被绑定到不同的分区中,每个分区有不同的锁保护,分区数量由系统变量
    innodb_adaptive_hash_index_parts 控制,默认置为 8 ,最⼤值为 512 。

3) 怎么查看⾃适应哈希索引的信息?

通过使⽤ SHOW ENGINE InnoDB STATUS 访问 InnoDB 标准监视器输出中 INSERT BUFFER AND ADAPTIVE HASH INDEX 部分查看⾃适应哈希索引使⽤信息,如果锁争抢过多,可以考虑增加⾃适应哈希索引分区数量或禁⽤⾃适应哈希索引。在这里插入图片描述

6 ⽇志缓冲区

  • ⽇志缓冲区在内存中的位置
    在这里插入图片描述

1) ⽇志缓冲区的作⽤?

解答过程

  • ⽇志缓冲区是服务器启动时向操作系统申请的⼀⽚连续的内存区域,存储即将要写⼊磁盘⽇志⽂件
    的数据。
  • 在对数据库进⾏DML操作时,InnoDB会记录对应操作的⽇志,⽐如为保证数据完整性实现数据库
    崩溃恢复的RedoLog,这些⽇志会⾸先写⼊LogBuffer中,从⽽解决同步写磁盘导致的性能问题,
    然后根据不同落盘策略最终写⼊磁盘

2)⽇志不通过Log Buffer直接写⼊磁盘不⾏吗?

  • 如果日不通过Log Buffer直接写⼊磁盘,那么每次进⾏DML操作都会进⾏⼀次磁盘I/O,这样会严重影响效率,所以把⽇志统⼀写⼊内存中的Log Buffer,根据刷盘策略统⼀进⾏落盘操作,可以实现⼀次磁盘I/O写⼊多条⽇志,从⽽提升效率

三.InnoDB 磁盘文件

1.InnoDB存储引擎包含哪些磁盘⽂件?

  • 从官⽹给出的InnoDB架构图中可以找到答案
  • InnoDB存储引擎架构链接在这里插入图片描述
    解答问题
  • InnoDB的磁盘⽂件主要是表空间⽂件和其他⽂件,表空间包括:系统表空间、独⽴表空间、通⽤
    表空间、临时表空间和撤销表空间;其他⽂件有重做⽇志和双写缓冲区

1)什么是表空间?

  • 表空间可以理解为MYSQL为了管理数据而设计的⼀种数据结构,主要描述的对结构的定义,表空间文件是对定义的具体实现,以⽂件的形式存在于磁盘上,以后我们说的表空间指的就是表空间文件
  • InnoDB存储引擎的表空间包括:系统表空间、独⽴表空间、通⽤表空间、临时表空间和撤销表空

2.系统表空间 - System Tablespace

在这里插入图片描述

1)系统表空间的作⽤

解答问题

  • 系统表空间存储了MySQL中所有系统表的数据,也包括数据字典;
  • 系统表空间也是变更缓冲区的存储区域,当数据库服务器关闭时,没有合并到缓冲池的⼆级索引修
    改操作被保存到系统表空间;
  • 在以前的版本中,系统表空间也包含双写缓冲区,从MySQL8.0.20开始,双写缓冲区从系统表空间
    中移到单独的文件中。

2)系统表空间⽂件保存在哪⾥?

  • 系统表空间可以对应⼀个或多个数据文件,默认情况下,MySQL在 data ⽬录中创建⼀个系统表
    空间数据文件 ibdata1 。系统表空间数据文件的大小和数量由 innodb_data_file_path 启动选项定义。

在这里插入图片描述

3)系统表空间都有哪些可以配置的选项

分析过程

  • 可以通过 innodb_data_file_path 选项定义,如果没有指定 innodb_data_file_path的值,则默认创建⼀个大小可以自动扩展的数据文件,文件名为 ibdata1 ,初始大小 12MB 。在这里插入图片描述在这里插入图片描述

  • 数据文件命名规范的完整语法包括文件名、文件大小、自动扩展属性和max属性: 在这里插入图片描述

  • 通过在 file_size 值后⾯指定单位 K 、 M 或 G 来设置文件大小,单位为 千字节 、 兆字节 或 千兆字节 。如果以 K 为单位指定文件大小,应设置为1024的倍数。否则,千字节值四舍五⼊到最接近的兆字节 (MB) ,且文件大小⾄少为 12MB 。

  • 指定多个数据文件可以使⽤分号 ; 分隔。例如:在这里插入图片描述

  • autoextend 和 max 属性只能用于最后指定的数据⽂件,当指定 autoextend 属性时,数据文件的大小会根据需要自动扩容,默认每次增加 64MB 。可以通过系统变量innodb_autoextend_increment 控制增量的大小;如果要指定数据文件的最大容量,在autoextend 后⾯指定 max 属性。注意:只有在明确了解限制磁盘使⽤的情况下才使用 max 属性。下⾯的配置允许 ibdata1 扩展到 500MB :
    在这里插入图片描述

  • 系统表空间⽂件默认创建在 data ⽬录下。如果指定其他的⽬录,使⽤innodb_data_home_dir 选项。例如,要在名为 myibdata 的⽬录下创建⼀个系统表空间数据⽂件,可以使⽤如下配置: 在这里插入图片描述

注意:

  • 指定 innodb_data_home_dir 时,必须以斜杠 / 结尾,InnoDB不会⾃动创建⽬录,所以在启动服务器之前要确保指定的⽬录已经存在,最终通过 innodb_data_home_dir 指定的路径与数据⽂件名组合起来⽣成完整路径,如果 innodb_data_home_dir 不指定,默认值为" ./ ",即MySQL的数据⽬录
  • 如果 innodb_data_file_path 指定⼀个绝对路径,则不会读取 innodb_data_home_dir的值,系统表空间⽂件根据指定的绝对路径创建,启动服务器之前必须确保指定的⽬录存在。
  • 在添加新的数据⽂件时,不要指定现有的⽂件名,InnoDB在启动服务器时会创建并初始化新的数
    据文件

解答问题

  • 根据实际应⽤场景通过配置对应的系统变量来指定数据文件的大小、名称、数量和其他属性

4)修改系统表空间配置后什么时候⽣效?

  • 在修改系统表空间配置时,先停⽌MySQL服务,修改完成后,再重新启动MySQL服务之后⽣效。

3. 独⽴表空间 - File-Per-Table Tablespace

在这里插入图片描述

1)独⽴表空间的作⽤?

解答问题

  • File-Per-Table 表空间包含单个InnoDB表的数据和索引,默认情况下每张表都对应⼀个表空间数据⽂件,便于维护,所以称为 File-Per-Table Tablespace

2)独⽴表空间⽂件保存在哪⾥?

  • File-Per-Table 表空间在 data/database_name/ ⽬录下的 table_name.ibd 表空间数据⽂件中创建。 .ibd ⽂件与表同名。例如,表 test_db.t1 的数据⽂件,如下所示:在这里插入图片描述

  • 查看数据⽂件 在这里插入图片描述

3) 每个表都对应⼀个独⽴表空间吗?

  • 不⼀定
  • 默认每张表都对应⼀个表空间数据⽂件,但也可以通过系统变量 innodb_file_per_table[={OFF|ON}] 控制开启或禁⽤是否为每张表⽣成⼀个独⽴表空间⽂件,如果禁⽤会在系统表空间中创建表;
  • 可以在选项⽂件中指定 innodb_file_per_table 设置,也可以在运⾏时使⽤ SET GLOBAL语句设置在这里插入图片描述

4)独⽴表空间的优点和缺点?

解答问题

  1. 优点
  • 使⽤ TRUNCATE 或 DROP 语句删除 File-Per-Table 表空间中的表后,磁盘空间会返回给操作系统,从⽽提⾼磁盘利⽤率,⽽共享表空间(⽐如: System Tablespace )则不会回收磁盘空间,⽽且在共享表空间中这些空间只能被InnoDB表重新使⽤;
  • 执⾏时 TRUNCATE TABLE 时性能更好;
  • 可以在其他⽬录或单独的存储设备上创建 File-Per-Table 表空间⽂件的数据⽂件,从⽽达到I/O优化、空间管理或备份的⽬的;在这里插入图片描述
  • ⽀持与 DYNAMIC 和 COMPRESSED行格式,而系统表空间不⽀持;
  • 发⽣数据损坏、备份、⼆进制⽇志不可⽤或MySQL服务器实例⽆法重新启动时提⾼成功恢复的机会;
  • 单个表容量大小限制为 64TB ,所以可以存储更多的数据,⽽共享表空间中的表的总容量为64TB。
  1. 缺点
  • 每个表都可能有未使⽤的空间,这些空间只能由对应的表使⽤,如果管理不当,可能会导致空间浪费;
  • 当每个表都有⾃⼰的数据⽂件,操作系统需要维护更多的⽂件描述符,如果表⾮常多,可能会影响
    性能;
  • 可能会出现更多的磁盘碎⽚,会影响 DROP TABLE 表扫描性能;
  • innodb_autoextend_increment 系统变量定义了⾃动扩展共享表空间⽂件的增量大小,但对于 File-Per-Table 表空间文件不起作⽤, File-Per-Table 表空间⽂件始终⾃动扩展,初始大小根据表定义分配最⼩的空间,之后以 4MB 为增量进行扩容。

4. 撤销表空间 - Undo Tablespaces

在这里插入图片描述

1)撤销表空间的作⽤?

解答问题

  • 撤销表空间中包含撤销⽇志(Undo Log),撤销⽇志记录了如何撤销事务对聚集索引记录的最新更改
    (事务的回滚),通过对事务的回滚,从而保证事务ACID特性中的原⼦性。

2)在使⽤MySQL时并没有⼿动创建撤销表空间,它是什么时候被创建的?

分析过程

  • MySQL初始化时会在数据⽬录下创建两个默认的撤销表空间,数据文件名分别为 undo_001 和
    undo_002 ,数据字典中对应undo表空间名称为 innodb_undo_001 和innodb_undo_002 :在这里插入图片描述

解答问题

  • MySQL初始化时会在数据⽬录下创建两个默认的撤销表空间,数据⽂件名分别为 undo_001 和
    undo_002
(1)默认的撤销表空间名称和路径是什么?
  • 要查看撤销表空间名称和路径,请查询 INFORMATION_SCHEMA.FILES 在这里插入图片描述

3)可以⼿动创建撤销表空间吗?

  • 可以,通过使⽤ CREATE UNDO TABLESPACE 语句可以创建撤销表空间在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
(1)什么时候需要⼿动创建撤销表空间?
  • 对于⻓时间运⾏的⼤事务,撤销⽇志可能会变得很⼤,通过创建额外的撤销表空间来防⽌单个撤销
    表空间变得太⼤,从MySQL 8.0.14 开始,可以在运⾏时使⽤ CREATE UNDO TABLESPACE
    法创建额外的撤销表空间;
(2)使⽤⾃⼰创建的撤销表空间需要注意什么?
  • 通过系统变量 innodb_undo_directory 指定撤销表空间的默认存放路径,如果不指定默认位置为数据⽬录;
  • 撤销表空间文件名必须以 .ibu 为扩展名,定义undo表空间文件名时如果需要指定路径,必须使⽤绝对路径;不允许指定相对路径,建议使⽤唯⼀的撤销表空间文件名,避免在以后移动和复制的过程中发⽣文件名冲突;
  • 如果指定其他路径,那么路径必须在 innodb_directories 中定义,以便MySQL扫描并识别;
  • 最多⽀持 127 个 undo 表空间,包括实例初始化时创建的两个默认表空间;
  • MySQL 8.0.23 开始初始撤销表空间大小通常为 16MB,并根据服务器负载以 [16MB, 256MB] 的
    增量进行扩容;

MySQL 8.0.14 之前版本,额外的撤销表空间通过配置系统变量 innodb_undo_tablespaces 来
创建,取值范围[2,127],MySQL 8.0.14 开始,此变量已弃⽤且不再可配置。

4)如何删除撤销表空间?

分析过程

  • 从 MySQL 8.0.14开始使⽤ CREATE UNDO TABLESPACE 语法创建的撤销表空间可以使⽤
    DROP UNDO TABALESPAC 语法删除;
  • 撤销表空间在被删除之前必须是空的,要清空撤销表空间,必须⾸先使⽤ ALTER UNDO
    TABLESPACE 语法将撤销表空间标记为不活动,以便该表空间不再⽤于其他新的事务;在这里插入图片描述
  • 在将undo表空间标记为⾮活动后,等待当前undo表空间的事务完成后表空间被截断到初始大小,当undo表空间为空,就可以进⾏删除操作 在这里插入图片描述
    解答问题
  • 从 MySQL 8.0.14开始使⽤ CREATE UNDO TABLESPACE 语法创建的撤销表空间可以使⽤DROP UNDO TABALESPAC 语法删除,但要确保撤销表空间在被删除之前必须是空的,具体的操作步骤如下:
    • 将撤销表空间标记为不活动
    • 等待当前undo表空间的事务完成后表空间被截断到初始⼤⼩
    • 执⾏删除操作
(1)删除撤销表空间的示例
  • 示例
    在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
(2)撤销表空间被置为不活动并且已被截断为初始大小,这时不想删除了是否可以重新启⽤?
  • undo表空间状态为空时,可以重新激活,方法如下:在这里插入图片描述

5)如何查看撤销表空间的状态?

解答问题

  • 通过 SHOW STATUS LIKE ‘Innodb_undo_tablespaces%’; 语句可以查看撤销表空间的基本信息在这里插入图片描述

5.撤销日志 - Undo Log

在这里插入图片描述

1) 什么是撤销日志?

解答问题

  • 当事务对数据进行修改的时候,每个修改操作都会在磁盘上创建与之对应的Undo Log,当事务需要回滚时,会根据Undo Log逐⼀进行撤销操作,从⽽保证事务的原⼦性。也就是说撤销⽇志是为事务的回滚操作而诞生的机制,它是⼀个撤销操作记录的集合。
  • Undo日志保存在Undo⽇志段中,Undo日志段位于回滚段中,回滚段位于undo表空间和全局临时表空间中
(1)撤销日志的写入时机?
  • 在事务执行每个DML之前,会根据DML构建对应的撤销日志,并申请⼀个 undo log segments (撤销日志段),把日志记录在申请到的撤销段中,再执行真正的DML操作,执行过程如下所示:

在这里插入图片描述

2)撤销日志在撤销表空间中的组织形式是怎样的?

分析过程

  • ⾸先看来撤销日志在撤销表空间中的组织结构图,如下所示:
    在这里插入图片描述
  • **Undo log segments (撤销日志段)**也称为撤销段,⼀个撤销日志段可以保存多个事务的回滚日志,但在同⼀时间只能被⼀个活跃事务使用,对应的空间在事务提交或回滚后才可以被重⽤。
  • **rollback segments (回滚段)**中包含撤销日志段,通常位于undo表空间和全局临时表空间中,使⽤系统变量 Innodb_rollback_segments 可以定义分配给每个undo表空间和全局临时表空间的回滚段的数量,默认值为128,取值范围是[1,128];
  • ⼀个回滚段⽀持的事务数取决于回滚段中的undo slots(槽数)和每个事务所需的undo日志数,⼀个回滚段中的undo槽数可以根据InnoDB页面大小进⾏计算,公式是(InnoDB Page Size / 16),⽐如默认情况下 InnoDB Page Size 大小为 16KB ,那么⼀个回滚段就可以包含 1024 个 undo slot ⽤来存储事务的撤销日志。
  • 回滚段中还记录了 History List 的头节点 History List Base Node ,以及回滚段的大小
  • 通过系统变量 innodb_rollback_segments 可以设置Undo表空间中的回滚段数量,最大值和默认值都是128

在这里插入图片描述
在这里插入图片描述

解答问题

  • 撤销表空间中包含 rollback segments (回滚段),每个回滚段中包含若⼲undo slots(槽数),每个槽对应⼀个 Undo log segments (撤销⽇志段),撤销日志段中包含具体的撤销日志

3)撤销日志的格式是怎样的?

分析过程

  • 撤销⽇志格式⽰意图如下:
    在这里插入图片描述

解答问题

  • ⼀条记录在Undo Log页中的Undo Log日志大体包含两部分:分别是记录了Undo类型、表ID、上一条、下一条⽇志的偏移地址等在内的"基本信息",以及记录了不同操作和数据的"操作信息",如上图所示
(1)在事务中不同的DML操作对应的撤销⽇志是否不同?
  • 在执⾏DML语句操作数据库时,不同SQL语句对应的撤销操作不同,不同的撤销操作对应的Undo
    Log存储格式也不相同,按照增、删、改等不同的DML操作,⽣成对应的撤销⽇志。
(2)不同操作对应的撤销⽇志如何区分?
  • Undo类型有很多种,最常⻅的就是增、删、改,分别⽤ TRX_UNDO_INSERT_RECTRX_UNDO_DEL_MARK_REC 和 TRX_UNDO_UPD_EXIST_REC 表示,如图所示:

在这里插入图片描述

  • 新增( TRX_UNDO_INSERT_REC )时的UndoLog操作信息相对简单,只记录了主键值,主键⻓度
    等主键信息;
  • 删除( TRX_UNDO_DEL_MARK_REC )时除了记录主键信息之外,还记录了旧的事务ID,旧的
    ROLL_POINTER信息,⽤来构建有序的Undo版本链,还会记录⼀些被索引字段的信息;
  • 更新( TRX_UNDO_UPD_EXIST_REC )时较为复杂,如果不更新主键则和删除时类似,会记录主键信息、旧的事务ID、旧的ROLL_POINTER信息、被索引字段的信息和被更新的信息;如果更新了主键,则会记录两条Undo Log,⼀条删除的和⼀条新增的;在这里插入图片描述

4)撤销日志是如何组织在⼀起的?

分析过程
• 撤销日志的组织示意图如下:在这里插入图片描述在这里插入图片描述

解答问题

  • ⼀条条Undo Log会被逐⼀放在Undo Log页中,Undo Log页和其他类型的页⼀样都会包含头尾信
    息,除此之外还有Undo Log特有的信息,包括:
    • UNDO PAGE HEADER : 记录了Undo Log页类型、日志偏移位置、下一页链表引⽤等信息
    • UNDO LOG SEGMENT HEADER : 记录了回滚段信息
    • UNDO LOG HEADER : 记录了产生这条日志的事务Id:Trx ID;事务的提交顺序:Trx No和其他事务相关信息
  • 在这三个特有的头信息之外,其他空间都会⽤来记录Undo Log日志,如果某个事务很大,⼀个
    Undo Log页没有办法完整记录,就需要申请新的Undo Log页,然后通过 UNDO PAGE HEADER中链表引⽤信息链接到前一个页,后⾯的这些页只需要记录Undo Log并不需要记录Undo头信息。
  • 这个由Undo Log构成的链表称做Undo链,在事务中会起到非常重要的作⽤。
事务提交后Undo Log是否就可以删除了?
  • 这⾥强调一下,对于新增操作所记录的Undo Log日志,在事务提交之后就可以直接删除了,而删
    除和更新的Undo Log日志还需要服务事务的MVCC,所以并不能直接删除,而是加入到hisotry list 中。因些InnoDB为了最大程度节省空间提升效率对Undo Log进行了分类

5)撤销⽇志如何分类?

解答问题

  • Undo Log分为两⼤类:⼀类只记录新增操作,事务提交后可以直接清除;另⼀类记录删除和更新
    操作,所以相应的回滚链也会被区分为2个:Insert Undo链 和 Update Undo链(Delete + Update)
  • 另外普通表和临时表分别对应这两类Undo链,如是⼀个事务既有新增⼜有修改并且⽤到了临时
    表,那么这个事务最多可以分配四个撤销⽇志,也就是四个Undo链,分别是:
    • 对用户定义的普通表进行 INSERT 操作
    • 对用户定义的普通表进行 UPDATE 和 DELETE 操作
    • 对用户定义的临时表进行 INSERT 操作
    • 对用户定义的临时表进行 UPDATE 和 DELETE 操作
  • 根据事务的操作按需要写⼊Undo日志,比如,在普通表和临时表执行 INSERT 、 UPDATE 和
    DELETE 操作的事务需要会写⼊以上四种类型的撤销日志;只在普通表上执行 INSERT 操作的事务只需要⼀个撤消日志
  • 对普通表执行操作的事务将从指定的系统表空间或undo表空间的回滚段分配undo⽇志。对临时表
    执⾏操作的事务从指定的临时表空间回滚段分配undo⽇志。

6)InnoDB最大⽀持并发读写事务的数量如何计算?

解答问题

  • 可以⽤以下公式计算InnoDB能够⽀持的并发读写事务的数量
  • 如果每个事务执行 INSERT 或 UPDATE 或 DELETE 操作,注意只执行⼀种类型的操作,并发读
    写事务数为:在这里插入图片描述
  • 如果每个事务执行 INSERT 和 UPDATE 或 DELETE 操作,并发读写事务数为:在这里插入图片描述
  • 如果⼀个事务在临时表上执⾏ INSERT 操作,并发读写事务数为:在这里插入图片描述
  • 如果⼀个事务在临时表上执⾏ INSERT 和 UPDATE 或 DELETE 操作,并发读写事务数为:在这里插入图片描述

7)如何理解Undo链?

解答问题

  • Insert Undo链 和 Update Undo链采⽤了不同的组织⽅式;

  • 对于新增操作,Insert Undo链中的每个Undo Log都会对应⼀条新的数据⾏,这个数据行中用
    ROLL_POINTER 信息来关联Undo Log,在回滚时就可以通过它找到需要回滚的Undo Log了,如
    图所示:在这里插入图片描述

  • 对于删除和更新,Update Undo链中的每个Undo Log也都对应⼀个数据行,每次更新都会通过
    Undo Log中的 ROLL_POINTER 进行关联,从而每个数据行都会构成⼀个Undo Log版本链,回滚的时候就可以依序撤销,这个版本链在事务的MVCC中起到了⾮常重要的作⽤,⽤于解决事务的"隔离性",Update Undo链如下图所示:在这里插入图片描述

  • 以下是⼀个关于更新操作的Undo链在这里插入图片描述

8)撤销⽇志为什么需要落盘?

分析过程

  • 在对数据进行修改时,都是在内存中操作的,也就是在Buffer Pool中修改对应的数据页,在修改数
    据页之前先把对应的撤销日志记录在内存中,如果此时事务回滚直接根据内存中的撤销日志做回滚
    操作即可;
  • 在修改完成提交事务后,脏页进行落盘操作,此时事务已提交,不能回滚,所以撤销日志也就失效
    了;
  • 当服务器崩溃时,如果事务没有提交,所有的修改都在内存中,还没有落盘,对于修改直接丢弃;
    如果事务已经提交,则根据重做⽇志和双写缓冲区中的备份进⾏恢复;
  • 通过分析看上去撤销⽇志并不需要落盘,其实以上的分析场景并没有考虑到全部的场景,⽐如⼤事
    务的运⾏、MVCC中版本链什么时候可以销毁、事务的不同隔离级别等因素;

解答问题

  • 在运行大事务时,InnoDB为了避免⼤事务提交时统⼀落盘操作带来的性能问题,允许在事务进行的过程中就进行落盘操作并记录对应的UndoLog,当发⽣崩溃恢复时,通过回放UndoLog把未提交的事务进行回滚;
  • 如果⼀个事务已经提交,但还有其他事务需要访问版本链中对应的UndoLog,那么也需要把相应的
    撤销日志保存到 hisotry list 中。
  • 不同隔离级别下,没有提交的事务也可能会落盘,回滚时依然要完成撤销操作
(1)撤销日志在内存中如何记录?
  • 与数据页在内存中的保存方式相同,撤销日志在内存中也保存在Buffer Pool中,与磁盘中的UndoLog页结构⼀致,内存中每个UndoLog页通过控制块管理
  • 在内存中使⽤四个链表来管理正在使用的UndoLog页和空闲UndoLog页,根据不同的日志类型分
    为:
    ◦ Insert List :正在被使用的用来管理Insert类型的UndoLog页
    ◦ Insert Cache List :空闲的或被清理后可以被后续事务重⽤的Insert类型UndoLog页
    ◦ Update List :正在被使用的用来管理Update类型的UndoLog页
    ◦ Update Cache List :空闲的或被清理后可以被后续事务重用的Update类型UndoLog页在这里插入图片描述
(2)撤销⽇志的写⼊过程是怎样的?
  • 当写事务开始时,会先分配⼀个处理 ACTIVE 状态的 Rollback Segment ;
  • 当第⼀次DML操作产⽣Undo Record时,会轮询当前 Rollback Segment 中可⽤的 Slot ,以便获取⼀个 Undo Log Segment ;
  • 根据撤销⽇志的类型获取UndoLog⻚,并挂载到对应的List当中;
  • 在UndoLog⻚顺序写⼊⽇志,当⼀个UndoLog⻚写满之后,会获取新的UndoLog⻚以便继续写⼊当前事务⽣成的⽇志,这⾥注意:单条UndoLog不能跨⻚存储,也就是说当某条⽇志在当前⻚中放不下时,会整体保存下⼀⻚中;
  • 由后台线程把⽇志刷⼊磁盘;
  • 当事务结束之后(commit或者rollback), insert ⽇志类型对应的 Undo Log Segment 和UndoLog page 会直接回收,⽽ update ⽇志类型对应的 Undo Log Segment 和UndoLog page 会等待后台的清理操作完成后,确保⽇志不会有事务再访问后进⾏回收,回收的UndoLog⻚被挂载到Cache List中。
(3) 撤销日志的回滚过程是怎样的?
  • 回滚操作可以是用户通过rollback主动触发,也可能发⽣在崩溃恢复时,不论是哪种触发条件,回滚操作都是相同的,基本过程就是读取该事务的Undo Log,从后向前依次进⾏逆向操作,从而恢复索引记录;
  • 对于 Insert 类型的回滚操作就是 Delete ,在删除的过程中会重新调整主键索引和二级索引;
  • 对于Update和Delete类型的回滚操作,主要是回退这次操作在所有主键索引和二级索引的影响,可能包括重新插⼊被删除的二级索引记录、去除行管理信息中的Delete Mark标记、将二级索引记录修改回之前的值等;
  • 完成回滚的Undo Log会进行回收,将不再使用的UndoLog页的磁盘空间还给 Undo LogSegment ,这个过程是写⼊过程的逆操作。
(4) 撤销⽇志的清理过程是怎样的?
  • InnoDB通过保存多份Undo Log的历史版本来实现MVCC,当某个历史版本已经确认不会被任何现有的和未来的事务访问时,就应该被清理掉;

  • 当开启⼀个事务时都会被分配⼀个事务编号 trx_id ,而事务进行读操作时会创建⼀个ReadView,并记录当前所有事务中的最⼩活跃事务编号 m_low_limit_id ,如果版本链中 ⽇志的trx_id < m_low_limit_id ,则表示当前读操作发⽣时,日志对应的事务已提交,其修改的新版本是可⻅的,因此不再需要通过Undo版本链构建之前的版本,这个事务的Undo Log也就可以被清理了。在这里插入图片描述

  • Undo的清理⼯作是由专⻔的后台线程进⾏扫描和分发,并由多个线程进⾏清理,并可以通过系统
    变量 innodb_purge_threads 配置清理线程数,系统变量 innodb_purge_batch_size可以配置每次清理的⻚数。

6.双写缓冲区 - Doublewrite Buffer

在这里插入图片描述

1)双写缓冲区的作⽤?

解答问题

  • 双写缓冲区是磁盘上的⼀个存储区域,当 InnoDB 将缓冲池中的数据页写入到磁盘上表空间数据文件之前,先将对应的页写到双写缓冲区;如果在数据真正落盘的过程中出现了意外退出,比如操作系统、存储子系统崩溃或异常断电的情况, InnoDB 在崩溃恢复时可以从双写缓冲区中找到⼀份完好的页副本,执行过程如下图所示
    在这里插入图片描述
双写缓冲区中的数据保存在哪⾥?
  • 在MySQL 8.0.20之前, doublewrite 缓冲区位于InnoDB系统表空间中。从MySQL 8.0.20开
    始, doublewrite 缓冲区默认存储区域位于数据目录下的 doublewrite 文件中
    在这里插入图片描述

2)如何配置双写缓冲区?

解答问题

  • 是否启⽤ doublewrite 缓冲区可以通过系统变量 innodb_doublewrite[=ON|OFF] 控制,默认为启⽤,如果真实的业务场景更关注性能⽽不是数据完整性,可以考虑禁⽤doublewrite缓冲区,例如在执行测试的环境中;
  • doublewrite 文件所在目录通过系统变量 innodb_doublewrite_dir (MySQL 8.0.20中引⼊)指定,如果不指定则在 innodb_data_home_dir ⽬录(默认为data⽬录)下创建;如果指定doublewrite目录,建议设置在最快的存储介质上,以提⾼效率;在这里插入图片描述

命名⽅式为: #ib_page_size_file_number.dblwr ,以上 #ib_16384_0.dblwr 的文件表示当前数据页的大小为16KB,编号为0

  • 双写文件的数量通过系统变量 innodb_doublewrite_files 设置,默认情况下,为每个缓冲池实例创建两个doublewrite⽂件,也就是说⽂件数量为 innodb_buffer_pool_instances*2 ;此变量⽤于⾼级性能调优,⼤多数场景使⽤默认设置即可;

7.重做日志 - Redo Log

在这里插入图片描述

1)重做日志的作用?

解答问题

  • 重做日志在保证事务的持久性和⼀致性⽅⾯起到了至关重要的作用
  • 重做日志⽤于在数据库崩溃后恢复已提交事务还没有来的及落盘的数据。重做日志以文件的形式保存在磁盘上,在正常的操作过程中,MySQL根据受影响的记录进⾏编码并写⼊重做日志文件,这些数据称为"Redo",在重新启动时自动读取重做日志进行数据恢复。
(1)为什么要⽤Redo Log,而不是直接写磁盘?
  • 我们来分析⼀下,⾸先明确⼀点,我们对数据进行的DML操作都会包含在事务当中,当完成修改并
    且提交事务之后,在内存中被修改的数据页就要刷新到磁盘完成持久化;
  • 那么如果这次DML操作对应的修改开始刷盘的话,当服务器崩溃,没有被刷到磁盘的数据页就从内
    存中丢失,这时这个事务的修改在磁盘上就是不完整的,也就是没有保证事务的⼀致性
  • 为了解决这个问题,InnoDB在执⾏每个DML操作时,当内存中的数据页修改完成之后,把修改的内容以日志的形式保证在磁盘上,然后再对数据⻚进⾏真正的落盘操作,这样做就相当于对修改进⾏了⼀次备份,即使当服务器崩溃也不会受到影响,当服务器重启之后,可以从磁盘上的⽇志⽂件中找到上次崩溃之前没有来的及落盘的数据继续执⾏落盘操作;
  • InnoDB引擎的事务采⽤了 WAL技术(Write-Ahead Logging) ,基本思想是先写⽇志,再写磁盘,只有⽇志写⼊成功,事务才算提交成功,这⾥的⽇志就是Redo Log。当发⽣宕机且数据未刷到磁盘的时候,可以通过Redo Log来恢复,保证ACID中的持久性,这也是Redo Log的作⽤。
(2) Redo Log的写入时机?
  • 当发⽣数据修改操作时追加重做日志,已落盘数据对应的⽇志位置被记录为⼀个检查点,检查点之
    前的数据被置为⽆效,所以重做日志⽂件可以循环使⽤。以⼀个更新操作为例,重做日志的写入过
    程与时机,如下图所示:在这里插入图片描述
  • 前⾯已经介绍过为什么要使用Log Buffer,因为每次进行DML操作都会进行⼀次磁盘I/O,这样会严
    重影响效率,所以把日志统⼀写入内存中的Log Buffer,根据刷盘策略统⼀进行落盘操作,可以实现⼀次磁盘I/O写⼊多条日志,从而提升效率。

2)Redo Log的格式是怎样的?

分析过程

(1)RedoLog中需要记录哪些内容?
  • 当进行DML操作时,⾸先要修改内存中的数据页,但是修改的数据有可能只是数据页中很少的⼀部
    分内容,甚⾄有可能只修改了⼏个字节,那么在RedoLog中是要记录整个数据页吗?当然不是,如
    果每次保存整个数据页的话就有太多的无用数据写入日志,严重影响效率而且浪费空间
  • 为了节省空间提高效率,RedoLog只记录被修改的内容,比如当前的DML修改了哪个表空间、表空
    间中的哪个数据页,数据页中多少偏移量处的值修改成了什么,比如:

在这里插入图片描述

  • 这样就可以⽤很⼩的⽇志记录当前对数据⻚所做的修改,⼤⼤节省了空间

解答问题

  • RedoLog本质上只是记录了事务对数据库做了哪些修改,修改操作包含多种场景,比如对数据行、
    索引页的增删改,对范围的修改与删除等等,不同场景的 redo ⽇志定义了不同的类型,但是绝大
    部分类型的 redo 日志都有下边这种通用的结构包括:
    • Type :日志类型, 1BTYE
    • Space ID :操作所属的表空间, 4BTYE
    • Page no:操作的数据页在表空间中的编号, 4BTYE
    • data :日志的内容,长度不固定
  • 如下图所示:在这里插入图片描述
    data 部分的具体内容是什么?
  • data 部分⼜可以分为:数据页中的偏移量,修改内容的⻓度和具体的修改内容,如下图所示:在这里插入图片描述

3)RedoLog的类型分为哪些?

分析过程

  • 查看RedoLog的类型,最完整最直观的⽅式就是通过阅读源代码,如下所示:
/** @file include/mtr0types.h
Mini-transaction buffer global types
Created 11/26/1995 Heikki Tuuri
*******************************************************/
// 省略...
/** @name Log item types
The log items are declared 'byte' so that the compiler can warn if val
and type parameters are switched in a call to mlog_write_ulint. NOTE!
For 1 - 8 bytes, the flag value must give the length also! @{ */enum mlog_id_t {
/** if the mtr contains only one log record for one page,
i.e., write_initial_log_record has been called only once,
this flag is ORed to the type of that first log record */MLOG_SINGLE_REC_FLAG = 128, //提供额外信息类型/** one byte is written */
MLOG_1BYTE = 1,/** 2 bytes ... */
MLOG_2BYTES = 2,/** 4 bytes ... */
MLOG_4BYTES = 4,/** 8 bytes ... */
MLOG_8BYTES = 8,//数据页类型,主要对日志进行优化/** Record insert */
MLOG_REC_INSERT_8027 = 9,/** Mark clustered index record deleted */
MLOG_REC_CLUST_DELETE_MARK_8027 = 10,/** Mark secondary index record deleted */
MLOG_REC_SEC_DELETE_MARK = 11,/** update of a record, preserves record field sizes */
MLOG_REC_UPDATE_IN_PLACE_8027 = 13,/*!< Delete a record from a page */
MLOG_REC_DELETE_8027 = 14,/** Delete record list end on index page */
MLOG_LIST_END_DELETE_8027 = 15,/** Delete record list start on index page */
MLOG_LIST_START_DELETE_8027 = 16,/** Copy record list end to a new created index page */
MLOG_LIST_END_COPY_CREATED_8027 = 17,/** Reorganize an index page in ROW_FORMAT=REDUNDANT */
MLOG_PAGE_REORGANIZE_8027 = 18,/** Create an index page */
MLOG_PAGE_CREATE = 19,/** Insert entry in an undo log */
MLOG_UNDO_INSERT = 20,/** erase an undo log page end */
MLOG_UNDO_ERASE_END = 21,/** initialize a page in an undo log */
MLOG_UNDO_INIT = 22,/** reuse an insert undo log header */
MLOG_UNDO_HDR_REUSE = 24,/** create an undo log header */
MLOG_UNDO_HDR_CREATE = 25,/** mark an index record as the predefined minimum record */
MLOG_REC_MIN_MARK = 26,/** initialize an ibuf bitmap page */
MLOG_IBUF_BITMAP_INIT = 27,#ifdef UNIV_LOG_LSN_DEBUG/** Current LSN */
MLOG_LSN = 28,#endif /* UNIV_LOG_LSN_DEBUG */
/** this means that a file page is taken into use and the prior
contents of the page should be ignored: in recovery we must not
trust the lsn values stored to the file page.
Note: it's deprecated because it causes crash recovery problem
in bulk create index, and actually we don't need to reset page
lsn in recv_recover_page_func() now. */
MLOG_INIT_FILE_PAGE = 29,/** write a string to a page */
MLOG_WRITE_STRING = 30,/** If a single mtr writes several log records, this log
record ends the sequence of these records */
MLOG_MULTI_REC_END = 31,/** dummy log record used to pad a log block full */
MLOG_DUMMY_RECORD = 32,/** log record about creating an .ibd file, with format */
MLOG_FILE_CREATE = 33,/** rename a tablespace file that starts with (space_id,page_no) */
MLOG_FILE_RENAME = 34,/** delete a tablespace file that starts with (space_id,page_no) */
MLOG_FILE_DELETE = 35,/** mark a compact index record as the predefined minimum record */
MLOG_COMP_REC_MIN_MARK = 36,/** create a compact index page */
MLOG_COMP_PAGE_CREATE = 37,/** compact record insert */
MLOG_COMP_REC_INSERT_8027 = 38,/** mark compact clustered index record deleted */
MLOG_COMP_REC_CLUST_DELETE_MARK_8027 = 39,/** mark compact secondary index record deleted; this log
record type is redundant, as MLOG_REC_SEC_DELETE_MARK is
independent of the record format. */
MLOG_COMP_REC_SEC_DELETE_MARK = 40,/** update of a compact record, preserves record field sizes */
MLOG_COMP_REC_UPDATE_IN_PLACE_8027 = 41,/** delete a compact record from a page */
MLOG_COMP_REC_DELETE_8027 = 42,/** delete compact record list end on index page */
MLOG_COMP_LIST_END_DELETE_8027 = 43,/*** delete compact record list start on index page */
MLOG_COMP_LIST_START_DELETE_8027 = 44,/** copy compact record list end to a new created index page */
MLOG_COMP_LIST_END_COPY_CREATED_8027 = 45,/** reorganize an index page */
MLOG_COMP_PAGE_REORGANIZE_8027 = 46,/** write the node pointer of a record on a compressed
non-leaf B-tree page */
MLOG_ZIP_WRITE_NODE_PTR = 48,/** write the BLOB pointer of an externally stored column
on a compressed page */
MLOG_ZIP_WRITE_BLOB_PTR = 49,/** write to compressed page header */
MLOG_ZIP_WRITE_HEADER = 50,/** compress an index page */
MLOG_ZIP_PAGE_COMPRESS = 51,/** compress an index page without logging it's image */
MLOG_ZIP_PAGE_COMPRESS_NO_DATA_8027 = 52,/** reorganize a compressed page */
MLOG_ZIP_PAGE_REORGANIZE_8027 = 53,/** Create a R-Tree index page */
MLOG_PAGE_CREATE_RTREE = 57,/** create a R-tree compact page */
MLOG_COMP_PAGE_CREATE_RTREE = 58,/** this means that a file page is taken into use.
We use it to replace MLOG_INIT_FILE_PAGE. */
MLOG_INIT_FILE_PAGE2 = 59,/** Table is being truncated. (Marked only for file-per-table) */
/* MLOG_TRUNCATE = 60, Disabled for WL6378 *//** notify that an index tree is being loaded without writing
redo log about individual pages */
MLOG_INDEX_LOAD = 61,/** log for some persistent dynamic metadata change */
MLOG_TABLE_DYNAMIC_META = 62,/** create a SDI index page */
MLOG_PAGE_CREATE_SDI = 63,/** create a SDI compact page */
MLOG_COMP_PAGE_CREATE_SDI = 64,/** Extend the space */
MLOG_FILE_EXTEND = 65,
/** Used in tests of redo log. It must never be used outside unit tests. */
MLOG_TEST = 66,
MLOG_REC_INSERT = 67,
MLOG_REC_CLUST_DELETE_MARK = 68,MLOG_REC_DELETE = 69,
MLOG_REC_UPDATE_IN_PLACE = 70,
MLOG_LIST_END_COPY_CREATED = 71,
MLOG_PAGE_REORGANIZE = 72,
MLOG_ZIP_PAGE_REORGANIZE = 73,
MLOG_ZIP_PAGE_COMPRESS_NO_DATA = 74,
MLOG_LIST_END_DELETE = 75,
MLOG_LIST_START_DELETE = 76,/** biggest value (used in assertions) */
MLOG_BIGGEST_TYPE = MLOG_LIST_START_DELETE};
// 省略...

解答问题

  • RedoLog的类型根据数据操作的不同场景和对日志的优化⽅式有几十种之多,总体可以分为:
    • ⽤于数据页的日志类型,比如对数据页的修改
    • ⽤于表空间文件的日志类型,比如对表空间的修改
    • 提供额外信息的日志类型

4)不同日志类型对应了哪些操作?

分析过程

  • ⽇志类型总体可以分为三⼤类,分别是:用于数据页的日志类型、用于表空间文件的日志类型和提
    供额外信息的日志类型,不同的日志类型对应的⽇志内容也不尽相同,而进⾏DML操作时,大多数
    RedoLog属于用于数据页的日志类型
  • 属于用于数据页的日志类型中的几种最常见数据操作所对应的日志类型如下:
    • MLOG_WRITE_STRING = 30 , type 对应的值为30,表示在页面的某个偏移量处写入一个字符串,由于字符串的⻓度不固定,需要用到⼀个表示长度的区域记录,此时日志内容格式如下图所示:在这里插入图片描述
    • MLOG_4BYTES = 4, type 对应的值为4,这种类型应⽤于对固定⻓度值的修改,⽐如修改整型字段,由于⻓度固定,所以⽤于表⽰⻓度的区域可以省略,从⽽尽可能的减少空间使⽤,此时⽇志内容格式如下图所⽰:在这里插入图片描述
    • 类似的类型还有 MLOG_1BYTE = 1, MLOG_2BYTES = 2 , MLOG_8BYTES = 8 分别表示固定修改1字节、2字节、8字节的数据,⽇志格式与 MLOG_4BYTES = 4 相同
    • 还有其他⼀些⽇志类型,⽐如:
      • MLOG_REC_INSERT_8027 = 9 ,表示写⼊⼀条⾏格式为⾮紧凑型的记录,对应的⾏格式为 Redundant
      • MLOG_COMP_REC_INSERT_8027 = 38 ,表示写⼊⼀条⾏格式为紧凑型的记录,对应
        的格式为Compact、Dynamic和Compressed
      • MLOG_REC_UPDATE_IN_PLACE_8027 = 13 ,表示更新⼀条记录
      • MLOG_REC_DELETE_8027 = 14 ,表示从数据⻚中删除⼀条记录
      • MLOG_LIST_END_DELETE_8027 = 15 ,表示从索引⻚中删除最后⼀条记录
      • MLOG_LIST_START_DELETE_8027 = 16 ,表示从索引⻚中删除第⼀条记录
      • MLOG_LIST_END_DELETE_8027 和 MLOG_LIST_START_DELETE_8027 配合可以删除索引⻚中⼀个范围的记录,⽽不⽤记录每⼀条记录的删除⽇志

      • MLOG_PAGE_CREATE = 19 ,表示创建⼀个索引⻚,这个类型是关于页的类型
  • 属于⽤于表空间⽂件的⽇志类型:
    • MLOG_FILE_CREATE = 33 ,表示创建⼀个.ibd表空间文件
    • MLOG_FILE_RENAME = 34 ,表示重命名⼀个表空间文件
    • MLOG_FILE_DELETE = 35 ,表示删除⼀个表空间文件
  • 属于提供额外信息的日志类型:
    • MLOG_MULTI_REC_END = 31 只由⼀个字节的 Type 构成,⽤于标识⼀个 MiniTransaction(MTR)的结尾。

解答问题

  • 不同的⽇志类型对应的⽇志内容和作⽤各不相同
  • ⽤于数据⻚的⽇志类型
  • 主要记录数据⻚的修改,⽐如创建和删除数据⻚,以及对数据的增删改查操作
  • ⽤于表空间⽂件的⽇志类型主要记录对表空间⽂件的修改
  • 提供额外信息的⽇志类型主要标记⼀个Mini-Transaction的结尾
如果⼀个DML操作修改了表中的多个字段,日志如何表示?

上⾯的分析过程介绍了简单的DML操作对应的⽇志,通常情况下,⼀个DML操作会修改表中的多个字
段,也可能修改多条记录,对于正常的增删改对应不同的⽇志类型,对应⽇志所包含的主要信息如下
图所示:在这里插入图片描述

  • 新增操作:主要包含数据⻚内的偏移量,主键信息,新增的字段个数,每个字段的内容的实际⻓
    度,具体的内容等
  • 删除操作:主要包含数据⻚内的偏移量,主键信息、旧事务的Id,旧roll_pointer,是否删除标识
  • 更新操作:主要包含数据⻚内的偏移量,主键信息、旧事务的Id,旧roll_pointer,被更新字段的
    数据,每个被更新字段的实际⻓度,更新的具体内容

5)什么是Mini-Transaction?

(1) DML操作会对数据页产生什么样的影响?
  • 以⼀个Insert操作为例,对数据页的影响⼀般分为两种情况:
    • 如果写⼊记录所在的数据页空间充⾜,⾜够存储⼀条将要写⼊的记录,那么就可以直接写入,
      如下图所示:在这里插入图片描述
    • 如果写⼊的数据⻚空间不充⾜,⽆法放下这条记录,由于在数据⻚中真实数据是按主键顺序排列的,那么就要新建⼀个数据⻚,对原来的数据进⾏调整,把⼀部分数据复制到新的数据⻚中,以便在⽬标数据⻚上留出⾜够的空间来保存即将写⼊的记录,此时对应的⽰意图如下所示:在这里插入图片描述
  • 通过以上两种情况下插⼊⼀条记录的分析可以看出,当数据⻚空间充⾜的情况下可以直接写⼊数
    据,并记录⼀条对应RedoLog即可
  • 当数据⻚空间不充⾜⽆法放下这条记录的情况下,会创建⼀个新数据⻚,同时还有数据的复制和写
    ⼊,索引树⾮叶⼦节点上修改,在实际的执⾏过程中还有对表空间中段、区中统计信息的修改等
    等,这意味⼀个简单的Insert操作有会产⽣很多条RedoLog。
(2) 在记录RedoLog时服务器崩溃了导致日志不完整怎么办?
  • 那么这时有⼀个问题需要考虑,试想⼀下如果执⾏这⼀系统操作的时候,RedoLog只记录了⼀半服
    务器就崩溃了,那么当服务器重启的时候如果按照RedoLog进⾏恢复,得到的结果肯定是错误的,
    所以在记录RedoLog的时候要保证⼀个DML所对应的⼀系列⽇志必须是完整的才可以执⾏恢复操
    作,否则就不执行恢复。
(3) Mini-Transaction的定义
  • Mini-Transaction就是针对以上的操作过程定义的概念,也就是说把记录⼀个DML操作的过程称为⼀个 Mini-Transaction ,简称 MTR ,⼀个所谓的MTR包含⼀个DML操作产⽣的⼀组完整⽇志,在进⾏崩溃恢复时这⼀组RedoLog做为⼀个不可分割的整体。
  • 这⾥所说的不可分割的组是MySQL中定义的,常⻅的有:
    • 向聚簇索引对应B+树的⻚⾯中插⼊⼀条记录时产⽣的RedoLog不可分割;
    • 向某个⼆级索引对应B+树的⻚⾯中插⼊⼀条记录时产⽣的RedoLog不可分割;
    • 还有其他的⼀些对⻚⾯的访问操作时产⽣的RedoLog不可分割。
  • 每条语句根据具体的执⾏情况可能会产⽣多个MTR。

解答问题

  • Mini-Transaction是MySQL内部对底层数据⻚的⼀个原⼦操作,包含⼀个DML操作产⽣的⼀组完整
    ⽇志,保证数据库异常恢复时数据⻚中数据的⼀致性。
(4) 如何标识⼀组RedoLog属于同⼀个MTR?
  • 在执⾏DML操作的过程中,每⼀个对数据⻚的修改都会记录⼀条RedoLog,这些⽇志会被顺序记录
    下来,并在这组⽇志的最后加⼀条特殊的⽇志标识作为⼀个MRT的结尾,这条特殊的⽇志结构⾮常
    简单,只有⼀个 TYPE 字段,类型为 MLOG_MULTI_REC_END = 31 ,也就是⽇志分类中的提
    供额外信息的⽇志类型,⼀个MTR对应的⽇志组,如下图所示:在这里插入图片描述
(5)如果⼀个MTR中只有⼀条⽇志是否可以优化?
  • 当然可以,如果⼀个MTR只有⼀条⽇志,直接在这条⽇志后加⼀个类型为MLOG_MULTI_REC_END = 31 的标识可以做为MTR的结尾,但这样做有点浪费空间;
  • InnoDB为了尽可能的节省空间,在MTR只有⼀条⽇志的情况下,做了⼀个优化。通过上⾯的介绍
    了解了⽇志类型虽然很多,但也只有⼏⼗种,⽽⽤来表⽰⽇志类型的 TYPE 字段⻓度为 1BTYE ,
    ⽽这 1BTYE 中只⽤7个⽐特位,代表整数127,就完全可以表⽰所有的⽇志类型,与是省出来⼀个
    ⽐特位就可以⽤来表⽰当前MTR包含⼀条还是⼀组RedoLog,也就是说如果 TYPE 字段的第⼀个
    ⽐特位为 1 ,表⽰MTR只包含⼀条RedoLog,为 0 表⽰MTR包含⼀组RedoLog,如下图所示:在这里插入图片描述
(6) 事务与Mini-Transaction是什么关系?
  • Mini-Transaction是包含的是⼀个DML操作对应的⼀组RedoLog,⽽⼀个事务中可能会包含多个DML操作,所以⼀个事务中包含⼀个或多个SQL语句,⼀个SQL语句包含⼀个或多个MRT,⼀个MTR包含⼀条或多条RedoLog,他们之间的关系如下图所示:在这里插入图片描述

6)RedoLog的是如何写入缓冲区的?

前置知识

  • 这个问题可以理解为RedoLog的写⼊过程,要了解写⼊过程,必须先介绍RedoLog在内存和⽂件中
    是如何进⾏描述和组织的,我们提出以下⼏个问题:
(1)⽤来组织RedoLog的数据结构是什么?
  • ⽤来组织RedoLog的数据结构是Redo页,页的大小是 512B ,也可以称为⼀个 Redo Log Block ,这个大小刚好对应磁盘上⼀个扇区,当⽇志写⼊磁盘时可以保证连续性, Redo Log Block 的示意图如下所示:在这里插入图片描述
  • 在⼀个 Redo Log Block 中,包含⽤来存储管理信息的块头 Log Block Header (占12Byte)和块尾 Log Block Trailer (占4Byte),其他的空间是真正⽤来存储⽇志的区域 Log BlockBody (占496B)
(2) Log Block Header和Log Block Trailer都记录了哪些信息?
  • Log Block Header 和 Log Block Trailer 包含的信息如下图所示:在这里插入图片描述
  • Log Block Header
    • LOG_BLOCK_HDR_NO :Block的唯⼀标识,是⼀个⼤于0的值,取值范围1~0x40000000UL,而0x40000000UL对应的整数是1073741824即1GB,也就是说InnoDB最多能够⽣成1GB个⽇志块,每个⽇志块为512B,所以InnoDB允许维护⽇志的最⼤容量为 512GB 。
    • LOG_BLOCK_HDR_DATA_LEN :表⽰Block中已经使⽤了多少字节,由于块头占⽤了12B的
      空间,所以初始值为12,当 Log Block Body 被全部写满时那么这个值就是512;
    • LOG_BLOCK_FIRST_REC_GROUP :如果⼀个MTR会⽣产多条redo⽇志记录,这些⽇志记录被称之为⼀个redo⽇志记录组, LOG_BLOCK_FIRST_REC_GROUP 代表该Block中第⼀个MTR中第⼀条⽇志的偏移量。
    • LOG_BLOCK_CHECKPOINT_NO :表示检查点的编号
  • Log Block Trailer
    • LOG_BLOCK_CHECKSUM :表示Block的校验和,⽤于正确性校验。
(3) Redo Log Block在Log Buffer中是如何组织的?
  • 在内存中RedoLog存储在⽇志缓冲区(Log Buffer)中,⽇志缓冲区是服务器启动时向操作系统申请的⼀⽚连续的内存区域,并被划分成若⼲个连续的 Redo Log Block ,⽤来存储即将要写⼊磁盘⽇志⽂件的数据,如下图所示:在这里插入图片描述
  • ⽇志缓冲区⼤⼩可以通过系统变量 innodb_log_buffer_size 指定,默认大小为 16MB ,取值范围1048576(1MB) ~ 4294967295(4GB)在这里插入图片描述
    在这里插入图片描述

分析过程

  • 向⽇志缓冲区中写⼊⽇志是⼀个顺序写⼊的过程,也就是从缓冲区的第⼀个 Redo Log Block的 Log Block Body 开始依次向后写,⼀个block的空间空间⽤完之后再写下⼀个block,那么有⼀个⾸先要解决的问题,当有⼀记⽇志需要写⼊缓冲区时,应该往哪个block中的哪个位置写呢?
(4) 从日志缓冲区写RedoLog时从内存中的哪个地址开始写?
  • InnoDB 的提供了⼀个名为 buf_free 的全局变量,该变量表示后续写⼊⽇志在 Log Buffer中的起始位置,如图所示:在这里插入图片描述
(5) 不同的事务在并发执⾏时如何记录RedoLog?
  • 通过前⾯的介绍了解到,InnoDB以MTR为单位记录RedoLog,⼀个事务中包含多个MTR,⼀个MTR包含多条RedoLog,这些RedoLog是⼀个不可分割的⽇志组;
  • ⼀个事务在执⾏过程中并不是每生成⼀条RedoLog就写⼊到Log Buffer中,而是把生成的RedoLog先缓存在内存的⼀个区域中,当⼀个MTR执行完成后把这组⽇志⼀起复制到Log Buffer;
  • 假设有两个事务T1,T2并发执行,每个事务中都包含2个MRT,即事务T1包含mtr_t1_1和mtr_t1_2,T2包含mtr_t2_1和mtr_t2_2,如下图所示:在这里插入图片描述
  • 在并发环境下不同事务中的MTR是交替执行的,当MTR执行完成之后对应生成的RedoLog会被写入Log Buffer,所以在Log Buffer中日志的写入形式如下图所示在这里插入图片描述
  • 需要说明⼀点,不同的MTR产⽣的⽇志组占⽤的存储空间可能不⼀样,有的MTR产⽣的⽇志很少,
    有的MTR产⽣的⽇志量⾮常多。

解答问题

  • RedoLog在内存中⽤Redo⻚进⾏组织,称为 Redo Log Block ,每个 Redo Log Block 大小固定为512B,对应磁盘上⼀个扇区,⽇志被顺序安排在 Log Block Body 中;
  • 在Log Buffer中多个 Redo Log Block 顺序排列, Redo Log Block 的个数由Log Buffer的⼤⼩决定;
  • 当执⾏事务时,不同的语句对应不同的数据库操作,⼀条SQL语句可能包含多个MTR,⼀个MTR包含多条RedoLog,MTR中的多条⽇志称为⼀个⽇志组,写⼊Log Buffer的⽇志是以MTR对应的⽇志组为⼀个单位,这组⽇志不可分割。

7) Redo Log的刷盘时机?

分析过程

  • 当⼀个MTR执⾏完成后,RedoLog会被写⼊Log Buffer,⽽Log Buffer大小是有限的,并且这些记录日志的⽬的是为了服务器崩溃后的数据恢复,在内存中保存也不安全,所以在把它们刷到磁盘上进行保存

解答问题

  • InnoDB在以下情况会把RedoLog刷到磁盘:
    • Log Buffer 空间不⾜时:Log Buffer大小是有限的,可以通过系统变量innodb_log_buffer_size 设置,如果当前Log Buffer中的RedoLog占⽤了Log Buffer总容量⼀半左右会触发刷盘;
    • 事务提交时:当事务提交时,事务中对应的MTR已经完全记录在了Log Buffer中,在数据真正落盘之前,需要把对应的RedoLog刷新到磁盘;
    • 后台线程定时刷盘:后台的 Master Thread 线程,大约每秒都会把Log Buffer中的RedoLog刷新到磁盘;
    • 正常关闭服务器时:在服务关闭之前会把会把Log Buffer中的RedoLog刷新到磁盘;
(1) 刷盘策略可以进⾏配置吗?
  • 可以,⽇志缓冲区的内容定期刷新到磁盘,可以通过系统变量 Innodb_flush_log_at_timeout=N设置,N默认为1,单位为秒;
  • 通过设置系统变量 innodb_flush_log_at_trx_commit 设置写⼊和刷盘策略,默认值为1
    • 0 :⽇志每秒写⼊系统缓冲区并刷新到磁盘,未写⼊系统缓冲区的事务⽇志可能会在MYSQL崩溃时丢失;
    • 1 :⽇志在每次事务提交时写⼊系统缓冲区并刷新到磁盘;
    • 2 :⽇志在每次事务提交后写⼊系统缓冲区并每秒⼀次刷新到磁盘,未刷新到磁盘的⽇志可能在系统崩溃时丢失。
    • 如果启⽤⼆进制⽇志且设置 sync_binlog = 1 时,则必须设置innodb_flush_log_at_trx_commit= 1
(2) 不同的刷盘策略有什么影响?

⾸先看⼀下Log Buffer、操作系统缓存和磁盘中⽇志⽂件的关系,如图所⽰:在这里插入图片描述

  • 这⾥主要讨论系统变量 innodb_flush_log_at_trx_commit 对应的⼏种情况:
    • 值为0时:表⽰⽇志每秒写⼊操作系统缓存并刷新到磁盘,如果MySQL崩溃,那么在⼀秒内没有写⼊操作系统缓存的Redo Log将会丢失;
    • 值为2时:⽇志在每次事务提交后写⼊系统缓冲区并每秒⼀次刷新到磁盘,此时已提交的事务Redo Log全部都写⼊了操作系统缓存,MySQL⽆论是否崩溃,Redo Log都会以指定的时间刷新到磁盘,但是如果服务器崩溃或断电,将会导致操作系统缓存中的Redo Log丢失;
    • 值为 1 时:⽇志在每次事务提交时写⼊系统缓冲区并刷新到磁盘,此时Redo Log从Log Buffer中写⼊操作系统缓存并⽴即刷新到磁盘,从⽽尽可能的保证⽇志的完整性,推荐使⽤。

8)Redo Log对应磁盘上的文件是什么?

分析过程

  • 重做⽇志⽂件位于数据⽬录下的 #innodb_redo ⽬录中在这里插入图片描述

  • 重做⽇志⽂件分为普通类型和备⽤类型,普通类型是正在使⽤的日志文件,备⽤是准备使⽤的日志文件,InnoDB 共维护 32 个重做⽇志⽂件,每个⽂件的大小等于1/32*innodb_redo_log_capacity

  • 重做⽇志⽂件使⽤ #ib_redoN 命名约定,其中 N 是重做日志文件编号,备⽤的重做日志文件使⽤ _tmp 为后缀。如下示例显⽰有1个活动(普通)重做日志文件和31个备⽤重做日志文件:在这里插入图片描述

  • 每个普通的重做日志文件都与⼀个特定的 LSN 取值范围相关联,⽤于崩溃恢时快速定位到要执行重做的⽇志,可以使⽤下⾯的查询显⽰活动重做日志文件的 START_LSNEND_LSN 值;在这里插入图片描述

解答问题

  • 重做⽇志⽂件位于数据目录下的 #innodb_redo 目录中,在MySQL8.0中InnoDB 共维护 32 个重做⽇志⽂件,每个文件的大小等于 1/32*innodb_redo_log_capacity
  • 重做⽇志⽂件分为普通类型和备用类型,并且使⽤ #ib_redoN 命名约定,其中 N 是重做日志文件编号,备⽤的重做日志文件使用 _tmp 为后缀
  • 重做⽇志的总容量可以通过系统变量 innodb_redo_log_capacity 设置,8.0.34版本开始最大为512GB,而8.0.34之前的版本是128GB。在这里插入图片描述
(1)这么多日志文件日志写到哪个文件中?
  • 通过查看 #innodb_redo ⽬录,可以看到系统⽣成了32个RedoLog⽂件,当RedoLog从内存刷到磁盘时,先从第⼀个⽇志⽂件开始写,第⼀个写满之后顺序写到第⼆个,以此类推;如果最后⼀个也写满了,就会重新从第⼀个⽂件开始写,也就是说重做⽇志⽂件可以循环使⽤,如图所示:在这里插入图片描述
  • 这⾥可能会出现⼀个问题,如果循环写⼊的话,那么后写⼊的⽇志会不会把之前写⼊的内容覆盖
    了?当然有这个可能,为了解决这个问题,InnoDB提出checkpoint的概念,关于checkpoint后⾯
    会详细介绍。
(2) 什么是LSN?
  • LSN是 Log Sequence Number 的简写,称为⽇志序号;
  • MySQL在运⾏期间,只要执⾏DML操作就会修改数据⻚,意味着会不断的⽣成RedoLog,InnoDB
    为了记录⽣成的⽇志总量(字节数),设计了⼀个只增不减的全局变量,这个全局变量就是LSN,起
    始值: 16*512 = 8192 ,最⼤值 2^64 - 1 ;
  • 当⼀个MTR所包含的⼀组RedoLog被记录在 Redo Log Block 中时,实际是保存在 Log
    Block Body 区域,但是在统计LSN增量时,如果MTR跨 Block 保存时,是按照实际写⼊的⽇
    志⼤⼩加上 Log Block Header 所占的12Byte)和块尾 Log Block Trailer 所占4Byte;
  • 示例:
    • a. 系统启动后初始化LogBuffer,buf_free指向第⼀个block偏移量为12Byte的位置,也就是
      block Header之后,此时LSN也会增加12,即8192 + 12 = 8204
    • b. 假设MTR_1中包含的⼀组RedoLog⼤⼩为200Byte,那么LSN就会在原来的基础上加200,即:
      8204 + 200 = 8404
    • c. 假设MTR_2中包含的⼀组RedoLog⼤⼩为1000Byte,这⾥当前的Block_1已经放不下这个
      MTR,于是⽇志顺序保存在后⾯的Block中,占满第⼆个Block后,直到使⽤了第三个Block的
      ⼀部分空间,⽇志保存完成;
    • 这时LSN不但要记录MTR_2中⽇志的总⼤1000Byte,还要记录Block_1+Block_2的Log Block
      Trailer和Block_2+Block_3的Log Block Header,总⼤⼩为:1000+122+42=1032,此
      时LSN的值为:8404+1032 = 9436,如下图所示:在这里插入图片描述

9)Redo Log日志文件的格式?

分析过程

(1) Log Buffer中的Redo Log Block与磁盘中的Redo Log Block有哪些不同?
  • 在内存中Log Buffer是⼀⽚连续的内存空间,被划分成了若⼲个 512 字节大小的 Redo Log Block ⽤来保存Redo Log,将Log Buffer中的Redo Log刷新到磁盘,本质就是把 Redo Log Block 的写⼊⽇志⽂件中,所以Redo Log对应的⽇志⽂件其实也是由若⼲个 512 字节⼤⼩的block 组成MySQL会根据配置⽣成⼀组撤销⽇志⽂件,每个⽂件的格式和⼤⼩都⼀样,由两部分组成:
    • 管理区:前 2048 个字节,也就是前4个block存储⼀些⽇志⽂件的管理信息
    • 数据区:从第2048字节往后是⽤来存储LogBuffer对应的 Redo Log Block
  • 也就是说真实的⽇志是从每个⽇志⽂件的第2048个字节开始写⼊,如图所示在这里插入图片描述
  • 所以Log Buffer中的Redo Log Block与磁盘中的Redo Log Block在结构上是相同的,只不过在磁盘上多了⽤于⽂件管理的⽂件头信息

解答问题

  • 磁盘中RedoLog的格式与内存中的格式相同,在内存中Log Buffer是⼀⽚连续的内存空间,被划分成了若⼲个 512 字节⼤⼩的 Redo Log Block ⽤来保存Redo Log,将Log Buffer中的Redo Log刷新到磁盘,本质就是把 Redo Log Block 的写⼊⽇志⽂件中,所以Redo Log对应的⽇志⽂件其实也是由若⼲个 512 字节⼤⼩的 block 组成,只不过在磁盘上多了⽤于⽂件管理的⽂件头信息。
(2)重做日志文件管理区包含哪些信息?
  • 关于 Redo Log Block :的结构与内存结构相同,前 2048字节 分为4个Block分别为:
    • LOG_CHECKPOINT_1 :第一个日志文件中日志头的第一个检查点信息
    • LOG_ENCRYPTION :日志文件头信息中的加密信息
    • LOG_CHECKPOINT_2 :第一个日志文件中日志头的第二个检查点信息
    • LOG_FILE_HDR_SIZE :日志文件头信息在这里插入图片描述
(3)管理区中具体管理了什么信息?
  • 管理区各字段中的信息随着MySQL版本迭代变化⾮常⼤,这⾥主要介绍⼀些关键信息
  • LOG_CHECKPOINT_1LOG_CHECKPOINT_2 :主要是记录CHECKPOINT操作时对应的
    LSN,LSN会交替写⼊到 LOG_CHECKPOINT_1LOG_CHECKPOINT_2
  • LOG_ENCRYPTIONLOG_FILE_HDR_SIZE 中的加密信息
  • LOG_FILE_HDR_SIZE :主要记录⽇志⽂件的⼀些信息,主要包括:
    • LOG_HEADER_FORMAT :占4字节,⽇志的格式标识,和MySQL版本相关,有重⼤更新的版
      本才设置相应的值,在MySQL5.7.9之前⼀直都是0
    • LOG_HEADER_START_LSN :占8字节,日志文件中第⼀个LSN编号
    • LOG_HEADER_CREATOR :占32字节,记录日志的创建者,正常生成的日志⼀般为"MEB"+MySQL的版本号,如果是运行 mysqlbackup 程序,在备份过程中⽣成的日志,则记录MySQL的版本号

10)什么是CHECKPOINT-检查点?

分析过程在这里插入图片描述

  • RedoLog从内存刷到磁盘上的⽇志⽂件使⽤循环写⼊的⽅式,也就是从第⼀个⽇志⽂件顺序写到最
    后⼀个⽇志⽂件,当最后⼀个⽇志⽂件写满时⼜重新写第⼀个⽇志⽂件,那么就可能出现⽇志被覆
    盖的情况,那么哪些⽇志可以被覆盖哪些不能被覆盖呢?
(1) 哪些RedoLog可以被覆盖?
  • ⾸先回顾⼀下RedoLog的作⽤,RedoLog是⽤作崩溃后恢复没有完成落盘的事务,也就是说当
    Buffer Pool中的脏⻚写⼊RedoLog,但数据⻚还没有落盘时发⽣的崩溃,当服务器重启之后可以根
    据RedoLog进⾏恢复,这也是RedoLog的应⽤时机,所以这种状态下的RedoLog不能被覆盖,如下
    图所示:在这里插入图片描述
  • 如果缓冲池中的脏⻚在记录RedoLog之后,也完成了真正的落盘操作,那么相应的RedoLog就没有
    ⽤了,所以这部分RedoLog就可以被覆盖,如下图所示:在这里插入图片描述
  • 经过分析可以看出,判断⽇志⽂件中的RedoLog是否可以覆盖的依据是它对应的数据⻚是否已经刷
    新到磁盘。
(2) 如何记录可以覆盖的⽇志⽂件位置?
  • 前⾯介绍过InnoDB使⽤LSN是来记录RedoLog总字节数,在这个基础上InnoDB采⽤⼀个全局变量
    checkpoint_lsn 来记录当前系统中可以被覆盖⽇志总量是多少,也就是说checkpoint_lsn 记录已落盘脏⻚对应的⽇志结束时LSN的值,此时LSN⼩于checkpoint_lsn 的RedoLog就可以被覆盖,如图所示:在这里插入图片描述
  • 当脏⻚刷新到磁盘之后,重新计算 checkpoint_lsn 的操作,称为⼀次 CHECKPOINT 操作,也可以说是重置⼀次检查点,系统会⽤⼀个 checkpoint_no 变量记录发⽣ CHECKPOINT 操作的次数,每做⼀ CHECKPOINT 操作 checkpoint_no 就会加1
  • 由于RedoLog⽂件的⼤⼩是固定的,在系统启动时已经分配好了对应的 Redo Log Block ,所以很容易就可以根据 checkpoint_lsn 计算写⼊位置在⽇志⽂件中的偏移量• 关于检查点相关的 checkpoint_nocheckpoint_lsn 以及写⼊偏移量的信息会被记录在第⼀个⽇志⽂件的管理区,同时InnoDB规定,当checkpoint_no的值是偶数时写到checkpoint1 中,是奇数时写到 checkpoint2 中。

解答问题

  • CHECKPOINT 也称为检查点,由于RedoLog⽂件是可以循环使⽤的,当最后⼀个⽂件写满时⼜会
    从第⼀个⽂件开始写⼊,这必将导致⽼的⽇志被覆盖, CHECKPOINT 是标记已被刷新到磁盘的脏
    ⻚刷对应的RedoLog可以被覆盖的⼀种操作,当⽇志的LSN⼩于已落盘脏⻚对应的LSN都可以被覆
    盖。
(3)如果没有小于 checkpoint_lsn 的⽇志时如何处理?
  • 如果⽇志⽂件中没有小于 checkpoint_lsn 的⽇志时,表明⽇志⽂件已经使⽤完了,这时原来的⽇志不能被覆盖,InnoDB会先优先刷新脏⻚到磁盘,再做 CHECKPOINT 操作,之后再继续进⾏⽇志记录。

11)重做⽇志还有哪些主要的配置项?

分析过程

  • 重做⽇志在磁盘上所占的空间可以通过系统变量 innodb_redo_log_capacity 控制,变量值以字节为单位,最⼤值 549755813888 ,表⽰ 512GB ,可以在选项文件或在运行时使用 SET GLOBAL 语句进行设置,如下所示:在这里插入图片描述

在这里插入图片描述在这里插入图片描述

  • 重做日志的目录可以通过系统变量 innodb_log_group_home_dir 进⾏设置,如果没有指定则日志⽂件位于数据目录的 #innodb_redo ⽬录中,如果定义了innodb_log_group_home_dir 变量,则日志文件存放在该目录下的 #innodb_redo ⽬录中;在这里插入图片描述
    解答问题
  • 根据实际应⽤场景通过配置对应的系统变量来指定 Redo Log 在磁盘上所占的空间的大、所在⽬录等属性。

12)如何查看重做⽇志的状态?

  • 通过状态变量 innodb_redo_log_capacity_resized 显⽰当前重做⽇志容量限制:在这里插入图片描述

  • 可以通过查询 performance_schema.innodb_redo_log_files 表来查看活动重做⽇志⽂件的信息在这里插入图片描述

  • 通过使⽤ SHOW ENGINE InnoDB STATUS 访问 InnoDB 标准监视器输出中 LOG 部分查看有关Redo Log的信息在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

13)如何根据RedoLog进行崩溃恢复?

分析过程

  • 在MySQL正常运⾏时,RedoLog不仅发挥不了它的作⽤⽽且还会对服务器的性能造成影响,但是服务器⼀旦崩溃,在重新启动时,就可以根据RedoLog中的记录把数据⻚恢复到崩溃前的状态
(1) 如何确定哪些⽇志需要恢复?
  • 前⾯我们介绍过每⼀次 CHECKPOINT 操作都会重新计算 checkpoint_lsn ,checkpoint_lsn 之前的⽇志表⽰已经被刷到磁盘数据⻚所⽣成的RedoLog,既然已被刷到磁盘,也就没有必要进⾏恢复,所以需要恢复的是 checkpoint_lsn 之后的⽇志
(2) 如何获取最新的 checkpoint_lsn 和恢复的起点?
  • RedoLog⽂件组中的第⼀个⽂件的管理信息中有两个block checkpoint1 和 checkpoint2 ,其中都存储了 checkpoint_lsn 和 checkpoint_no 信息,每次做 CHECKPOINT 操作时,会在这两个block中交替写⼊ CHECKPOINT 信息,只要需要把这两个block中保存的checkpoint_no 值⽐较⼀下,哪个值⼤就表示哪个block存储的就是最近的⼀次checkpoint信息。这样我们就能拿到最近发⽣的 checkpoint 对应的 checkpoint_lsn 值以及它在RedoLog⽂件组中的偏移量 checkpoint_offset 。
(3) 如何确认恢复的终点?
  • 我们⽤之前已经掌握的内容分析⼀下这个问题,⾸先RedoLog是顺序写⼊的,当⼀个block写满了之后再写下⼀个,⽽每⼀个block的 log block header 中都有⼀个名为LOG_BLOCK_HDR_DATA_LEN 的属性,该属性记录了当前block使⽤了多少字节,对于写满的block来说,该值⼀定是 512 ,所以找到第⼀个 LOG_BLOCK_HDR_DATA_LEN 的值不为512,就可以确定恢复扫描的最后⼀个block,这个block中的最后⼀条⽇志就是恢复的终点。
(4) 如何进⾏恢复?
  • 确定了需要扫描哪些⽇志进⾏崩溃恢复之后,接下来就是怎么进⾏恢复了,假设现在的⽇志⽂件中
    有RedoLog,如图所示:在这里插入图片描述
  • 第⼀条⽇志在 checkpoint_lsn 之前,表⽰已经落盘不⽤恢复;
  • checkpoint_lsn 之后的⽇志可以通过顺序扫描的⽅式,根据⽇志记录的内容依次恢复对应的数据⻚
  • InnoDB在顺序读取⽇志进⾏恢复的过程中采⽤了⼀些优化措施:⾸先根据⽇志的 Space Id 和Page No 计算出散列值,以这个散列值为 KEY ,把 Space Id 和 Page No 相同的⽇志放到哈希表的同⼀个槽⾥,如果有多个 Space Id 和 Page No 相同的⽇志,那么按照⽇志⽣成的先后顺序使⽤链表连接起来,如下图所示:
    在这里插入图片描述
(5)如何确定哪些⽇志在崩溃前已经落盘?
  • checkpoint_lsn 之后的⽇志有可能就根本没有落盘,也有可能已经落盘但没有来的及做CHECKPOINT ,在恢复时如何区分呢?
  • 在⻚结构章节介绍过,磁盘上的每个⻚都包含⼀个 File Header 信息,其中⼜包含已被刷到磁
    盘的LSN: FIL_PAGE_FILE_FLUSH_LSN 信息,在恢复时就可以通过当前⽇志对应的LSN与
    FIL_PAGE_FILE_FLUSH_LSN 进⾏⽐较,如果⽇志的LSN⼩于等于已刷新到磁盘的LSN,那就证明⽇志对应的数据在崩溃之前已经落盘,直接跳过即可

解答问题
恢复的过程主要分为以下几步:

  1. 通过 checkpoint_lsn 和第⼀个没有写满的⽇志⻚确定需要恢复⽇志的起始和结束位置;
  2. 遍历⽇志并把Space Id 和 Page No相同的⽇志组织在⼀起,以便⼀次性恢复完相应数据⻚的所有内容;
  3. ⽇志的LSN⼩于磁盘数据⻚⽂件记录的已刷新LSN时,表示这些数据在崩溃之前已落盘,跳过即可

四.附录

1.通⽤表空间 - General Tablespace

在这里插入图片描述

1)通⽤表空间的作⽤和特性?

解答问题

  • 通⽤表空间是使⽤ CREATE tablespace 语法创建的共享InnoDB表空间
  • 通⽤表空间能够存储多个表的数据,与系统表空间类似也是共享表空间;
  • 服务器运⾏时会把表空间元数据保存在内存中,在表的数量相同的情况下,通⽤表空间⽐独⽴表空
    间的数量更少,所以消耗的内存也就更少;
  • 数据⽂件可以放置在数据⽬录或数据⽬录之外的其他位置,对于单独管理关键表⾮常有⽤;
  • ⽀持所有的表格式和⾏格式的相关特性;

2)怎么创建通⽤表空间?

分析过程

  • 创建通⽤表空间可以使⽤ CREATE TABLESPACE 语法。在这里插入图片描述

  • 注意: tablespace_name 表空间名区分大小写

    解答问题

  • 创建通用表空间可以使用 CREATE TABLESPACE 语法,与创建表类似,语句里用TABLESPACE 关键字指明创建的是表空间。

(1)创建通⽤表空间的⽰例
  • 示例:在 data ⽬录下创建通⽤表空间
    在这里插入图片描述在这里插入图片描述

  • ADD DATAFILE ⼦句在MySQL 8.0.14及以后的版本是可选的,之前是必需的。如果没有指定
    ADD DATAFILE ⼦句,则⾃动创建⼀个以 UUID 为⽂件名的表空间数据⽂件,通⽤表空间数据⽂件以 .ibd 为扩展名。在这里插入图片描述

(2)创建通⽤表空间时要注意什么?
  • 可以在数据⽬录中创建通⽤表空间,也可以在数据⽬录之外创建通⽤表空间。为避免与隐式创建的
    独⽴表⽂件表空间冲突,不⽀持在data⽬录的⼦⽬录中创建通⽤表空间。当在数据⽬录之外创建通
    ⽤表空间时,该⽬录必须存在,并且必须在创建表空间之前让InnoDB识别,要使⽤⾃定义的⽬录
    可以通过系统 innodb_directories 指定。 Innodb_directories 是⼀个只读启动选项,配置后需要重新启动服务器。
  • Innodb_directories 默认值是 NULL ,同时 innodb_data_home_dir ,innodb_undo_directory 和 datadir 定义的⽬录会被附加到 innodb_directories 参数值中,在InnoDB启动时会⾃动被识别(包括⼦⽬录),⼿动指定⽬录的⽅式,如下所示:在这里插入图片描述
  • 示例:不能在数据⽬录的⼦⽬录下创建通⽤表空间在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

InnoDB不是默认存储引擎的情况下,必须指定 ENGINE = InnoDB ⼦句

3)如何向通⽤表空间中添加表?

前置知识

  • 创建好通⽤表空间之后就可以使⽤了,前⾯介绍了通⽤表空间能够存储多个表的数据,接下来介绍
    如何向通⽤表空间中添加表
    分析过程
  • 示例:向通⽤表空间中添加表,在创建表时使⽤ TABLESPACE ⼦句指定通⽤表空间即可在这里插入图片描述在这里插入图片描述

解答问题
⾸先创建通⽤表空间,之后使⽤ CREATE 语句创建表时通过 TABLESPACE ⼦句指定通⽤表空间,语
句执⾏成功后即在指定的通⽤表空间下创建了表

4)怎么删除通⽤表空间?

分析过程

  • DROP TABLESPACE 语句⽤于删除⼀个InnoDB通⽤表空间,在删除通⽤表空间之前,必须将所
    有表从表空间中删除,如果表空间不为空,将返回错误。查询通⽤表空间中的表,可以使⽤下⾯的
    语句在这里插入图片描述

  • 示例:⼀个完整的通⽤表空间删除流程在这里插入图片描述在这里插入图片描述

解答问题

  • 可以使⽤ DROP TABLESPACE 语句⽤于删除⼀个通⽤表空间,与删除表类似,语句⾥⽤TABLESPACE 关键字指明删除的是表空间

5)使⽤通⽤表空间时要注意什么?

  • 使用 TRUNCATE 或 DROP 语句截断或删除表时,通用表空间的空闲容量并不会释放,并且只能用于新的InnoDB表;
  • 通用表空间不属于任何数据库,使用 DROP DATABASE 操作数据库和属于该数据库所有的表时,并不会删除通用表空间。
  • tablespace_name 表空间名区分大小写

2. 临时表空间 - Temporary Tablespaces

在这里插入图片描述

1)什么是临时表?

解答问题

  • 临时表存储的是临时数据,不能永久的存储数据,⼀般在复杂的查询或计算过程中⽤来存储过渡的
    中间结果,MySQL在执⾏查询与计算的过程中会⾃动⽣成临时表,⽐如表连接查询时得到的结果集
    就是⼀张临时表,因为结果中可能包含多个表中的字段并没有⼀张真实的表与之完全对应。
(1) 除了系统⾃动创建的临时表,可以手动创建临时表吗?
  • 用户可以通过使 用 CREATE TEMPORARY TABLE 语句⼿动创建临时表
  • 用户创建的临时表也称为外部临时表;MySQL在执行查询与计算的过程中自动生成的临时表称为内部临时表。

2) 什么是外部临时表?

分析过程

  • 使⽤ CREATE TEMPORARY TABLE 语句创建的临时表是外部临时表
    在这里插入图片描述在这里插入图片描述

  • 通过 INNODB_TEMP_TABLE_INFO 查询临时表元数据在这里插入图片描述

  • TEMPORARY 表只在当前会话中可⻅,并且在会话关闭时⾃动删除。这意味着两个不同的会话可以使⽤相同的临时表名,⽽不会相互冲突,临时表也不会与已有的⾮临时表名冲突,如果创建了与现有表同名的临时表,则现有表被隐藏,直到临时表被删除。在这里插入图片描述在这里插入图片描述

  • 重启MySQL服务器后,再次查询临时表信息,得到空集合在这里插入图片描述
    解答问题

  • 使⽤ CREATE TEMPORARY TABLE 语句创建的临时表是外部临时表,表只在当前会话中可⻅,
    并且在会话关闭时⾃动删除

3)什么是内部临时表?

分析过程

  • 由服务器自动创建的临时表是内部临时表
  • 服务器在以下情况会自动创建临时表,这个过程用户不能直接控制:
    • 使用 UNION 语句合并查询结果
    • 对视图时的⼀些操作,比如使用 UNION 或聚合函数
    • 使用⼦查询
    • 使用 DISTINCT 和 ORDER BY 的查询可能需要⼀个临时表
    • 使用 INSERT…SELECT 语句向表中写⼊数据时,需要先用⼀个内部临时表来保存 SELECT 语句查询出来的行,然后将这些行插入到目标表中
    • 使用 COUNT(DISTINCT) 和 GROUP_CONCAT() 表达式时
    • 使用窗⼝函数时

解答问题

  • 由服务器⾃动创建的临时表是内部临时表,通常MySQL在执⾏查询与计算的过程中会⾃动⽣成的内
    部临时表

4)临时表都有哪些设置?

分析过程

  • 系统变量 internal_tmp_mem_storage_engine 用于指定内存中内部临时表的存储引擎,值为 TempTable (默认值)或 MEMORY
  • TempTable 存储引擎为 VARCHARVARBINARY 列以及其他⼆进制⼤对象类型进⾏了优化;
  • 从MySQL 8.0.28开始 tmp_table_size 定义了由 TempTable 存储引擎创建的单个内部临时表允许使⽤内存的最⼤值,当达到 tmp_table_size 限制时,MySQL⾃动将内存中的内部临时表转换为磁盘上的InnoDB内部临时表。 tmp_table_size 的默认值是 16MB ;
  • 系统变量 temptable_max_ram 定义 TempTable 存储引擎创建的所有临时表可以使⽤的最⼤内存,默认为 1GB ,超出限制后将内存中的内部临时表转换为磁盘上内部临时表;
  • 当内存临时表使⽤内存存储引擎 internal_tmp_mem_storage_engine=MEMORY 时,系统变量 max_heap_table_size 可以限制内存内部临时表的最大行数,默认 16777216 ,
  • 内存存储引擎临时表变得太大,MySQL会⾃动将其转换为磁盘上的临时表,内存中临时表的大小由 tmp_table_sizemax_heap_table_size 这两个系统变量中最小的值决定。

解答问题

  • 通过配置对应的系统变量来指定临时表使⽤的存储引擎、使⽤内存的大小、表中的最大行数等选
    项。

5)临时表中的数据存在哪⾥?

分析过程

  • 磁盘上的临时表数据存储在临时表空间中,MySQL8.0版本中磁盘上的临时表存储引擎⽀持
    InnoDB ,分为两种类型分别是:
    • 会话临时表空间( session temporary tablespaces )
    • 全局临时表空间( global temporary tablespace )。
(1) 会话临时表空间的作用?
  • 磁盘上的会话临时表空间存储由用户创建的外部临时表和优化器创建的内部临时表;
(2) 会话临时表空间的数据存在哪里?
  • 当MySQL接收到第⼀个创建磁盘临时表的请求时,从临时表空间池中分配会话临时表空间;⼀个会
    话最多分配两个表空间,⼀个⽤于用户创建的临时表,另⼀个⽤于优化器创建的内部临时表。会话的临时表空间⽤于存储会话创建的所有磁盘临时表,当会话断开连接时,临时表空间将被截断并释放回池中;

  • 服务器启动时会创建⼀个包含 10 个临时表空间的临时表空间池,表空间会根据需要⾃动添加到池
    中,临时表空间池在MySQL正常关闭或中⽌初始化时被删除;

  • 会话临时表空间⽂件扩展名为 .ibt ;

  • 系统变量 innodb_temp_tablespaces_dir 可以指定会话临时表空间的位置。默认数据⽬录下的 #innodb_temp ⽬录(开头的 # 号是为了避免与数据库⽬录命名冲突),如果⽆法创建临时表空间池,服务器则拒绝启动;在这里插入图片描述在这里插入图片描述

(3) 全局临时表空间的作⽤?
  • 全局临时表空间存储对用户创建的临时表所做的更改,以便以后回滚操作
(4) 全局临时表空间的数据存在哪⾥?
  • 系统变量 innodb_temp_data_file_path 指定了全局临时表空间数据文件的相对路径、名称、大小和属性。如果没有指定,则默认在系统表空间⽬录(系统变量innodb_data_home_dir 指定的⽬录)中创建,默认名为 ibtmp1 ,初始⽂件大小略大于12MB ;在这里插入图片描述

  • 全局临时表空间在正常关闭或中⽌初始化时被删除,并在每次启动服务器时重新创建,如果⽆法创
    建全局临时表空间,则拒绝启动;如果服务器意外停⽌,重启服务器时会⾃动删除并重新创建全局
    临时表空间。

解答问题

  • 磁盘上的临时表数据存储在临时表空间中,临时表空间分为两种分别是:
    • 会话临时表空间( session temporary tablespaces ),默认数据⽬录下的#innodb_temp ⽬录中
    • 全局临时表空间( global temporary tablespace ),默认在数据⽬录下中创建,名为ibtmp1

6)怎么查看全局临时表空间的信息和大小?

分析过程

  • 可以通过 INFORMATION_SCHEMA.FILES 查看全局临时表空间的元数据:在这里插入图片描述在这里插入图片描述

  • 要检查全局临时表空间数据文件的大小,可以查询 INFORMATION_SCHEMA.FILES 中的具体字
    在这里插入图片描述

  • 默认情况下,全局临时表空间数据⽂件会⾃动扩展并根据需要增加大小,要确定全局临时表空间数据文件是否自动扩展,可以检查 innodb_temp_data_file_path 变更设置:在这里插入图片描述

解答问题

  • 可以通过 INFORMATION_SCHEMA.FILES 查看全局临时表空间的元数据
(1)全局临时表空间数据文件的大小可以设置吗?
  • 可以通过系统变量 innodb_temp_data_file_path 指定最大文件大小,并重新启动服务器,语法与配置系统表空间文件相同在这里插入图片描述
http://www.dtcms.com/a/351441.html

相关文章:

  • 从混乱到高效:企业如何构建可持续发展的IT工单系统
  • 清分系统在电商中的一些案例
  • nginx-负载均衡
  • GeoServer与GISBox:地理数据服务器对比解析
  • 【STM32】CubeMX(十三):RT-THREAD
  • 脑电分析——学习笔记
  • 常用测试有哪些
  • Spring Boot 集成 Docker 构建与发版完整指南
  • [docker]Failed to initialize NVML: Unknown Error
  • 【C++】用哈希表封装实现unordered_set和unordered_map
  • 深入剖析悲观锁、乐观锁与分布式锁
  • 如何才能使RISC V架构成为机器学习的核心
  • U-Net图像语义分割中梯度下降的直观解释
  • 动态规划:为什么暴力算法会有重复子问题
  • 深度学习自动驾驶BEV【专业名词解释汇总】
  • VS中创建Linux项目
  • Tomcat的VM options
  • 我在TSX开发中的实用指南:从基础到实战
  • Java大厂面试实战:从Spring Boot到微服务架构的全链路技术解析
  • swift 开发抠图工具实现思路,与代码详解
  • Java全栈开发面试实录:从基础到实战的深度解析
  • Nginx如何实现反向代理和负载均衡器等功能的
  • 要闻集锦|阿里官网调整为四大业务板块;华为云重组多个事业部涉及上千人;群核科技在港交所更新招股书
  • Swift高阶函数-contains、allSatisfy、reversed、lexicographicallyPrecedes
  • 【大前端】实现一个前端埋点SDK,并封装成NPM包
  • 如何避免频繁切换npm源
  • Redis相关命令详解及其原理
  • AI在提升阅读效率的同时,如何加强理解深度?
  • 嵌入式(day34) http协议
  • 使用Java对接印度股票市场API开发指南