Mysql-架构、存储、日志

参考:


1.逻辑架构

image-20230624141516939

简单来说,就是分为2层,服务层和引擎层(下图的两个绿色框框)。

类似于咱们的公司,有管理层和执行层,管理层负责去拆分任务,执行层负责去具体执行。

img

image-20250522161143207

整个MySQL Server由以下组成

  • Connection Pool : 连接池组件
  • Management Services & Utilities : 管理服务和工具组件
  • SQL Interface : SQL接口组件
  • Parser : 查询分析器组件
  • Optimizer : 优化器组件
  • Caches & Buffers : 缓冲池组件
  • Pluggable Storage Engines : 存储引擎
  • File System : 文件系统

1.1 服务层

  • 查询缓存已在 MySQL 8.0 移除。

  • 服务层引擎层通过统一接口(如 Handler API)交互,实现存储引擎的插件化。

功能描述 核心组件/机制 特点
处理客户端连接、身份验证和通信协议 线程池、连接池、身份验证模块 - 多协议支持(TCP/IP、Unix Socket)
- 线程复用减少开销
- 用户权限验证
SQL 解析、查询优化、执行逻辑控制 解析器、优化器、执行引擎、查询缓存 - 生成执行计划
- 优化索引和 JOIN 顺序
- 内置函数和跨引擎功能(如视图、触发器)

image-20250521182156382

  • 连接器

连接器主要和身份认证和权限相关的功能相关,就好比一个级别很高的门卫一样。

主要负责用户登录数据库,进行用户的身份认证,包括校验账户密码,权限等操作。

如果用户账户密码已通过,连接器会到权限表中查询该用户的所有权限,之后在这个连接里的权限逻辑判断都是会依赖此时读取到的权限数据,也就是说,后续只要这个连接不断开,即使管理员修改了该用户的权限,该用户也是不受影响的。

  • 查询缓存

MySQL 8.0 版本后移除

查询缓存主要用来缓存我们所执行的 SELECT 语句以及该语句的结果集。

连接建立后,执行查询语句的时候,会先查询缓存,MySQL 会先校验这个 SQL 是否执行过,以 Key-Value 的形式缓存在内存中,Key 是查询语句,Value 是结果集。

如果缓存 key 被命中,就会直接返回给客户端,如果没有命中,就会执行后续的操作,完成后也会把结果缓存起来,方便下一次调用。

当然在真正执行缓存查询的时候还是会校验用户的权限,是否有该表的查询条件。

MySQL 查询不建议使用缓存,因为查询缓存失效在实际业务场景中可能会非常频繁,假如你对一个表更新的话,这个表上的所有的查询缓存都会被清空。对于不经常更新的数据来说,使用缓存还是可以的。所以,一般在大多数情况下我们都是不推荐去使用查询缓存的。

MySQL 8.0 版本后删除了缓存的功能,官方也是认为该功能在实际的应用场景比较少,所以干脆直接删掉了。

  • 分析器

    MySQL 没有命中缓存,那么就会进入分析器,分析器主要是用来分析 SQL 语句是来干嘛的,分析器也会分为几步。

    • 词法分析

      一条 SQL 语句有多个字符串组成,首先要提取关键字,比如 select,提出查询的表,提出字段名,提出查询条件等等。

    • 语法分析

      主要就是判断你输入的 SQL 是否正确,是否符合 MySQL 的语法。

  • 优化器

优化器的作用就是它认为的最优的执行方案去执行(有时候可能也不是最优)。

比如多个索引的时候该如何选择索引,多表查询的时候如何选择关联顺序等。

  • 执行器

当选择了执行方案后,MySQL 就准备开始执行了。

首先执行前会校验该用户有没有权限,如果没有权限,就会返回错误信息。如果有权限,就会去调用引擎的接口,返回接口执行的结果。

1.2 引擎层

存储引擎是基于表的,而不是数据库。创建表的时候,可以指定不同表的执行引擎。

功能描述 核心组件/机制 特点
数据存储、事务管理、索引实现 InnoDB、MyISAM、Memory 等插件式引擎 - 事务支持(ACID)
- 锁机制(行锁/表锁)
- 引擎可插拔,逻辑与物理存储分离
物理数据存储和文件系统交互 数据文件(.ibd、.MYD)、日志文件 - 持久化到磁盘
- 文件格式管理(如页结构)
- 日志保障数据安全(binlog、redo log)

常见的Mysql存储引擎如下:

引擎类型 适用场景 特点
InnoDB OLTP、高并发事务 支持行锁、外键、崩溃恢复
MyISAM 读密集型、静态表 表级锁、全文索引、压缩存储
RocksDB 高写入吞吐、SSD 优化 LSM-Tree 结构、分层压缩
Memory 临时表、高速缓存 数据存于内存,无持久化

常见的几种 MySQL 存储引擎对比

其中,比较常见的是InnoDB和MyISAM的对比,即我们常见的InnoDB三大特点。

  • 支持事务
  • 支持外键
  • 行级锁
特性 InnoDB MyISAM
事务支持 ✅ (完整 ACID 特性)
锁粒度 行级锁 表级锁
外键约束
崩溃恢复 通过 Redo/Undo Log 自动恢复 需手动修复(REPAIR TABLE
全文索引 ✅(MySQL 5.6+) ✅(早期版本唯一支持)
存储文件 .ibd(数据+索引) .MYD(数据) + .MYI(索引)
缓存机制 Buffer Pool 缓存数据页和索引 仅缓存索引,数据依赖操作系统缓存
压缩存储 支持页压缩(KEY_BLOCK_SIZE 支持只读压缩表
典型场景 OLTP、高并发事务 读密集型、静态表、临时表

2.文件存储

为啥要了解这个,就好像,假设要我们自己设计一个简易数据库。

数据最终都要存储吧?那我们怎么存?

例如,表结构信息放在一个txt的最开头,然后一行一个记录。

又例如,表结构信息统一放在一个txt,然后用不同的txt来记录不同表的数据。

上面提到了InnoDB和MySAM存储引擎,现在介绍下各自的文件存储格式。

数据库存储的本质即数据库中的数据最终需要持久化到磁盘,存储格式决定了:

  • 数据如何组织
  • 如何快速找到数据
  • 如何保证数据安全

存储引擎类似于咱们的数据管家,负责:

  • 把表数据转换为磁盘文件
  • 管理数据读写、索引、事务等细节

2.1 查看位置

2.1.1 通过配置查询

InnoDB文件的路径通常在MySQL配置文件(如my.cnfmy.ini)中定义。

  • Linux/Mac:默认路径为 /etc/my.cnf/etc/mysql/my.cnf/usr/local/mysql/etc/my.cnf
  • Windows:通常在MySQL安装目录下的 my.ini 文件中。
# InnoDB系统表空间文件(ibdata1)
innodb_data_file_path = ibdata1:12M:autoextend

# InnoDB日志文件(ib_logfile0、ib_logfile1)
innodb_log_group_home_dir = /path/to/logs

# 数据目录(默认存储位置)
datadir = /var/lib/mysql

2.1.2 通过命令查询

-- 查看数据目录(datadir)
SHOW VARIABLES LIKE 'datadir';

-- 查看InnoDB系统表空间路径
SHOW VARIABLES LIKE 'innodb_data_file_path';

-- 查看InnoDB日志文件路径
SHOW VARIABLES LIKE 'innodb_log_group_home_dir';

-- 检查是否启用独立表空间(每个表单独存储为.ibd文件)
SHOW VARIABLES LIKE 'innodb_file_per_table';

image-20250522163628938

2.2 文件明细

MyISAM在MySQL 5.5之前是默认的存储引擎,后面切换到InnoDB。

2.2.2 MyISAM

.frm    # xx表结构(MyISAM格式)
.MYD    # xx表数据
.MYI    # xx表索引
db.ibd      # 系统表空间(含数据字典)
ibdata1     # 元数据等

2.2.2 InnoDB

k,之前一直觉得这几个名字绕口,后面意识到这个ib可能就是InnoDB的缩写。

InnoDB中有3个关键的文件:

ib(InnoDB) + data + 1(第一个文件)

File for Relational Metadata(关系型元数据文件)

ib(InnoDB) + d(Data)

  • 系统表空间(ibdata1):存放所有 InnoDB 表的公共信息(如事务日志、回滚段)等。

  • 表结构文件(.frm,):定义表结构,如字段类型等

  • 表数据文件(.ibd):存储数据+索引等

数据库的核心就是存储数据保证数据安全可用,那么,基于这个,我们可以推测下大概需要存储哪些玩意?

  • 系统自身的数据:数据库名、表名、列名、权限等元数据。
  • 用户的表数据:你创建的表(例如 users 表、orders 表)
  • 事务日志:简单的方案就是先记录用户做了啥,再去更改数据。记录失败(突然断电等),数据也不会被更新,保证数据安全。
  • 临时数据:辅助运算用的,排序、分组等操作需要的临时数据,查询的中间结果等。

那么,这些数据,能不能全部仍在一起呢?

在MySQL 5.6.6版本以前,MySQL默认会把所有的innodb的表都放在同一个文件中(ibdata1),当该文件过大的时候,MySQL容易出错,维护性能差。

Innodb存储引擎可将所有数据存放于ibdata*的共享表空间,也可将每张表存放于独立的.ibd文件的独立表空间,共享表空间以及独立表空间都是针对数据的存储方式而言的。

  • 共享表空间: 某一个数据库的所有的表数据,索引文件全部放在一个文件中。

  • 独立表空间: 每一个表都以独立的文件方式来进行存储,有一个.frm表描述文件,还有一个.ibd数据文件。

对比维度 共享表空间 独立表空间
存储方式 所有表的数据和系统数据集中在 ibdata1 每个表独立存储为 .ibd 文件
空间释放 ❌ 删除表后空间不释放,文件持续增长 ✅ 删除表后立即释放 .ibd 文件空间
I/O 性能 ❌ 高并发时单文件成为瓶颈
所有表竞争同一文件的 I/O,高并发时吞吐量下降。
✅ 分散 I/O,支持并行读写
备份恢复 ❌ 必须全库备份,无法单独恢复表 ✅ 支持单表备份和恢复
压缩与加密 ❌ 不支持 ✅ 支持表级压缩(COMPRESSION)和加密(TDE
适用场景 历史遗留系统、临时表或特殊需求(如全库事务密集型操作) 现代数据库默认方案,适合90%的生产场景
默认配置 MySQL 5.5 及之前版本默认 MySQL 5.6+ 默认启用

所以,从共享表空间自然而然演进成了独立表空间,即一个关键的配置项。

共享表空间仅用于特殊场景:如临时表、历史系统维护,或对全库事务一致性有极端要求的场景。

# 启用独立表空间
innodb_file_per_table = ON

2.2.1.1 系统表空间ibdata1

image-20250522164414271

A.存了啥?
  • 数据字典:数据字典本身是 InnoDB 表,存储表、列、索引等元数据信息。

image-20250522173547650

UndoLog、ChangeBuffer、双写缓冲后面扩展讲

  • Undo Log:记录事务修改前的旧值,用于事务回滚和多版本并发控制(MVCC)

  • Change Buffer:优化对非唯一索引的插入操作。

  • 双写缓冲:防止数据页写入不完整(crash-safe)。

  • innodb_file_per_table=OFF 时,将存储所有 InnoDB 表的数据和索引。

下面介绍下ChangeBuffer和双写缓冲。

B.相关配置

上面不是介绍了一个查看的命令:

-- 查看InnoDB系统表空间路径
SHOW VARIABLES LIKE 'innodb_data_file_path';

image-20250522164544301

这里的配置值ibdata1:12M:autoextend(默认值)啥意思呢?

  • ibdata1
    系统表空间文件的名称,默认情况下,InnoDB的系统表空间文件名为 ibdata1

    如果配置了多个文件(如 ibdata1:10M;ibdata2:10M:autoextend),InnoDB会按顺序使用这些文件。

  • 12M
    文件的初始大小。12M 表示初始大小为 ​12MB

    • 如果文件不存在,MySQL启动时会自动创建该文件,并分配12MB的初始空间。
  • autoextend
    自动扩展规则。当文件空间不足时,InnoDB会自动扩展文件大小。

    每次扩展的默认增量是 8MB(具体大小可能因MySQL版本或配置而不同)。

咋工作的呢?

  • 初始阶段
    MySQL启动时,如果 ibdata1 文件不存在,会自动创建一个大小为12MB的文件。
  • 空间不足时
    当系统表空间(存储数据字典、双写缓冲区、事务日志等)需要更多空间时,InnoDB会按以下步骤处理:
    1. 检查 autoextend 是否启用。
    2. 如果是,将文件大小增加一个增量(默认8MB)。
    3. 重复扩展,直到磁盘空间耗尽或达到文件系统的最大限制。

2.2.1.2 表结构文件.frm

.frm在MySql8.0不再是单独的文件,分离到了mysql.ibd文件中。

2.2.1.3 表数据文件.ibd

存储表数据和表索引

3.三大日志

特性 Binlog(归档日志) Redo Log(重做日志) Undo Log(回滚日志)
所属层级 MySQL Server 层 InnoDB 存储引擎层 InnoDB 存储引擎层
主要作用 1. 主从复制
2. 数据恢复(逻辑层)
1. 崩溃恢复(物理层)
2. 保证事务持久性
1. 事务回滚(原子性
2. MVCC 多版本读(隔离性
存储引擎支持 所有存储引擎(如 InnoDB、MyISAM) 仅 InnoDB 仅 InnoDB
日志性质 逻辑日志(记录 SQL 或行的逻辑操作) 物理日志(记录数据页的物理修改) 逻辑日志(记录数据行的旧版本)
内容示例 UPDATE user SET name='yang' WHERE id=1; 将表空间 ID=3、页号=5 的页中偏移量 0x100 处的值从 'old' 改为 'yang' 记录 id=1 的旧值 name='old',用于回滚或 MVCC 快照读
生命周期 事务提交后写入,可配置保留策略(按时间或文件大小) 循环写入,通过 Checkpoint 机制清理已刷盘的日志 事务提交后可能被 Purge 线程清理(需确保无事务依赖旧版本)
持久化保证 依赖配置(sync_binlog 参数控制刷盘频率) 通过 WAL(Write-Ahead Logging)机制保证,事务提交时按配置(innodb_flush_log_at_trx_commit)刷盘 依赖 Redo Log 持久化,自身修改会记录到 Redo Log
应用场景 - 主从复制
- 按时间点恢复数据
- 崩溃恢复时重放未刷盘的脏页操作
- 保证事务提交后的持久性
- 事务回滚时恢复数据
- 提供非阻塞读(MVCC)

3.1 undoLog

事务日志/回滚日志,方便回滚用的,主要用于确保事务的ACID特性中的原子性

记录的是逻辑操作,即数据在被修改之前的状态。包括插入(INSERT)、删除(DELETE)和更新(UPDATE)。

主要功能:

  • 事务回滚

    当事务需要回滚时,通过执行undo log中记录的逆向操作来恢复到事务开始前的数据状态。

  • 多版本并发控制(MVCC)

    结合ReadView机制,利用undo log实现多版本并发控制,从而支持高并发读写操作。

操作记录:

操作类型 Undo Log 记录内容 回滚机制 备注
INSERT 新插入记录的主键 ID。 根据主键 ID 删除对应的记录。 事务提交后可直接清理,无需保留历史版本。
UPDATE 逆向操作(如将字段值从 B 回滚为 A)。 执行逆向操作,将数据恢复到修改前的值。 需保留至所有依赖该版本的事务结束(支持 MVCC)。
DELETE 被删除数据的完整副本(包括所有字段值)。 将数据副本重新插入原表,并恢复删除标记或隐藏版本。 删除操作实际标记为“逻辑删除”,回滚时恢复为可见。

3.2 redoLog

redo log是引擎层记录实际数据变化的日志,例如将xx页的偏移量xx为2的数据更新成yang,只属于 InnoDB 存储引擎,主要用于确保事务的ACID特性中的持久性

redoLog在磁盘上的名字叫ib_logfile

image-20250522221233516

讲之前,先了解下缓冲池(buffer pool)和变更缓冲(Change Buffer)的概念。

3.2.1 缓冲池buffer pool

为啥要有缓冲,可以了解下我这篇文章:Linux-文件写入和文件同步,mysql引入的核心是为了提高性能。

  • 缓冲池(buffer pool):InnoDB 存储引擎的内存缓存区域,用于缓存频繁访问的数据页和索引页,通过减少磁盘 I/O 提升数据库性能。

内存中修改后未同步到磁盘的数据页叫脏页,通过后台线程 Checkpoint 定期将脏页刷回磁盘(支持多种刷盘策略)。

  • 所有数据操作(读/写)优先在 Buffer Pool 中进行。
  • 数据页从磁盘读取后缓存在 Buffer Pool 中,后续操作直接操作内存副本,减少磁盘访问。

3.2.2 变更缓冲Change Buffer

假设写到缓冲池就不管了,然后突然宕机,数据没落盘,丢了咋办?不是你Mysql告诉我说成功提交了?你的持久性呢?

  • Change Buffer(变更缓冲):Change Buffer 是 Buffer Pool 的一部分,用于缓存非唯一二级索引页的变更操作(INSERT/UPDATE/DELETE),减少随机 I/O,提升写性能。

非唯一索引是啥,主键这种表里只能有唯一值的我们叫唯一索引,表里可以有多个值的索引我们就可以叫非唯一索引。例如员工号和部门号,多个员工都可能有相同的部门号,部门号就可以添加成一个非唯一索引。

3.2.2.1 适用场景

  • 非唯一二级索引:强调记录唯一,所以我们必须确保实时唯一,这个时候就必须访问磁盘数据,不能使用poolbuffer。

  • 数据页不在内存:若目标索引页已在 Buffer Pool 中,直接修改内存页,无需 Change Buffer。

3.2.2.2 工作原理

graph LR A["Change Buffer"] --> B["Buffer Pool"] B --> C["磁盘"] subgraph MySQL 内存结构 A["Change Buffer<br/>(缓存非唯一索引变更)"] B["Buffer Pool<br/>(缓存数据页)"] end subgraph 持久化存储 C["磁盘<br/>(数据文件)"] end style A fill:#f9f,stroke:#333 style B fill:#9cf,stroke:#333 style C fill:#cfc,stroke:#333
  • Change Buffer 合并(延迟合并)

    • 当某个索引页首次被加载到 Buffer Pool 时,系统会检查 Change Buffer 中是否存在针对该页的未合并操作。
    • 若有,则将 Change Buffer 中记录的变更(如 INSERT/UPDATE/DELETE)合并(Merge)到内存中的索引页,生成最新的数据版本。
  • 脏页标记

    • 合并后的索引页会被标记为 脏页(Dirty Page),表示内存中的数据与磁盘上的数据不一致。
  • Checkpoint 触发刷盘

    • InnoDB 通过 Checkpoint 机制 将脏页刷回磁盘,具体流程:
      • 后台线程(Page Cleaner) 周期性扫描 Buffer Pool 中的脏页。
      • 根据刷盘策略(如 innodb_flush_neighbors)选择脏页,通过 异步 I/O 写入磁盘的数据文件(.ibd)。
      • 刷盘完成后,脏页标记被清除,内存页与磁盘数据一致。

3.3.3 日志预写WAL(Write-Ahead Logging)

正是由于使用到了缓冲池等,内存数据不可靠,为了确保数据持久化,才引入了咱们的redoLog。

通过先将RedoLog持久化到磁盘中来确保数据持久性。

image-20250522221043012

日志先写入RedoLogbuffer缓冲区,然后被后台进程刷盘,不用等脏页刷入磁盘。

即使系统奔溃,脏页刷盘失败,也可以通过RedoLog的内容,将数据恢复到当前最新的状态。

这里你可以看到,咱们的redoLog咋也有个pool?不是等着写完啊?那不也有丢失的风险。

还记得redis的持久化配置嘛,一样的,都是性能的权衡。

MySQL 通过参数 innodb_flush_log_at_trx_commit 控制 Redo Log 的刷盘行为。

InnoDB 存储引擎有一个后台线程,每隔1 秒,就会把 redo log buffer 中的内容写到文件系统缓存(page cache),然后调用 fsync 刷盘。

参数值 刷盘逻辑 数据丢失风险场景 性能 适用场景
0 完全异步刷盘: 事务提交时不做任何操作
依赖后台线程每秒将 redoLog bufferpage cache → 磁盘
MySQL 崩溃:丢失最多 1 秒数据
服务器宕机:丢失最多 1 秒数据
⚡️ 最高(无 I/O) 允许数据丢失的高吞吐场景(如日志)
1 同步刷盘: 事务提交时强制 redoLog bufferpage cache → 磁盘(调用 fsync 几乎无丢失 (仅当磁盘故障时可能丢失) ⚠️ 最低(频繁 I/O) 强一致性场景(如金融交易)
2 半同步刷盘: 事务提交时 redoLog bufferpage cache,后台线程每秒 page cache → 磁盘 MySQL 崩溃:无丢失(数据在 page cache
服务器宕机:丢失最多 1 秒数据
⚡️ 较高(无 fsync 允许宕机丢失的高并发写入场景

默认值为 1,为了保证事务的持久性,我们必须将其设置为 1。

image-20250523194114867

类似于redis的持久化,关于redis的,可以参考我这篇文章:Redis-4-持久化

3.3.4 日志检查组

硬盘上存储的 redo log 日志文件不只一个,而是以一个日志文件组的形式出现的,每个的redo日志文件大小都是一样的。

还记得前面的图吗?

image-20250522221233516

比如可以配置为一组4个文件,每个文件的大小是 1GB,整个 redo log 日志文件组可以记录4G的内容。

它采用的是环形数组形式,从头开始写,写到末尾又回到头循环写,如下图所示。

image-20250523194511592

即类似于这样:

image-20250523194537159

在这个日志检查组中,还有2个比较重要的属性。

  • write pos:当前记录的位置,一边写一边后移。
  • checkpoint:已刷盘的数据对应的日志起点,即标识 Redo Log 中最早未被覆盖的日志位置。

每次刷盘 redo log 新写数据日志文件组中,write pos 位置就会后移更新。

每次 MySQL 加载日志文件组恢复数据时,会清空加载过的 redo log 记录,并把 checkpoint 后移更新。

write poscheckpoint 之间的还空着的部分可以用来写入新的 redo log 记录。

注意啊,我们要站在整个日志文件组的角度来看。

img

这两个点有啥用?我们得理解整体是基于环形的,理想情况下是write pos一直往前写,check point在后面追着。

  • 如果 write pos 追上 checkpoint ,表示日志文件组满了,这时候不能再写入新的 redo log 记录。

    这个时候呢,得停下来刷盘下,赶赶checkponit的进度。

img

3.3.5 性能分析

反正都在刷盘了,为啥不直接基于数据页来搞?

  • mysql操作的最小单元是页,大小是16KB,刷盘比较耗时,可能就修改了数据页里的几 Byte 数据,没必要把完整的数据页刷盘。
  • 数据叶刷盘的话,得随机写,而我们的redoLog是顺序写。

3.3 binlog

binlog 是server层的日志,记录内容是语句的原始逻辑,类似于上面说的把user表id为1的记录name值更新为2这种原始的逻辑。

不管用什么存储引擎,只要发生了表数据更新,都会产生 binlog 日志。

MySQL 数据库的数据备份、主备、主主、主从都离不开 binlog,需要依靠 binlog 来同步数据,保证数据一致性。

  • 数据恢复:通过详尽地记录所有影响数据状态的SQL命令,Binlog为从特定时间点或由于意外操作导致的数据丢失提供了恢
    复手段。一旦发生数据损坏或丢失事件,可以通过重放Binlog中的历史更改来恢复至先前的状态。
  • 主从复制:对于需要跨多台服务器实现数据备份的应用场景,可以利用监听写入数据库的Binlog日志机制,将主数据库的
    所有更新同步到一个或多个从属数据库上,从而构建出高效的分布式数据库架构。

img

binlog 日志有三种格式,可以通过binlog_format参数指定。

  • STATEMENT格式:记录实际执行的SQL语句。这种格式的优点是日志文件小,写入速度快,因为只记录SQL。但缺点是无法保证所有情况下主从数据一致,比如使用非确定性函数(如NOW()、UUID())时,主从可能不一致。
  • ROW格式:记录被修改的行的数据变化,包括修改前后的数据。这种格式的优点是能精确复制数据变化,确保主从一致。缺点是日志文件较大,尤其是批量操作时,影响存储和网络传输。
  • MIXED格式:结合STATEMENT和ROW,根据具体操作选择最合适的格式。MySQL会自动判断哪些操作适合STATEMENT,哪些需要ROW格式。例如,涉及非确定性函数时使用ROW,其他情况使用STATEMENT。

3.3.1 写入时机

因为一个事务的 binlog 不能被拆开,无论这个事务多大,也要确保一次性写入,所以系统会给每个线程分配一个块内存作为binlog cache

事务执行过程中,先把日志写到binlog cache,事务提交的时候,再把binlog cache写到 binlog 文件中。

可以通过binlog_cache_size参数控制单个线程 binlog cache 大小,如果存储内容超过了这个参数,就要暂存到磁盘(Swap)。

3.3.2 两阶段提交

redo log(重做日志)让 InnoDB 存储引擎拥有了崩溃恢复能力。

binlog(归档日志)保证了 MySQL 集群架构的数据一致性。

在执行更新语句过程,会记录 redo log 与 binlog 两块日志,以基本的事务为单位。

redo log 在事务执行过程中可以不断写入,而 binlog 只有在提交事务时才写入,所以 redo log 与 binlog 的写入时机不一样。

img

假设,写完redoLog后, binlog 没写完就出现了异常,这时候 binlog 里面没有对应的修改记录,之后用 binlog 日志恢复数据时,就会少一部分数据。

为了解决两份日志之间的逻辑一致问题,InnoDB 存储引擎使用两阶段提交方案。

原理很简单,将 redo log 的写入拆成了两个步骤preparecommit,这就是两阶段提交

img

  • prepare后,binlog异常:回滚事务

MySQL 根据 redo log 日志恢复数据时,发现 redo log 还处于prepare阶段,并且没有对应 binlog 日志,就会回滚该事务。

img

  • prepare后,binlog也写了,最后的commit阶段异常了:直接提交不回滚事务

执行框住的逻辑,虽然 redo log 是处于prepare阶段,但是能通过事务id找到对应的 binlog 日志,所以 MySQL 认为是完整的,就会提交事务恢复数据。

img

3.3.3 主从数据延迟?

主库写了binlog,从库呢监听binlog的变化,从库是先写到自己的relay log(重放日志)的,写完了就直接返回ok了(本次同步结束)。

由于同步结束的时机是从库relay log写入成功,从库啥时候能根据这个relaylog完成数据更新是不确定的。

所以提示提示数据同步完成的时候,有小概率从库是查不到数据的。

image-20250523202726704

3.4 扩展

3.4.1 事务执行时各个log的操作

  1. 事务开始

    • 客户端发送 BEGIN 命令,开启事务。
  2. 数据修改与日志生成

    • InnoDB 引擎修改 Buffer Pool中的数据页,标记为脏页。
    • InnoDB 引擎生成 Undo Log(记录旧数据,用于回滚和 MVCC)。
    • InnoDB 引擎写入 Redo Log Buffer(物理日志,状态为 Prepare)。
  3. 两阶段提交(2PC)协调

    • MySQL Server写入 Binlog(逻辑日志,记录 SQL 操作逻辑)。

    • InnoDB 引擎将 Redo Log 状态标记为 Commit,完成事务提交。

  4. 异步持久化

    • 后台线程将脏页、Redo Log、Binlog 异步刷盘(具体时机依赖配置策略)。
    • 释放 Undo Log(延迟清理,用于 MVCC 多版本读)。
11
posted @ 2023-06-24 14:19  羊37  阅读(24)  评论(0)    收藏  举报