MySQL 存储引擎是什么?

上一篇顺着一条 SQL 走完了 Server 层到引擎层的链路,最后停在存储引擎层,说数据真正存在那里。这篇把这一层单独拿出来看:它到底管什么,MySQL 有哪些引擎,平时为什么用 InnoDB。

先澄清一个容易误解的点:存储引擎不是一个可选项,每张表都必须有一个 。建表不写 ENGINE,它就取默认值,MySQL 5.5 之后默认是 InnoDB。


存储引擎管什么

Server 层把 SQL 解析、优化成一份执行计划,但"数据存在磁盘的什么位置、怎么找、怎么改、加锁加到什么粒度"是存储引擎的事。

回忆一下执行器那一段:执行器调用的是一套引擎接口,有没有索引决定了它调"取第一行"还是"取满足条件的下一行",但它不知道数据到底是怎么找到的,那是引擎内部的实现。

事情 谁负责
SQL 语法语义、权限、执行计划 Server 层
数据怎么组织、怎么读写、怎么加锁、支不支持事务 存储引擎层

这个分工带来的直接结果是引擎可插拔:换引擎不用改 SQL,换的是底层行为。所以"用什么引擎"这个问题,本质是在问"这张表要不要事务、要不要行锁、数据怎么组织"。


有哪些引擎

SHOW ENGINES 列出当前实例支持哪些,Support 是 DEFAULT 的那一行就是默认引擎:

text 复制代码
Engine             Support  Transactions
InnoDB             DEFAULT  YES
MyISAM             YES      NO
MEMORY             YES      NO
ARCHIVE            YES      NO
CSV                YES      NO
BLACKHOLE          YES      NO
MRG_MYISAM         YES      NO
PERFORMANCE_SCHEMA YES      NO

建表时用 ENGINE 指定,不写就是 InnoDB:

sql 复制代码
CREATE TABLE t (
  id   int primary key,
  name varchar(20)
) ENGINE = MyISAM;

一个库里不同的表可以用不同的引擎,引擎的粒度是表,不是库。

真正需要认识的只有三个:InnoDB、MyISAM、Memory,剩下的 Archive、CSV、Blackhole 都是特定用途的边角引擎,日常基本用不到。


InnoDB

InnoDB 是现在的默认引擎,也是绝大多数场景唯一该选的。它和 MyISAM 的差别不是性能高低,而是支持的语义完全不同。

事务、行锁、外键

这三样是 MyISAM 给不了的:

  • 事务 :InnoDB 支持 BEGIN / COMMIT / ROLLBACK,一组操作要么全成要么全不成,靠的是 undo log 和 redo log
  • 行级锁:只锁被改的那几行,别的事务改别的行不受影响,并发高
  • 外键:真正能强制外键约束的只有 InnoDB

关于行锁有一个常被忽略的点:行锁是加在索引上的 。如果 UPDATE 的条件没走索引,InnoDB 定位不到具体是哪些行,就只能把整张表锁住,行为退化成表锁。所以"没走索引"不只是查询慢,写操作还会把并发一起拖垮。

崩溃恢复

MyISAM 是没有崩溃恢复能力的。InnoDB 有两套日志兜底:进程或者机器挂掉,重启的时候先按 redo log 把已提交但没落盘的改动重做,再按 undo log 把没提交的事务回滚掉,数据自动回到一致状态。

这套东西的细节在 Redo Log 那篇里,这里只要记住:InnoDB 掉电不会把表搞坏,MyISAM 会。

数据就存在主键索引里

InnoDB 的表,数据行本身存在主键索引的叶子节点上,这种结构叫聚簇索引(clustered index)。也就是说主键索引的叶子存的就是完整的一行,找到主键就找到了整行数据。

这带来两个后果:

  1. 主键即位置。按主键查最快,一次索引查找就拿到整行;按普通索引查,叶子节点里存的是主键值,得再回主键索引查一次,这就是回表
  2. 主键最好用自增的。主键决定了行在页里的物理顺序,如果主键是随机的(比如 UUID),新插入的行可能落进任意一个已满的页,触发页分裂和数据搬移;自增主键则总是往最后一页追加

顺带解释一个常见问题:为什么 InnoDB 的 count(*) 很慢,MyISAM 却很快?MyISAM 直接在文件头存了行数,返回的是常数。InnoDB 不行,因为同一行在不同事务眼里存不存在都不一样(MVCC 的多版本),没法维护一个全局精确的行数,只能一行行数。


MyISAM

MyISAM 是早年的默认引擎,现在除了个别场景已经不用了,但面试里还常和 InnoDB 摆在一起对比。

表锁,不支持事务

MyISAM 的锁粒度是表级:写操作会把整张表锁住,别的连接读写这张表都得等。加上不支持事务,一组操作中间挂了,前面已经改掉的不会回滚。

所以 MyISAM 只适合读多写少、且对一致性要求不高的场景。

索引和数据分开存

MyISAM 的数据和索引是两个独立的文件,一张表对应三个:

文件 内容
.frm 表结构
.MYD 数据
.MYI 索引

它的索引是非聚簇 的:B+ 树的叶子节点存的不是整行,而是数据行的物理地址,拿到地址再去 .MYD 里定位。正好和 InnoDB 相反,InnoDB 是索引即数据。

count(*) 快,但会坏

前面说过,MyISAM 的文件头存了总行数,count(*) 不带 where 时直接返回,不用扫表,这一点上确实比 InnoDB 快。

反过来它没有崩溃恢复日志,服务器突然断电,表可能处于损坏状态,重启后要用 REPAIR TABLE 尝试修复,修不好的部分数据就丢了。InnoDB 不会出现这个问题。


Memory 和其它

MEMORY 把数据全放在内存里,用哈希索引,重启之后数据全丢,只剩表结构。适合做临时表,或者放一些能随时重建的小尺寸缓存。因为表锁加上哈希索引只擅长等值查找,拿它当主力存储不合适。

其余几个基本用不到,知道大概就行:

引擎 特点
ARCHIVE 只支持插入和查询,压缩存储,适合归档日志
CSV 数据以 CSV 文本存,能被外部程序直接读,不支持索引和事务
BLACKHOLE 写进去就丢,读永远为空,只用来验证 binlog 之类的链路

InnoDB 和 MyISAM 对比

维度 InnoDB MyISAM
事务 支持 不支持
锁粒度 行级锁 表级锁
外键 支持 不支持
崩溃恢复 有 redo/undo,自动恢复 无,可能损坏需修复
索引结构 聚簇索引,索引即数据 非聚簇,索引存行地址
count(*) 要扫表,慢 存了行数,快
全文索引 5.6 之后支持 早就支持
适用场景 绝大多数场景 读多写少、无事务要求的旧系统

怎么选

默认选 InnoDB,这句话适用于绝大多数情况。需要事务、需要并发写、需要崩溃后能自动恢复,这些都是现代业务的底线要求,MyISAM 给不了。

改成其它引擎的理由只有两类:数据明确不需要持久化(MEMORY),或者只需要顺序写入归档(ARCHIVE)。不要为了"读快"去选 MyISAM,它那点读性能优势在 InnoDB 的 buffer pool 面前基本不存在,却要拿掉事务和崩溃恢复,这笔账不划算。

相关推荐
星云低代码开发平台1 小时前
ERP 与 CRM 客户信用核对:业务键、时点和权限验收
大数据·数据库·人工智能
for_ever_love__1 小时前
MySQL 8.0 数据类型选型指南:INT、DECIMAL、VARCHAR 与时间字段怎么选
mysql·adb·数据类型·varchar·长度·字段类型选择
xiaogai_gai1 小时前
销售出库单同步实战:旺店通→金蝶云星空的单一策略深度拆解
大数据·前端·数据库
下页、再停留1 小时前
【C#桌面客户端系列学习-11】将SQLite数据库打包到exe里
数据库·c#·visual studio
下页、再停留1 小时前
【C#桌面客户端系列学习-12】封装SQLite数据库基础操作工具类(CURD)
数据库·c#·visual studio
LYmatch1 小时前
2024年408真题第41~47题汇总解析
数据库
程序员Sunday1 小时前
Redis 缓存穿透、击穿、雪崩怎么区分?从回源压力找原因
数据库·redis·缓存
我科绝伦(Huanhuan Zhou)1 小时前
磐维数据库(PanWeiDB)日常运维命令总结
运维·数据库
guo_wen_qiang1 小时前
redis实现秒杀功能-超卖&库存遗留问题的解决
数据库·redis·缓存