MySQL1:分层架构

本文涉及的产品
云数据库 PolarDB MySQL 版,列存表分析加速 4核8GB
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
RDS Agent(兼容OpenClaw),2核4GB
简介: MySQL采用“服务层+存储引擎层”双层架构:上层(Server层)含连接器、解析器、优化器、执行器等,负责SQL处理与权限管控;下层(如InnoDB)专注数据存储、索引、事务与崩溃恢复。执行器仅发指令,真干活的是存储引擎。

MySQL 其实是 上层组件、下层存储引擎 组合起来的,结构可以理解为:

┌─────────────────────────────────┐
│          Server 层              │
│  (连接器、查询缓存、分析器、      │
│   优化器、执行器、内置函数等)      │
└────────────┬────────────────────┘
             │ 统一的 API 接口
┌────────────▼────────────────────┐
│        存储引擎层               │
│  (InnoDB、MyISAM、Memory 等)    │
│  负责数据的存储和提取            │
└─────────────────────────────────┘

更流程化地说:

客户端
   ↓
连接器
   ↓
SQL接口
   ↓
解析器
   ↓
优化器
   ↓
执行器
   ↓
存储引擎(InnoDB)

需要注意:MySQL 本身并不真正负责操作数据,真正操作数据的是存储引擎。

一条 SQL 是怎么执行的?

例如:

SELECT * FROM user WHERE id = 1;

MySQL 内部会经历:

1.连接器

写 SQL 之前,先要和连接器建立 TCP 连接。

登录 MySQL、建立连接、校验账号密码、权限验证,都是连接器负责。

由于后续这个连接里的操作都使用此时读取到的权限,所以修改权限后要重新连接才生效。

例如:

mysql -uroot -p

就是连接器在工作。

连接空闲超过 wait_timeout (默认是8小时)后会自动断开。

长连接 vs 短连接

  • 短连接:执行完几个SQL就断开,下次再重连。频繁创建连接开销大

  • 长连接:连接复用,但MySQL临时使用的内存是绑定在连接对象上的,长连接累积可能导致内存暴涨(OOM),因为不会被立即释放

    • 解决:定期断开长连接;或执行 mysql_reset_connection 重置连接状态(MySQL 5.7+)

2.查询缓存(8.0 已删除)

执行 SQL 前,先看这条 SQL 有没有之前查过、结果有没有缓存。Key 是 SQL 语句,Value 是结果。

有就直接返回,不走后面流程;现在 MySQL8.0 已经彻底删掉查询缓存

任何对表的更新操作(INSERT/UPDATE/DELETE)都会清空该表的所有查询缓存。且对于写多读少的业务,查询缓存命中率极低,反而成了性能负担。所以 8.0 后彻底删掉了查询缓存。

需要注意的是:8.0 之后删除的是 查询缓存(query cache),InnoDB Buffer Pool 仍然在发挥缓存作用,缓存的是 ⌈ 页 ⌋ (数据页、索引页),而不是整条 SQL 结果

3.解析器

做两件事:

  • 词法分析:识别关键字 select、from、where,分析这些关键字是什么意思
  • 语法分析:判断 SQL 写得合不合法,语法错在这里直接报错

本质上就是将 SQL 转换为 MySQL 可以理解的结构。

注意:分析器只检查语法,不检查语义(比如表是否存在、列是否存在是在执行阶段或预处理阶段检查的)。

4.优化器

SQL 语句可能有多种执行方式,优化器选代价最小、速度最快的:

例如:

SELECT * FROM user
WHERE age = 20
AND name = 'Tom';

优化器会决定:

  • 用哪个索引
  • 多表关联顺序
  • JOIN 顺序
  • 是否走全表扫描
  • 子查询是否转为半连接

也就是说,我们写的 SQL ≠ 真正的执行方式。真正的执行计划由优化器决定。这有点像编译器优化。注意,它选的其实不一定真的是最优的。

5.执行器

先判断对表有没有权限(连接器那一步获取的权限),如果有权限,再调用存储引擎(InnoDB)接口,真正去磁盘 / 内存一行行拿数据。

对于没有索引的查询,逐条读取并比对;有索引则按索引去查找满足条件的第一行,再下一条,直到不满足,然后返回结果给客户端。

存储引擎会决定是从 Buffer Pool 读页,还是从磁盘读页。

需要注意,执行器只是调用存储引擎接口,真正干活的是存储引擎,它真正负责数据存储、索引、事务、锁。MySQL 是老板,存储引擎是真正干活的人。

如果再详细一点,涉及到日志:

执行器 -> InnoDB引擎 -> 先写 redo log (prepare阶段) 
                    -> 写 binlog
                    -> redo log commit
  • redo log(重做日志):InnoDB 独有的物理日志,用于崩溃恢复。采用 WAL(Write-Ahead Logging),先写日志再刷盘,保证不丢数据。redo log 是循环写。

  • binlog(归档日志):Server 层的逻辑日志,记录语句的原始逻辑,用于主从复制数据恢复。binlog 是追加写。

  • 两阶段提交:先让 redo log 处于 prepare 状态,再写 binlog,最后提交 redo log 。这样确保两个日志一致,崩溃恢复时能确定事务状态。

循环写和追加写具体是什么意思,下文会讲。

打个比方

  • MySQL 服务层(连接器、分析器、优化器、执行器):公司老板 / 管理层
  • InnoDB 存储引擎仓库管理员
  • 磁盘:真实仓库货架
  • 内存 Buffer Pool:仓库门口的临时货架(缓存)

流程:

  1. 老板(执行器)说:帮我查 id=100 的数据
  2. 仓库管理员(InnoDB)自己决定

    • 先看门口临时货架(内存 BufferPool)有没有这条数据
    • 有 → 直接从内存拿,快
    • 没有 → 去真实仓库(磁盘)把数据页读出来,放到内存,再返回给老板

👉 注意:

**执行器只发指令,不碰磁盘、不管理内存缓存;

读内存还是读磁盘,全部是 InnoDB 自己内部搞定的。

再强调二者分工

1. 服务层(执行器)做什么

只干 3 件事:

  1. 经过优化器选定执行方案
  2. 调用 InnoDB 提供的接口:读一行、下一行
  3. 拿到数据后,做过滤、排序、聚合,返回给客户端

它完全不知道数据在内存还是磁盘,不关心物理存储细节。

2. 存储引擎(InnoDB)做什么

真正管事的:

  • 管理磁盘上的数据文件、索引文件
  • 管理内存缓冲池 Buffer Pool
  • 决定要不要缓存数据、淘汰旧数据
  • 处理事务、MVCC、行锁、redo/undo 日志
  • 响应执行器的「拿数据」请求,自己从内存 / 磁盘取
  • 负责事务一致性(ACID:Atomicity 原子性、Consistency 一致性、Isolation 隔离性、Durability 持久性)和数据完整性(外键、唯一约束等)

对于存储在磁盘文件的数据,InnoDB 会:

  • 分页存储
  • 使用缓存
  • 使用 B+ 树管理数据(数据页通过 B+ 树索引组织,主键索引叫聚簇索引,二级索引叫非聚簇索引)

后面我们再复习:为什么数据库不 ⌈按行连续存储⌋

另外,为什么 MySQL 敢说自己快?因为它不是直接读写硬盘。

InnoDB 内存中的 Buffer Pool 发挥了作用:

  • 读数据:先看内存里有没有,有就直接给,没有再从磁盘读并缓存。

  • 写数据:先修改内存中的数据页(标记为脏页),同时记录 redo log,然后异步刷盘。

另外,InnoDB 所有数据、索引都按 16KB 一页组织管理

InnoDB 最小磁盘 IO 单元是页,默认大小 16KB。一条数据很小,不会一条一次磁盘 IO,而是一次性加载一整页到缓冲池。索引、行数据都是以为单位组织存放在磁盘和内存中。

总结

执行器是发号施令的指挥官,InnoDB 是亲自跑腿、管内存管磁盘的实干者。

为什么是 InnoDB

1.InnoDB 支持事务:

例如:

BEGIN;

UPDATE account SET money = money - 100 WHERE id = 1;

UPDATE account SET money = money + 100 WHERE id = 2;

COMMIT;

所谓事务,就是要么全成功,要么全失败,不会在转账的时候出现你的账户少了100元,我的账户没有多100元的情况。

InnoDB 通过 undo log + redo log + binlog 保证事务原子性和持久性。

MyISAM 不支持。

2.InnoDB 支持行锁

例如:用户A修改 id=1,并不会影响用户B修改 id=2,并发相对高(但可能伴随竞态条件或死锁)。
而MyISAM是表锁,一个人改数据,整张表都可能被锁,并发较差。

注意,InnoDB是支持行锁+表锁,MyISAM 只支持表锁。

简单理解表锁和行锁:假设同时有两个用户执行下面这条 SQL:

UPDATE account SET money = money - 100 WHERE id = 1;
  • InnoDB
    • 如果两条 UPDATE 修改不同 id(行),行锁会让它们同时执行,不会出现 money 减 200 的情况
    • 如果修改相同 id,则第二条会等待第一条提交
  • MyISAM
    • 整表锁,所以第二条 UPDATE 必须等待第一条完成

3.InnoDB 支持崩溃恢复

即使突然断电,数据也不容易丢,因为有redo log 重做事务、undo log 回滚事务,这个后续会在日志篇详细复习。

4.InnoDB 使用聚簇索引

暂时先记住:数据和索引在一起(InnoDB 主键索引存储数据页),后续在索引篇详细复习

  • 优点
    • 主键查询非常快
    • 范围查询顺序存储,有利于扫描
  • 缺点
    • 插入顺序不对可能造成页分裂,写性能稍差
    • 二级索引查主键可能需要回表

而 MyISAM 使用非聚簇索引(索引指向地址

5.InnoDB 支持外键,MyISAM 不支持

总结

五个方面:

  • 事务
  • 锁粒度
  • 外键
  • 可靠性
  • 索引结构

自测

Q1:一条SQL怎么执行?

A1:
连接器(用户认证、建立TCP连接) ->
查缓存(MySQL 8.0 已删除,缓存命中率太低) ->
解析器(识别关键字、分析语法错误) ->
优化器(类似编译器优化,处理“选哪个索引”“多表查询哪个表先查”“JOIN顺序”之类的问题) ->
执行器(调用存储引擎接口) ->
存储引擎对真实物理数据进行操作 ->
执行器组装结果集

Q2:什么是存储引擎?

A2:MySQL相当于公司老板,存储引擎相当于公司员工。老板有调用员工的资格,可以类比为MySQL有调用存储引擎的接口。存储引擎管理磁盘/内存中的数据(B+树数据结构)、执行来自执行器的CRUD请求、处理事务/锁/MVCC/redo log/undo log。总之,存储引擎是真正干活的。

Q3:为什么是InnoDB?

A3:

原因一:InnoDB支持事务,MyISAM不支持。

所谓事务,就是指一段SQL语句要么都被执行、要么都不被执行。典型的应用场景是转账。如果没有事务,可能会出现A的账户少了100元,但他的转账对象B的账户并没有多出100元的情况。

原因二:InnoDB支持行锁,而MyISAM使用表锁。

对于InnoDB,A能修改id=1,B也能同时修改id=2,并发能力强,但也伴随竞态问题与死锁问题;对于MyISAM,一段时间内只能一个人修改,因为整张表都被锁住了。

原因三:InnoDB支持崩溃恢复

redo log 重做事务,undo log 回滚事务。InnoDB 崩溃恢复主要依靠 redo log 的 WAL 机制(Write-Ahead Logging,预写日志,简称 WAL)。断电重启后,InnoDB 会检查 redo log,将已提交但未写入磁盘的数据重放(redo),将未提交的事务通过 undo log 回滚,保证数据一致性。

原因四:InnoDB支持聚簇索引

暂时先记住:相当于主键与数据存在一起,实际上是主键索引存储数据页。

原因五:InnoDB 支持外键,MyISAM 不支持。

Q4:当执行更新语句时,InnoDB 为什么要先写日志,而不是直接写磁盘?

A4:如果直接写磁盘,想象一个场景:

MySQL 的数据存在磁盘上的 .ibd 文件里。这个文件被分成很多个 16KB 的数据页

当你执行 UPDATE users SET name = 'Tom' WHERE id = 100; 时:

  • 用户 id=100 的数据可能在磁盘文件的第 500 个页面。

  • 紧接着你执行 UPDATE orders SET status = 1 WHERE order_id = 999;

  • 订单数据可能在磁盘文件的第 2000 个页面。

如果你直接写磁盘,磁头就要在磁盘上飞快地跳来跳去(寻道),寻找第 500 页,写一点;再寻找第 2000 页,写一点。这种跳跃式的写入是缓慢的。

如果是先写日志,InnoDB 不会立刻去改磁盘里的那个 16KB 页面,而是先把这次修改的操作记录在 redo log(重做日志)里。

redo log 是追加写的:它就像一个无限延长的账本,新的记录永远写在末尾。(注意:这和上文说的redo log 循环写 binlog 追加写不矛盾,待会会说)

那么,这究竟快在哪里呢?主要有三个方面:

1.将同步变为异步
在没有 redo log 之前,为了保证数据不丢,你必须在用户点击“提交”后,原地等待磁头跳到第 500 页把数据写完,才能返回“更新成功”。这个等待过程(同步写)是用户感知的卡顿。

有了 redo log 之后,写日志是顺序的,极快。写完日志,MySQL 就立刻给用户返回成功了。至于数据什么时候从内存刷到第 500 页,那是后台线程的事(异步写)。用户不再需要为磁盘磁头的物理跳跃而等待。

2.合并写入

虽然磁头最终要去写第 500 页和第 2000 页,但后台线程并不会来一个写一个,而是采取攒一波再写的策略:

  • 场景 A(直接写):你在 1 分钟内对第 500 页修改了 10 次。磁头就要跳过去 10 次,写 10 次。
  • 场景 B(WAL 模式):这 10 次修改在内存里完成,并顺序记入 redo log。后台线程发现第 500 页很“热”,它会等这 10 次修改都完成后,只跳过去一次,把最终结果写入磁盘。

这就把 10 次随机 IO 变成了一次随机 IO。

3.磁盘调度优化(IO 排序)

当后台线程决定要把内存中的脏页(被修改过的页)刷回磁盘时,它手里可能积压了几百个不同位置的写入任务(比如第 500 页、第 2000 页、第 800 页……)。

如果直接写,磁头的轨迹可能是:500 -> 2000 -> 800,来回折返。 但在异步模式下,MySQL 或操作系统可以对这些任务进行 排序,让磁头按顺序走:500 -> 800 -> 2000。 这就像快递员送货,如果来一单送一单,他在城里乱跑;如果把一天的单子拿在手里规划路线,他只需要按顺序绕城一圈。

Q5:redo log 是循环写还是追加写?

A5:补充说一下为什么上文说redo log是循环写,这里又说redo log是追加写。这里需要澄清一个概念:“追加写”是指一种 IO 行为模式,而“循环写”是指一种空间管理策略。

1.物理层面:redo log 和 binlog 都是追加写

无论是 redo log 还是 binlog,在将数据写入磁盘那一刻,磁头的动作都是**顺序追加

  • redo Log 的追加:虽然它在磁盘上表现为固定大小的一组文件(比如 ib_logfile0ib_logfile1),但它内部维护了一个 write pos(当前写入位置)指针。每次写入都是从 write pos 开始向后顺序写入

  • binlog 的追加:它不断创建新文件(binlog.000001, binlog.000002...),也是一直向后追加

    2.空间管理策略的差异

特性 redo Log (重做日志) binlog (归档日志)
空间策略 循环写 (Circular) 追加写 (Append)
存储表现 空间固定。写满了会回到开头覆盖旧记录。 空间不固定。写满一个文件就开下一个。
覆盖前提 必须确保被覆盖的记录已经“落盘”(刷到数据页)。 永不覆盖,旧文件会一直保留直到被清理。
功能定位 崩溃恢复(保证最近的数据不丢)。 数据归档、主从复制(记录全量历史)。

总结:binlog 是“不覆盖的追加”,redo log 是“可覆盖的循环”。

3.为什么 Redo Log 要设计成循环写?

这是为了性能与空间的平衡

redo Log 只是暂存:它的任务是保证 Buffer Pool 里的脏页在还没来得及刷入 .ibd 文件时,如果掉电了能救回来。一旦脏页被成功刷入了磁盘,这段 redo log 就完成了使命,变成了无用记录。

避免无限膨胀:数据库每天的修改量巨大,如果 redo log 不循环覆盖,它会迅速吞噬所有磁盘空间。

Checkpoint 机制:InnoDB 维护了一个 checkpoint 标志。write pos 追着 checkpoint 跑。如果 write pos 快要追上 checkpoint 了(表示空间满了),MySQL 就会强制停下来,把内存里的脏页刷一波到磁盘,然后把 checkpoint 往前推,腾出空间。

Q6:一条UPDATE语句和一条SELECT语句,在执行流程上最主要的区别是什么?涉及了哪些SELECT没有的组件?

A6:UPDATE 在 InnoDB 层需要写 redo log 保证崩溃恢复,在 Server 层写 binlog 用于主从复制和数据恢复,并且为了保证两个日志一致,需要两阶段提交。SELECT完全不涉及这些日志。

具体流程:

1.执行器调用引擎获取数据

2.引擎修改内存中的数据页,写 undo log(用于回滚和MVCC)

3.写 redo log,状态为prepare

4.执行器写 binlog

5.提交事务,redo log 状态改为 commit(两阶段提交完成)

Q7:假设 MySQL 在写 binlog 的过程中突然宕机了,此时 redo log 处于 prepare 状态,binlog 还没写完。恢复后,MySQL 会怎么处理这个事务?是提交还是回滚?为什么?

A7:MySQL恢复后,会把这个事务回滚。

原因:

两阶段提交的恢复规则是:如果 redo log 处于 prepare 状态,就去检查对应的 binlog 是否完整。

本题场景下,binlog 还没写完就宕机,所以 binlog 不完整。

这时为了保证 redo log 和 binlog 数据一致,就会回滚事务,undo 掉 redo log 中 prepare 状态的数据。

那如果回滚,redo log 的数据不就丢失了吗?对,就是要让它丢失。因为 binlog 里没有这个操作,如果提交了,主从复制时从库会缺少这个操作,导致主从数据不一致。一致性大于一切。

Q8:执行器在操作表之前还会做一次权限校验,为什么连接器已经校验过了,执行器还要再校验?

A8:有些SQL在分析器阶段,可能还不知道要操作哪些表(比如存储过程中的动态SQL、某些触发器场景)。分析器只做语法检查,不检查表级权限。所以执行器在真正打开表执行之前,必须再校验一次该用户有没有这个表的操作权限。执行器校验的其实是表级别的权限,比如有没有SELECT、INSERT权限等。连接器只管能不能连进来。

目录
相关文章
|
14天前
|
人工智能 JSON 供应链
畅用7个月无影 JVS Claw |手把手教你把JVS改造成「科研与产业地理情报可视化大师」
LucianaiB分享零成本畅用JVS Claw教程(学生认证享7个月使用权),并开源GeoMind项目——将JVS改造为科研与产业地理情报可视化AI助手,支持飞书文档解析、地理编码与腾讯地图可视化,助力产业关系图谱构建。
23497 12
畅用7个月无影 JVS Claw |手把手教你把JVS改造成「科研与产业地理情报可视化大师」
|
3天前
|
人工智能 BI 持续交付
Claude Code 深度适配 DeepSeek V4-Pro 实测:全场景通关与真实体验报告
在 AI 编程工具日趋主流的今天,Claude Code 凭借强大的任务执行、工具调用与工程化能力,成为开发者与自动化运维的核心效率工具。但随着原生模型账号稳定性问题频发,寻找一套兼容、稳定、能力在线的替代方案变得尤为重要。DeepSeek V4-Pro 作为新一代高性能大模型,提供了完整兼容 Claude 协议的 API 接口,只需简单配置即可无缝驱动 Claude Code,且在任务执行、工具调用、复杂流程处理上表现极为稳定。
1050 0
|
8天前
|
人工智能 缓存 Shell
Claude Code 全攻略:命令大全 + 实战工作流(完整版)
Claude Code 是一款运行在终端环境下的 AI 编码助手,能够直接在项目目录中理解代码结构、编辑文件、执行命令、执行开发计划,并支持持久化记忆、上下文压缩、后台任务、多模型切换等专业能力。对于日常开发、项目维护、快速重构、代码审查等场景,它可以大幅减少手动操作、提升编码效率。本文从常用命令、界面模式、核心指令、记忆机制、图片处理、进阶工作流等维度完整说明,帮助开发者快速上手并稳定使用。
1963 4
|
18天前
|
人工智能 缓存 BI
Claude Code + DeepSeek V4-Pro 真实评测:除了贵,没别的毛病
JeecgBoot AI专题研究 把 Claude Code 接入 DeepSeek V4Pro,跑完 Skills —— OA 审批、大屏、报表、部署 5 大实战场景后的真实体验 ![](https://oscimg.oschina.net/oscnet/up608d34aeb6bafc47f
5666 21
Claude Code + DeepSeek V4-Pro 真实评测:除了贵,没别的毛病
|
19天前
|
人工智能 JSON BI
DeepSeek V4 来了!超越 Claude Sonnet 4.5,赶紧对接 Claude Code 体验一把
JeecgBoot AI专题研究 把 Claude Code 接入 DeepSeek V4Pro 的真实体验与避坑记录 本文记录我将 Claude Code 对接 DeepSeek 最新模型(V4Pro)后的真实体验,测试了 Skills 自动化查询和积木报表 AI 建表两个场景——有惊喜,也踩
6777 16
|
7天前
|
前端开发 API 内存技术
对比claude code等编程cli工具与deepseek v4的适配情况
DeepSeek V4发布后,多家编程工具因未适配其强制要求的`reasoning_content`字段而报错。本文对比Claude Code、GitHub Copilot、Langcli、OpenCode及DeepSeek-TUI等主流工具的兼容性:Claude Code需按官方方式配置;Langcli表现最佳,开箱即用且无报错;Copilot与OpenCode暂未修复问题;DeepSeek-TUI尚处早期阶段。
1261 3
对比claude code等编程cli工具与deepseek v4的适配情况
|
7天前
|
人工智能 前端开发 测试技术
Qoder Skills 完全指南:从零开始,让 AI 按你的标准执行
文章内容基于作者个人技术实践与独立思考,旨在分享经验,仅代表个人观点。