【MySQL】InnoDB架构

农民  论坛元老 | 2024-9-30 19:51:43 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 1021|帖子 1021|积分 3063

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本文MySQL版本是8.X版本
  这是官方文档给出来的架构图:MySQL :: MySQL 8.0 Reference Manual :: 17.4 InnoDB Architecture

可以看出,团体上是分成两部门的:内存布局(提高服从)和磁盘布局(数据长期化),下面将把每个区域都大抵做一个先容。

内存布局

缓冲池(Buffer Pool)

缓冲池是用来缓存各种数据,最主要的就是缓存 从磁盘加载的 数据页,当数据库请求数据时,InnoDB首先查看缓冲池中是否存在所需的数据,如果存在,则直接从内存中读取,从而大幅提高读取速率。
布局



Instances

缓冲池至少有一个Instances实例对象。内存操纵都是在Instances中进行的。
Instances的优点


  • 提升并发性能: 将Buffer Pool分成多个实例可以减少锁竞争,从而提高并发读取的服从。不同的连接可以并行地访问不同的Buffer Pool实例,减少了单一全局锁的压力。
  • 优化内存管理: 每个Buffer Pool实例可以配置不同的巨细,使得可以更好地控制内存的使用和分配。这对于大内存体系特别有效,可以有效地分配和管理大量的内存。

Instances数目相关


  • 通过体系变量 innodb_buffer_pool_instances 可以设置缓冲池实例的个数,默认是 1,最大值为 64。
  • 当缓冲池的巨细小于 1GB 时,无论指定 innodb_buffer_pool_instances 数是多少都会主动调整为 1。
  • 当缓冲池的巨细大于即是 1GB 时,默认的 innodb_buffer_pool_instances 值为 8,也可以指定大于 8的值来设置实例的数目。增加多个实例可以提升服务器的并发性能。
  • 为了获得最佳服从,建议通过指定 innodb_buffer_pool_instances 和 innodb_buffer_pool_size,为每个缓冲池实例设置至少 1GB 的空间。
    1. # 查看instances的个数
    2. show variables like "innodb_buffer_pool_instances";
    复制代码


Chunk

每个Instances实例中至少有一个Chunk块。每个Chunk中管理的是多少从磁盘加载到内存的Page数据页。
Chunk的优点
Chunk是在服务器运行状态下动态调整缓冲池巨细时的操纵单元。为了避免在调整巨细过程中复制全部缓冲池中的数据页,调整操纵以“块”为基本单元执行。

Chunk数目相关


  • Chunk巨细可以通过体系变量innodb_buffer_pool_chunk_size进行设置,默认为134217728字节即128MB。在设置巨细时,可以以1048576字节即1MB为单元增加或减少。
  • 更改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_chunk_size * innodb_buffer_pool_instances的值。


Page

数据页/缓冲页,就是磁盘中数据页的数据布局,读到内存后与磁盘中的对应。
页与页怎样连接
InnoDB中为了实现数据页在内存中的链表连接,界说了一个称为"控制块"(control block)的数据布局,此中包罗三个告急的信息:指向数据页的内存地点  前一个控制块的内存地点  后一个控制块的内存地点
为了确定控制块链表的起始位置,InnoDB专门界说了一个头节点,头节点中包罗了三个主要的信息:第一个控制块的内存地点  最后一个控制块的内存地点  链表中控制块的数目


控制块和页初始化
①在缓冲池初始化过程中,会为每个Chunk(内存块)分配内存空间。控制块会从Chunk的内存空间的左侧向右侧进行初始化,而数据页所占的内存则从右侧向左侧初始化。这意味着控制块的内存空间分配优先于数据页的分配。缓冲池在初始化过程中,剩余的内存空间无法容纳一个完整的控制块和其对应的数据页,就会产生碎片化的空间。

②在内存初始化完成后,建立了控制块与缓冲数据页之间的关系。

③后面从磁盘加载数据页是,直接缓存到缓冲页中即可。

页的管理
当磁盘中的页缓存到数据页之后,如果对于页有 删除、修改操纵,此时就是操纵缓存的页。
缓冲池使用三个链表来维护缓冲页,这三个链表代表页在内存的三种状态。

Free List:管理未被使用的内存页,即空闲页。当执行查询操纵时,如果所需的页已经在缓冲池中,则直接返回数据。如果不在缓冲池中且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中的页被修改后(即成为脏页),会将其加入Flush List。Flush List专门用来管理需要被写回到磁盘的脏页。数据库通过刷盘机制将Flush List中的脏页刷回磁盘,以确保数据的长期性和同等性。刷盘操纵完成后,将脏页的空间开释,并返回给Free List。
   缓冲池使用LRU算法管理链表,当有新页面添加到缓冲池时,近来最少使用的页面将被淘汰,并将新页面添加到列表的中间。这种中点插入策略将列表视为两个子列表:
  链表头部,是存放近来访问的新页面(年轻页面)子列表;
链表尾部,是存放近来较少访问的旧页体面列表。
  

  经常使用的页面保存在新子列表中,较少使用的页面保存在旧子列表中。随着时间的推移,旧子列表中的页面将会逐渐被淘汰。默认情况下,算法的执行过程如下:
  缓冲池总容量的 5/8 用于新子列表,3/8 用于旧子列表;
列表的中间插入点是新子列表的尾部与旧子列表头部的交界;
当一个页面被读入缓冲池时,首先插入到中点做为旧子列表的头节点;
当访问的页面在旧子列表中时,将被访问的页面移动到新子列表的头部,使其成为 "新" 页面;
数据库运行的过程中,缓冲池中被访问页面的位置不停更新,未访问的页面向列表的尾部移动,从而逐渐"变老",最终超出缓冲池容量的页面从旧子列表的尾部被淘汰。
  
缓冲池巨细

  1. show variables like "innodb_buffer_pool_size";
  2. #单位是字节 128M
复制代码

InnoDB 会为控制块额外分配内存空间,因此实际分配的内存总空间比指定的缓冲池巨细约莫多出 10%。
增大缓冲池巨细可以显著减少多次访问雷同表数据时的磁盘 I/O 操纵,由于数据已缓存在内存中,提高数据库的团体服从。不过,增大缓冲池巨细可能会导致服务器在启动时需要更长的初始化时间。

缓冲池信息

  1. show engine innodb status\G
复制代码


变更缓冲区(Change Buffer)

变更缓冲区用来缓存对二级索引数据的修改,是一个特殊的数据布局。当使用 INSERT、UPDATE 或 DELETE 语句修改二级索引对应的数据时,如果对应的数据页在缓冲池中,则直接进行更新。如果数据页不在缓冲池中,修改操纵则会被缓存到变更缓冲区。如许就避免了立刻从磁盘读取对应的数据页,而是比及未来的读操纵将数据页加载到缓冲池时,变更缓冲区中的修改操纵可以批量合并到缓冲池中,从而减少磁盘 I/O 的次数,提升体系性能。

合并修改执行时机:
读取对应的数据页时: 当体系需要读取某个数据页时,如果这个页上有变更缓冲区的待合并修改操纵,体系会先将变更缓冲区的修改合并到数据页中,然后再将数据页加载到缓冲池中。上图情况。
体系空闲或 Slow Shutdown 时: 在体系空闲或者进行缓慢关闭(Slow Shutdown)时,MySQL的主线程会启动合并操纵,将变更缓冲区的修改批量合并到对应的数据页,以减少后续读取时的磁盘 I/O 操纵。
Change buffer 的内存空间即将耗尽时: 如果变更缓冲区的内存空间即将用尽,MySQL会触发合并操纵,将缓冲区中的修改写入到对应的数据页中,开释变更缓冲区的内存空间。
Redo Log 写满时: Redo Log 是MySQL中用于长期化记录事件修改的告急组成部门。如果Redo Log写满,MySQL可能会停息事件处置惩罚并等待写入完成。此时,变更缓冲区的修改可能会被迫立刻写入数据页,以开释Redo Log空间并确保事件长期性。

为什么是二级索引?
由于聚集索引具有唯一性,这意味着每个主键值只能存在一次。考虑以下情况:假设表中有一个主键(ID),现在有两条 INSERT 语句尝试插入雷同的主键值(比方,id=1)。如果这两个操纵都被放入变更缓存中,待合并到缓冲池时,会出现重复的主键值。这违反了聚集索引的唯一性约束,因此聚集索引的修改不能简单地放入变更缓存中。
与聚集索引不同,二级索引通常不具有唯一性,并且它们的数据分布相对随机。对于二级索引的插入、删除和更新操纵,可能会涉及到不相邻的索引页。如果每次操纵都需要直接从磁盘读取数据,会导致大量的随机 I/O 操纵,影响体系性能。因此,通过将这些修改操纵暂时缓存到变更缓存区,待真正读取数据时再将修改合并到缓冲池中,可以显著提升服从,减少随机 I/O 的次数。

修改变更缓冲区

1. 缓冲范例和控制
在修改二级索引数据时,变更缓冲区可以显著减少磁盘I/O操纵,从而提高数据库的服从。然而,变更缓冲区占用了缓冲池的一部门空间,可能会影响可用于缓存数据页的内存。在业务场景中,如果读操纵远多于写操纵,或者表中二级索引较少,考虑禁用变更缓冲区可能有助于提高缓冲池的可用空间。
可以通过选项文件或使用 SET GLOBAL 语句来配置体系变量 innodb_change_buffering,以控制变更缓冲区对插入、删除操纵(索引记录标志删除)、清除操纵(索引记录物理删除)的开启或禁用:
all:默认值,缓存插入、删除标志和清除操纵。
none:不缓存任何操纵。
inserts:仅缓存插入操纵。
deletes:仅缓存删除标志操纵。
changes:缓存插入和删除标志操纵。
purges:缓存后台执行的物理删除操纵。

2. 更改缓冲区的最大巨细
通过体系变量 innodb_change_buffer_max_size 可以设置变更缓冲区的最大巨细,默认为 25,最大可设置为 50,表现变更缓冲区占用缓冲池总内存的百分比。
在大量插入、更新和删除操纵频仍的业务场景中,考虑增加 innodb_change_buffer_max_size 的值。
在读多写少的场景中,比方静态数据用于报表的场景,可以考虑减小 innodb_change_buffer_max_size 的值。
需要留意的是,如果变更缓冲区占用了过多的缓冲池内存空间,可能会导致缓冲池中的数据页更快被淘汰。

缓冲区信息

  1. show engine innodb status\G
复制代码


自顺应哈希索引(Adaptive Hash Index)

自顺应哈希索引使得InnoDB存储引擎在不捐躯事件特性、可靠性和缓冲池空间的前提下提升服从,使其更像是一个内存数据库。
哈希索引根据经常访问的索引页主动构建。根据InnoDB内部的监控机制,如果监测到某些查询可以通过建立哈希索引提高性能,则会主动对这些页创建哈希索引。这个过程被称为自顺应哈希索引。
当表完全放在内存中时,哈希索引可以通过直接查找任何元素来加快查询速率。
查询条件为key,页地点为value
自顺应哈希索引(AHI)会占用缓冲池的一部门内存区域,并在缓冲池初始化后进行初始化。为了减少AHI对锁竞争的压力,AHI 支持分区。你可以通过设置 innodb_adaptive_hash_index_parts 参数来配置分区的个数,默认值为8。
通过设置体系变量 innodb_adaptive_hash_index 可以开启或关闭自顺应哈希索引:
在选项文件中设置 innodb_adaptive_hash_index=[1|0] 可以实现开启或关闭。
通过下令行选项 --skip-innodb-adaptive-hash-index 也可以关闭自顺应哈希索引。

每个自顺应哈希索引被绑定到不同的分区中,每个分区有不同的锁保护。分区的数目由体系变量 innodb_adaptive_hash_index_parts 控制,默认值为 8,最大可配置为 512。

日志缓冲区(Log Buffer)

日志缓冲区是在服务器启动时向操纵体系申请的一片一连的内存区域,用来存储即将要写入磁盘日志文件的数据。
在进行数据库的数据操纵语言(DML)操纵时,InnoDB会记录这些操纵的日志,比方为了保证数据完整性而实现的重做日志(Redo Log)。这些日志首先会写入日志缓冲区(Log Buffer)。如许做可以办理由于同步写磁盘而导致的性能题目,由于将日志首先写入内存中的日志缓冲区通常比直接写入磁盘要快速。
随后,根据不同的落盘策略(比方 InnoDB 的 Checkpointing 过程),最终会将日志从日志缓冲区写入到磁盘中的日志文件,确保数据在长期化存储中的安全性和完整性。

磁盘布局

体系表空间(System Tablespace)

作用
存储体系表和数据字典:体系表空间存储了MySQL中全部体系表的数据,包罗数据字典。这些数据对于数据库的正常运行和元数据管理至关告急。
变更缓冲区存储: 当数据库服务器关闭时,体系表空间还保存了没有合并到缓冲池的二级索引修改操纵。这些修改在下次数据库启动时将被应用。
在MySQL 8.0.20之前的版本中,体系表空间还包罗了双写缓冲区。从MySQL 8.0.20开始,双写缓冲区已经被移出体系表空间,而是存储在一个单独的文件中。

配置选项
体系表空间可以对应一个或多个数据文件。默认情况下,MySQL在 data 目录中创建一个体系表空间数据文件 ibdata1。体系表空间数据文件的巨细和数目由 innodb_data_file_path 启动选项界说。以是我们可以从这个选下下手来修改配置。
   在修改体系表空间配置时,先制止MySQL服务,修改完成后,再重新启动MySQL服务之后生效。
  查看文件
  1. show variables like "%innodb_data_file_path%";
复制代码


file_name:file_size[:autoextend[:max:max_file_size]]
file_name: 文件名,用于标识数据文件。
file_size: 文件巨细,以 K、M 或 G 为单元指定。如果以 K 为单元,必须是 1024 的倍数;否则,四舍五入到最接近的 M(兆字节),且至少为 12MB。
autoextend: 可选属性,指定命据文件是否主动扩展。默认情况下,每次增加 64MB。可以通过体系变量 innodb_autoextend_increment 控制增量巨细。
max: 可选属性,仅适用于最后指定的数据文件,指定命据文件的最大容量。比方,max:500M 表现文件最大可以扩展到 500MB。
示例配置:
   # mysqld节点
[mysqld]
# 文件1名称为:ibdata1,巨细为50M
# 文件2名称为:ibdata2,巨细为50M,主动扩容
innodb_data_file_path=ibdata1:50M;ibdata2:50M:autoextend
  配置体系表空间文件路径: 
   # mysqld节点
[mysqld]
# 指定innodb数据目录
innodb_data_home_dir = /myibdata/
# 配置体系表空间
innodb_data_file_path=ibdata1:50M:autoextend
  留意事项:
指定 innodb_data_home_dir 时,路径必须以斜杠 / 结尾,MySQL 不会主动创建目录,需确保目录存在
如果 innodb_data_file_path 指定了绝对路径,则不会使用 innodb_data_home_dir 的值。
在添加新的数据文件时,不要指定已存在的文件名,InnoDB 在启动服务器时会主动创建并初始化新的数据文件。

独立表空间(File-Per-Table Tablespace)

作用
File-Per-Table 表空间是 InnoDB 存储引擎的一种设置,默认情况下,它将每张表的数据和索引存储在单独的数据文件中,这些文件被称为表空间数据文件。这种配置方式使得每个表的数据管理更为灵活和独立,有助于提高维护性和性能调整的精度。
配置选项
默认情况下,每张表都对应一个独立的表空间数据文件。这种设置可以通过体系变量 innodb_file_per_table 控制,该变量的值可以是 ON 或 OFF,用来决定是否为每张表生成独立的表空间文件。
选项文件设置:
在 MySQL 的选项文件(如 my.cnf)中,可以通过配置 [mysqld] 节点来设置 innodb_file_per_table 的值。比方:
   [mysqld]
innodb_file_per_table=ON
  这表现开启 File-Per-Table 表空间,每张表都有自己的表空间数据文件。

运行时设置:
可以通过 MySQL 的 SET GLOBAL 语句在运行时修改 innodb_file_per_table 的值。比方:
   mysql> SET GLOBAL innodb_file_per_table=ON;
  这会立刻将体系配置切换为使用 File-Per-Table 表空间。
优缺点
优点


  • 磁盘空间接纳:使用 TRUNCATE 或 DROP 表时,File-Per-Table 表空间会将磁盘空间返回给操纵体系,提高磁盘使用率。而共享表空间(如 System Tablespace)则无法接纳磁盘空间。
  • 性能优化:执行 TRUNCATE TABLE 操纵时,File-Per-Table 表空间通常有更好的性能表现,由于它不需要处置惩罚共享表空间的复杂性。
  • 灵活的存储位置:可以在其他目录或独立存储装备上创建 File-Per-Table 表空间文件,以达到 I/O 优化、空间管理或备份等目标。可以通过 DATA DIRECTORY 子句指定表的数据目录,使表数据存储在外部目录中。
  • 支持动态和压缩行格式:File-Per-Table 表空间支持更多的行格式选项,如 DYNAMIC 和 COMPRESSED,而共享表空间不支持这些选项。
  • 数据恢复和备份:在发生数据损坏、备份不可用或 MySQL 服务器实例无法重新启动时,File-Per-Table 表空间可以提高乐成恢复的机会。
  • 大表支持:单个表的容量限定为 64TB,相比之下,共享表空间中的表总容量也是 64TB,但 File-Per-Table 可以更灵活地管理单个表的大容量数据。
缺点:


  • 未使用空间管理:每个表可能存在未使用的空间,这些空间只能由对应的表使用,可能导致空间浪费,特别是管理不其时。
  • 文件描述符和性能:每个表有自己的数据文件,操纵体系需要维护更多的文件描述符。当表数目许多时,可能会对性能产生一定影响。
  • 磁盘碎片题目:可能会导致更多的磁盘碎片,特别是在执行 DROP TABLE 操纵时,可能会影响性能。
  • 主动扩展限定:对于 File-Per-Table 表空间文件,体系变量 innodb_autoextend_increment 界说的主动扩展增量不起作用。File-Per-Table 表空间文件会始终主动扩展,初始巨细由表界说决定,之后以 4MB 为增量进行扩容。

通用表空间(General Tablespace)

通用表空间(General Tablespace)是MySQL 8.0 引入的一种表空间范例,它与传统的 File-Per-Table 表空间有所不同。通用表空间允许将多个表存储在单个共享表空间文件中,相比之下,File-Per-Table 表空间每个表有自己的数据文件。

暂时表空间(Temporary Tablespace)

暂时表存储暂时数据,通常用于复杂查询或计算过程中存储过渡的中间效果。MySQL在执行查询和计算时会主动生成暂时表,比方在表连接查询时得到的效果集就是一张暂时表,由于效果中可能包罗多个表中的字段,并没有一张真实的表与之完全对应。
外部暂时表:
用户可以使用 CREATE TEMPORARY TABLE 语句手动创建暂时表。
这些表只在当前会话中可见,并且在会话关闭时会主动删除。
内部暂时表:
服务器在执行特定操纵时主动创建的暂时表。
用户无法直接控制这些表的创建,它们通常用于以下情况:
使用 UNION 合并查询效果
对视图执行操纵,如使用 UNION 或聚合函数
使用子查询
使用 DISTINCT 和 ORDER BY 的查询可能需要一个暂时表
使用 INSERT...SELECT 将查询效果插入表中时,需要一个内部暂时表来保存查询效果行
使用 COUNT(DISTINCT) 和 GROUP_CONCAT() 表达式
使用窗口函数时

撤销表空间(Uodo Tablespace)

MySQL在初始化时会在数据目录下创建两个默认的撤销表空间,其数据文件名分别为 undo_001 和 undo_002。这些撤销表空间在数据字典中的名称分别为 innodb_undo_001 和 innodb_undo_002。
作用
撤销表空间的主要作用是记录事件对聚集索引记录的修改,包罗新增、修改和删除操纵。通过记录这些修改,当事件需要回滚时,数据库体系可以根据撤销日志中的信息,将数据库状态恢复到事件开始之前的状态,从而确保事件的原子性。
事件回滚:当事件由于错误或者其他原因需要回滚时,撤销表空间记录的信息允许数据库体系撤销事件对数据的任何更改。
并发控制:撤销表空间也是实现数据库的并发控制机制的告急组成部门,确保事件之间的隔离性和同等性。

撤销日志(Undo Log)

撤销日志(Undo Log)是事件处置惩罚过程中的关键机制,用于确保事件的原子性。每当事件对数据进行修改时,体系会在磁盘上创建相应的撤销日志。
撤销日志写入时机


撤销日志在表空间构造形式

撤销表空间中包罗回滚段,每个回滚段中包罗多少个撤销槽(undo slots),每个槽对应一个撤销日志段(Undo log segments)。撤销日志段中包罗详细的撤销日志。

撤销日志段(Undo log segments):也称为撤销段,每个撤销日志段可以保存多个事件的回滚日志。不过同一时刻只能被一个活泼事件使用,其占用的空间只能在事件提交或回滚后重新使用。
回滚段(Rollback segments):包罗撤销日志段,通常位于撤销表空间和全局暂时表空间中。可以通过体系变量innodb_rollback_segments界说分配给每个撤销表空间和全局暂时表空间的回滚段数目,默认值为128,取值范围为[1, 128]。
回滚段支持的事件数:取决于回滚段中的撤销槽数目以及每个事件所需的撤销日志数。撤销段中的撤销槽数目可以根据InnoDB页巨细计算,公式为(InnoDB页巨细 / 16)。比方,对于默认的InnoDB页巨细为16KB,一个回滚段可以包罗1024个撤销槽,用于存储事件的撤销日志。
回滚段中的其他记录信息:回滚段还记录了History List的头节点(History List Base Node)和回滚段的巨细。
通过体系变量配置:可以使用体系变量innodb_rollback_segments设置撤销表空间中的回滚段数目,最大值和默认值均为128。
撤销日志构造形式及其内容


UNDO PAGE HEADER:记录Undo Log页的范例、日志偏移位置、下一个页链表引用等信息。
UNDO LOG SEGMENT HEADER:记录回滚段的信息。
UNDO LOG HEADER:记录产生这条日志的事件ID(Trx ID)、事件的提交顺序(Trx No)以及其他事件相关信息
Undo Log页的其他空间用来记录Undo Log日志。如果某个事件很大,一个Undo Log页无法完整记录,就需要申请新的Undo Log页。新页通过UNDO PAGE HEADER中的链表引用信息链接到前一个页。后续的页只需要记录Undo Log,不需要记录Undo头信息。
这由Undo Log构成的链表称为Undo链,在事件中扮演着非常告急的脚色。
Uodo链分类
Insert Undo链构造方式:每个Insert Undo链中的Undo Log对应一条新的数据行。数据行中使用ROLL_POINTER信息来关联Undo Log。在回滚操纵时,可以通过ROLL_POINTER信息找到需要回滚的Undo Log。

Update Undo链构造方式:对于删除和更新操纵,在Update Undo链中,每个Undo Log都对应一个数据行。每次更新都通过Undo Log中的ROLL_POINTER进行关联,因此每个数据行形成一个Undo Log版本链。在事件的MVCC(多版本并发控制)中,这个版本链起着非常告急的作用,用于办理事件的"隔离性"。回滚操纵可以依序撤销这些版本,确保数据在不同事件间的同等性和隔离性。


撤销日志分类




  • 新增 (TRX_UNDO_INSERT_REC):Undo Log操纵信息相对简单,只记录了主键值、主键长度等主键信息。
  • 删除 (TRX_UNDO_DEL_MARK_REC):除了记录主键信息外,还记录了:

    • 旧的事件ID
    • 旧的ROLL_POINTER信息
    • 用来构建有序Undo版本链的信息
    • 一些被索引字段的信息

  • 更新 (TRX_UNDO_UPD_EXIST_REC)

    • 如果更新操纵没有修改主键:
      类似于删除操纵,记录主键信息、旧的事件ID、旧的ROLL_POINTER信息、被索引字段的信息和被更新的信息。
    • 如果更新操纵修改了主键:
      记录两条Undo Log:
      一条删除的Undo Log
      一条新增的Undo Log

内存中的撤销日志

与数据页在内存中的保存方式雷同,撤销日志也保存在Buffer Pool中,其布局与磁盘中的UndoLog页同等。每个UndoLog页在内存中通过控制块进行管理。
在内存中,使用四个链表来管理正在使用的UndoLog页和空闲UndoLog页,根据不同的日志范例分为:
Insert List:正在被使用的,用来管理Insert范例的UndoLog页。
Insert Cache List:空闲的或者被清理后可以被后续事件重用的Insert范例UndoLog页。
Update List:正在被使用的,用来管理Update范例的UndoLog页。
Update Cache List:空闲的或者被清理后可以被后续事件重用的Update范例UndoLog页。


重做日志(Redo Log)

重做日志用于确保在数据库瓦解后能够恢复已经提交但尚未写入到磁盘的事件数据。重做日志以文件形式存储在磁盘上。在正常操纵过程中,MySQL会根据受影响的记录生成并写入重做日志文件中的数据,这些记录被称为"Redo"信息。
当数据库重新启动时,体系会主动读取重做日志来执行数据恢复操纵。如许可以确保即使在体系瓦解或异常断电等情况下,已提交的事件能够被精确恢复,从而保证数据的同等性和长期性。
为什么不直接写入磁盘?
当执行数据操纵语言(DML)操纵时,全部修改都应包罗在事件中。一旦修改完成并提交事件,内存中的修改数据页就应革新到磁盘以实现长期性。
如果在此DML操纵中开始革新到磁盘时服务器瓦解,未革新到磁盘的数据页将会丢失,导致事件在磁盘上的修改不完整,从而破坏了事件的同等性。
为办理此题目,InnoDB在每次执行DML操纵时,会将内存中修改的数据页内容首先以日志形式记录到磁盘上,然后再真正落盘。这种方式相称于对修改进行了一次备份,即使服务器瓦解,也能从磁盘上的日志文件中找到上次瓦解前未落盘的数据,并完成落盘操纵。
InnoDB引擎的事件采用了WAL技术(Write-Ahead Logging),其基本思想是先写日志,再写磁盘。只有日志写入乐成,事件才气算作提交乐成。这里的日志即为重做日志(Redo Log)。在发生宕机且数据未刷到磁盘时,可以通过重做日志来恢复数据,确保ACID特性中的长期性。这也是重做日志的作用所在。
重写日志写入时机

在数据修改操纵发生时,重做日志会被追加记录。已经落盘的数据对应的日志位置被记录为检查点,标志检查点之前的数据为无效。如许设计使得重做日志文件可以循环使用。

Mini-Transaction

上面说到只要是DML操纵就会先写到重做日志中,但是如果这个记录记录到一半时体系奔溃了又该怎么办?以是要有个机制来保证日志必须时完整的,否则就不按照不完整的日志进行恢复。
Mini-Transaction(MTR)是针对DML操纵过程界说的概念,指的是记录一个DML操纵所产生的一组完整日志。在MySQL中,这组Redo Log作为一个不可分割的团体用于瓦解恢复。这里所说的不可分割的组包罗:
向聚簇索引对应的B+树页面中插入一条记录时产生的Redo Log不可分割;
向某个二级索引对应的B+树页面中插入一条记录时产生的Redo Log不可分割;
其他对页面进行访问操纵时产生的Redo Log不可分割。
Mini-Transaction(MTR)包罗一个DML操纵所对应的一组Redo Log。一个事件可能包罗多个DML操纵,因此一个事件中可以包罗一个或多个SQL语句。每个SQL语句可能包罗一个或多个MTR,而每个MTR则包罗一条或多条Redo Log。


InnoDB为了尽可能节省空间,在MTR只包罗一条日志的情况下,进行了优化。日志范例只有几十种。用于表现日志范例的TYPE字段长度为1字节,此中只用了7个比特位,能够表现整数127,足以表达全部的日志范例。因此,可以通过省出一个比特位来表现当前MTR包罗一条还是一组Redo Log。换句话说,如果TYPE字段的第一个比特位为1,表现MTR只包罗一条Redo Log;为0表现MTR包罗一组Redo Log。


重写日志格式

Redo Log本质上记录了事件对数据库所做的修改操纵,涵盖了多种场景,如数据行和索引页的增删改,以及范围修改和删除等。不同范例的redo日志界说了不同的布局,但大部门范例的redo日志通常包罗以下通用布局:
Type: 日志范例,1字节
Space ID: 操纵所属的表空间,4字节
Page number: 操纵的数据页在表空间中的编号,4字节
Data: 日志的详细内容,长度不固定

管理重做日志数据布局

用来构造Redo Log的数据布局是Redo页,页的巨细是512字节,也可以称为一个Redo Log Block。这个巨细恰好对应磁盘上的一个扇区,如许设计可以保证在将日志写入磁盘时的一连性。

LOG_BLOCK_HDR_NO:Block的唯一标识,是一个大于0的值,取值范围是1到0x40000000UL。0x40000000UL对应的整数是1073741824,即1GB。因此,InnoDB最多能够生成1GB个日志块。每个日志块巨细为512字节,以是InnoDB允许维护的最大容量为512GB。
LOG_BLOCK_HDR_DATA_LEN:表现Block中已经使用了多少字节。由于块头占用了12字节的空间,以是初始值为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_CHECKSUM:表现Block的校验和,用于精确性校验。
Redo页的构造形式

写入是从缓冲区的第一个 Redo Log Block 的 Log Block Body 开始,依次向后写入。每个 Redo Log Block 的巨细为固定的512字节。
当一个 Block 的空间用完之后,接着写入下一个 Block。
InnoDB 提供了一个名为 buf_free 的全局变量,该变量表现后续写入日志在 Log Buffer 中的起始位置。

重做日志的刷盘时机

在InnoDB中,当一个MTR执行完成后,其生成的Redo Log会被写入到Log Buffer中。由于Log Buffer的巨细是有限的,并且此中记录的Redo Log的目标是为了在服务器瓦解后进行数据恢复,因此将其保存在内存中是不安全的。因此,InnoDB会在以下情况将Redo Log刷写(flush)到磁盘:
Log Buffer 空间不足时:Log Buffer的巨细通过体系变量 innodb_log_buffer_size 设置,当当前Log Buffer中的Redo Log占用了总容量的一半左右时,会触发将部门Redo Log刷写到磁盘。
事件提交时:当事件提交时,事件对应的MTR已经完全记录在Log Buffer中。在数据真正落盘之前,需要将这部门Redo Log革新到磁盘,以确保事件的长期性。
后台线程定时刷盘:后台的Master Thread线程约莫每秒会将Log Buffer中的Redo Log定期革新到磁盘,这种方式保证了长期化操纵的及时性和服从。
正常关闭服务器时:在正常关闭服务之前,会将Log Buffer中的全部Redo Log革新到磁盘,以确保全部未长期化的操纵得到保存。
做检查点(checkpoint)操纵时:检查点操纵是指将数据库中的全部已修改的数据页写入到磁盘。在执行检查点时,会包罗将Log Buffer中的部门或全部Redo Log刷写到磁盘,以保证数据库的同等性和长期性。
   刷盘策略在InnoDB中可以通过多种体系变量进行配置,确保数据的长期性和性能之间的平衡。以下是关于刷盘策略的相关配置:
  innodb_flush_log_at_trx_commit:该体系变量控制事件的Redo Log何时写入磁盘。
  0:每秒将Redo Log写入体系缓冲区,但不立刻革新到磁盘。在MySQL瓦解时可能会丢失最多1秒的事件。
1:每次事件提交时,将Redo Log写入体系缓冲区并立刻革新到磁盘,确保事件长期性。
2:每次事件提交后,将Redo Log写入体系缓冲区,但每秒革新一次到磁盘。在MySQL瓦解时可能会丢失最多1秒的事件。
Innodb_flush_log_at_timeout:如果设置了该体系变量,可以指定Redo Log在体系缓冲区中的停留时间(以秒为单元),超过这个时间会逼迫革新到磁盘。默认情况下,这个值是1秒。
  sync_binlog:如果启用了二进制日志(binary log),并且设置了sync_binlog = 1,则必须将innodb_flush_log_at_trx_commit设置为1,以确保二进制日志与InnoDB事件的同等性。
  这些配置选项允许数据库管理员根据应用步伐的需求来调整性能和数据安全之间的衡量。
  重做日志分类

用于数据页的日志范例:
这类日志记录了对数据页(如表的数据行或索引页)的修改操纵。它们包罗了对数据行的插入、更新、删除,以及索引页的修改等。
用于表空间文件的日志范例:
此类日志记录了对整个表空间文件的修改,比方对表空间的创建、扩展、压缩或者重定名等操纵。
提供额外信息的日志范例:
这类日志提供了关于事件、恢复或其他元数据的附加信息,如检查点信息、事件提交记录等。
重做日志怎样进行奔溃恢复

获取最新的 checkpoint_lsn 和恢复的出发点:最新的 checkpoint_lsn 可以从 RedoLog 文件组的第一个文件的管理信息中的 checkpoint1 和 checkpoint2 block 中获取。这两个 block 中存储了 checkpoint_lsn 和 checkpoint_no 信息。比较这两个 block 中的 checkpoint_no 值,较大的谁人对应着最新的 checkpoint 信息。
通过上述步调,您可以获得最新发生的 checkpoint 的 checkpoint_lsn 值及其在 RedoLog 文件组中的偏移量 checkpoint_offset。
确认恢复的终点:RedoLog 是顺序写入的,每个 block 的头部有 LOG_BLOCK_HDR_DATA_LEN 属性记录当前 block 使用了多少字节。对于写满的 block,这个值是 512 字节。
找到第一个 LOG_BLOCK_HDR_DATA_LEN 不为 512 的 block,这个 block 中的最后一条日志就是恢复的终点。
进行恢复:确定了需要恢复的出发点和终点后,需要顺序扫描 checkpoint_lsn 之后的日志记录。
对于每条日志记录,根据其内容依次恢复对应的数据页。
InnoDB 在恢复过程中采用了优化措施,使用哈希表将雷同 Space Id 和 Page No 的日志记录构造在一起,以减少读取数据页时的随机 I/O 次数。

双写缓冲区(Doublewrite Buffer Files)

双写缓冲区是磁盘上的一个存储区域,用于增强InnoDB的数据长期性和可靠性。详细来说,在InnoDB将缓冲池中的数据页写入到磁盘上表空间数据文件之前,会先将对应的页写入双写缓冲区。如许做的目标是为了在数据真正落盘的过程中,比方操纵体系或存储子体系瓦解或异常断电时,能够保证数据的完整性和同等性。
如果在数据写入双写缓冲区后,体系发生了瓦解或异常情况,InnoDB在瓦解恢复时可以从双写缓冲区中找到一份完好的页副本。这种机制有效地减少了数据损坏或丢失的风险,提高了数据库的可靠性和恢复能力。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

农民

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表