问题

MySQL有哪些存储引擎?它们的特点和区别是什么?

答案

1. 核心概念

MySQL采用插件式存储引擎架构,将查询处理数据存储分离,允许为不同的表选择不同的存储引擎。

核心存储引擎

  • InnoDB(默认,最常用)
  • MyISAM(早期默认,已淘汰)
  • Memory(内存引擎)
  • CSV
  • Archive
  • Blackhole
  • Federated

MySQL 5.5+ 默认存储引擎:InnoDB


2. 查看和设置存储引擎

2.1 查看存储引擎

-- 查看支持的存储引擎
SHOW ENGINES;

-- 查看默认存储引擎
SHOW VARIABLES LIKE 'default_storage_engine';

-- 查看表的存储引擎
SHOW TABLE STATUS FROM mydb LIKE 'orders';

-- 查看表的创建语句(包含引擎信息)
SHOW CREATE TABLE orders;

输出示例

Engine    Support  Comment
InnoDB    DEFAULT  Supports transactions, row-level locking, and foreign keys
MyISAM    YES      MyISAM storage engine
Memory    YES      Hash based, stored in memory
CSV       YES      CSV storage engine
Archive   YES      Archive storage engine

2.2 设置存储引擎

-- 创建表时指定引擎
CREATE TABLE orders (
    id INT PRIMARY KEY,
    status VARCHAR(20)
) ENGINE=InnoDB;

-- 修改已有表的引擎
ALTER TABLE orders ENGINE=InnoDB;

-- 设置默认引擎(全局)
SET GLOBAL default_storage_engine = InnoDB;

-- 设置默认引擎(会话)
SET SESSION default_storage_engine = MyISAM;

3. InnoDB存储引擎(重点)

3.1 核心特点

特性 支持 说明
事务 ✅ 支持 支持ACID特性
行级锁 ✅ 支持 并发性能高
外键 ✅ 支持 保证参照完整性
MVCC ✅ 支持 多版本并发控制
崩溃恢复 ✅ 支持 Redo Log + Undo Log
全文索引 ✅ 支持(5.6+) 支持中文分词(5.7+)
空间索引 ✅ 支持(5.7+) 地理信息系统
聚簇索引 ✅ 是 数据按主键顺序存储

3.2 核心架构

内存结构

  • Buffer Pool:缓存数据页和索引页
  • Change Buffer:缓存非唯一二级索引的变更
  • Adaptive Hash Index:自适应哈希索引
  • Log Buffer:缓存Redo Log

磁盘结构

  • 表空间(Tablespace)
    • 系统表空间:ibdata1
    • 独立表空间:表名.ibd(推荐)
  • Redo Log:重做日志(ib_logfile0/1)
  • Undo Log:回滚日志(存储在ibdata1或独立表空间)

3.3 事务支持

-- 标准事务操作
BEGIN;

INSERT INTO orders (id, user_id, amount) VALUES (1, 100, 200);
UPDATE accounts SET balance = balance - 200 WHERE id = 100;

-- 提交事务
COMMIT;

-- 或回滚事务
ROLLBACK;

-- 设置保存点
SAVEPOINT sp1;
INSERT INTO orders VALUES (2, 101, 300);
ROLLBACK TO SAVEPOINT sp1; -- 回滚到sp1

ACID特性

特性 说明 InnoDB实现
原子性(Atomicity) 事务要么全部成功,要么全部失败 Undo Log
一致性(Consistency) 事务前后数据完整性保持一致 约束、触发器
隔离性(Isolation) 多个事务并发执行互不干扰 MVCC + 锁
持久性(Durability) 事务提交后永久保存 Redo Log

3.4 锁机制

行级锁

-- 共享锁(S锁)
SELECT * FROM orders WHERE id = 1 LOCK IN SHARE MODE;

-- 排他锁(X锁)
SELECT * FROM orders WHERE id = 1 FOR UPDATE;

-- 自动加排他锁
UPDATE orders SET status = 'paid' WHERE id = 1;
DELETE FROM orders WHERE id = 1;
INSERT INTO orders VALUES (1, 100, 200);

间隙锁和临键锁(防止幻读):

-- 可重复读隔离级别下
BEGIN;
SELECT * FROM orders WHERE id BETWEEN 1 AND 10 FOR UPDATE;
-- 锁定范围:(上一个记录, 10]
-- 其他事务无法在此范围内插入记录

3.5 MVCC多版本并发控制

原理

  • 每行记录包含隐藏字段:DB_TRX_ID(事务ID)、DB_ROLL_PTR(回滚指针)
  • 通过Undo Log版本链保存历史版本
  • 通过ReadView判断数据可见性

优点

  • 快照读不加锁,读写不冲突
  • 大幅提升并发性能
-- 快照读(使用MVCC)
SELECT * FROM orders WHERE id = 1;

-- 当前读(加锁)
SELECT * FROM orders WHERE id = 1 FOR UPDATE;

3.6 崩溃恢复

Redo Log(重做日志)

作用:保证持久性
机制:WAL(Write-Ahead Logging)
流程:
1. 修改Buffer Pool中的数据页(脏页)
2. 先写Redo Log(顺序IO,快速)
3. 事务提交
4. 后台异步刷脏页到磁盘(随机IO,慢)

崩溃恢复:
- 重启时,读取Redo Log
- 重做已提交但未刷盘的事务

Undo Log(回滚日志)

作用:保证原子性和MVCC
机制:记录修改前的数据
流程:
1. 修改数据前,先记录旧值到Undo Log
2. 事务提交后,Undo Log不会立即删除(用于MVCC)
3. Purge线程异步清理不再需要的Undo Log

回滚:
- 事务失败时,读取Undo Log
- 恢复修改前的数据

3.7 聚簇索引

特点

  • 数据按主键顺序物理存储
  • 主键索引的叶子节点存储完整行数据
  • 二级索引的叶子节点存储主键值

优点

  • 主键查询极快
  • 范围查询性能好
  • 避免回表(覆盖索引)

缺点

  • 二级索引需要回表(先查主键,再查数据)
  • 插入时可能页分裂(主键乱序)
-- 主键查询(直接返回数据)
SELECT * FROM orders WHERE id = 1; -- 1次IO

-- 二级索引查询(需要回表)
SELECT * FROM orders WHERE user_id = 100; -- 2次IO
-- 1. 查二级索引,获取主键id
-- 2. 查主键索引,获取完整行数据

-- 覆盖索引(无需回表)
CREATE INDEX idx_user_status ON orders(user_id, status);
SELECT status FROM orders WHERE user_id = 100; -- 1次IO

3.8 适用场景

适合InnoDB的场景

  • 需要事务支持(金融、电商、订单)
  • 并发写入(用户评论、日志记录)
  • 数据完整性要求高(外键约束)
  • 需要崩溃恢复(关键业务数据)
  • 行级锁并发(热点数据更新)

实际应用

-- 订单表(需要事务)
CREATE TABLE orders (
    id BIGINT PRIMARY KEY,
    user_id BIGINT,
    amount DECIMAL(10,2),
    status VARCHAR(20),
    INDEX idx_user_id (user_id)
) ENGINE=InnoDB;

-- 账户表(需要强一致性)
CREATE TABLE accounts (
    id BIGINT PRIMARY KEY,
    balance DECIMAL(10,2),
    version INT -- 乐观锁
) ENGINE=InnoDB;

4. MyISAM存储引擎

4.1 核心特点

特性 支持 说明
事务 ❌ 不支持 无法回滚
行级锁 ❌ 不支持 仅支持表级锁
外键 ❌ 不支持 无参照完整性
MVCC ❌ 不支持 无多版本
崩溃恢复 ⚠️ 较差 容易损坏
全文索引 ✅ 支持(5.6前唯一) 早期优势
空间索引 ✅ 支持 地理信息系统
聚簇索引 ❌ 否 数据和索引分离
压缩表 ✅ 支持 只读表可压缩

4.2 文件结构

每张表包含3个文件:

orders.frm  -- 表结构定义(MySQL 8.0后改为.sdi)
orders.MYD  -- 数据文件(MyData)
orders.MYI  -- 索引文件(MyIndex)

4.3 锁机制

表级锁

-- 读锁(共享锁)
LOCK TABLES orders READ;
SELECT * FROM orders; -- ✅ 可以读
UPDATE orders SET status = 'paid'; -- ❌ 被阻塞

UNLOCK TABLES;

-- 写锁(排他锁)
LOCK TABLES orders WRITE;
SELECT * FROM orders; -- ✅ 可以读
UPDATE orders SET status = 'paid'; -- ✅ 可以写
-- 其他会话的所有操作都被阻塞

UNLOCK TABLES;

并发插入

-- MyISAM支持并发插入(表尾)
SET GLOBAL concurrent_insert = 2;

-- 会话A:SELECT
SELECT * FROM orders;

-- 会话B:INSERT(不阻塞)
INSERT INTO orders VALUES (100, 'new');

4.4 非聚簇索引

特点

  • 主键索引和二级索引结构相同
  • 叶子节点存储数据文件的物理地址
-- 主键查询
SELECT * FROM orders WHERE id = 1;
-- 1. 查主键索引树,获取物理地址
-- 2. 根据地址读取数据

-- 二级索引查询
SELECT * FROM orders WHERE user_id = 100;
-- 1. 查二级索引树,获取物理地址
-- 2. 根据地址读取数据
-- 无需回表,性能较好

4.5 优缺点

优点

  • 查询速度快(无事务开销)
  • 表级锁简单(开销小)
  • 占用空间小(无Undo Log)
  • 支持压缩表(只读场景)

缺点

  • 不支持事务(数据一致性差)
  • 表级锁(并发写入性能差)
  • 崩溃恢复差(容易损坏)
  • 不支持外键

4.6 适用场景

适合MyISAM的场景(已很少使用):

  • 只读或大量读取(日志分析、历史数据)
  • 插入不频繁(归档数据)
  • 不需要事务(统计报表)
  • 全文索引(MySQL 5.6之前)

⚠️ 现实情况

  • MySQL 5.5+ 默认使用InnoDB
  • InnoDB已全面超越MyISAM
  • 建议:新项目统一使用InnoDB

5. InnoDB vs MyISAM 对比

5.1 核心差异

对比项 InnoDB MyISAM
事务支持 ✅ 支持ACID ❌ 不支持
锁粒度 行级锁 表级锁
外键 ✅ 支持 ❌ 不支持
MVCC ✅ 支持 ❌ 不支持
崩溃恢复 ✅ 自动恢复 ⚠️ 容易损坏
索引类型 聚簇索引 非聚簇索引
全文索引 ✅ 5.6+ ✅ 一直支持
空间索引 ✅ 5.7+ ✅ 一直支持
主键必须 ✅ 建议有 ❌ 可选
COUNT(*) 慢(扫描) 快(存储计数)
AUTO_INCREMENT 重启后可能变化 持久化
存储空间 较大(Undo Log) 较小
并发写入 高(行锁) 低(表锁)
并发读取 高(MVCC) 中(表锁)
数据安全性 高(Redo Log)

5.2 性能对比

COUNT(*) 查询

-- MyISAM:极快(存储了行数)
SELECT COUNT(*) FROM orders; -- 1ms

-- InnoDB:较慢(需要扫描)
SELECT COUNT(*) FROM orders; -- 100ms+

-- 优化方案:
-- 1. 使用近似值
SELECT table_rows FROM information_schema.tables
WHERE table_name = 'orders';

-- 2. 使用Redis计数器
-- 3. 使用汇总表

插入性能

-- InnoDB(事务开销)
INSERT INTO orders VALUES (1, 100, 200); -- 需要写Redo Log和Undo Log

-- MyISAM(无事务)
INSERT INTO orders VALUES (1, 100, 200); -- 直接写数据文件

-- 批量插入优化
BEGIN;
INSERT INTO orders VALUES (1, 100, 200);
INSERT INTO orders VALUES (2, 101, 300);
-- ... 1000条
COMMIT; -- InnoDB批量提交,性能更好

范围查询

-- InnoDB(聚簇索引)
SELECT * FROM orders WHERE id BETWEEN 1 AND 1000;
-- 数据按主键顺序存储,范围查询快

-- MyISAM(非聚簇索引)
SELECT * FROM orders WHERE id BETWEEN 1 AND 1000;
-- 数据随机存储,范围查询可能需要多次随机IO

5.3 选择建议

使用InnoDB的场景(推荐):

  • ✅ 所有需要事务的场景
  • ✅ 高并发写入
  • ✅ 需要数据完整性和一致性
  • ✅ 需要崩溃恢复
  • ✅ 需要外键约束
  • 默认选择(99%的场景)

使用MyISAM的场景(不推荐):

  • ⚠️ 仅查询的历史数据(已归档)
  • ⚠️ 不重要的日志数据
  • ⚠️ 临时表、中间表

迁移方案(MyISAM → InnoDB):

-- 1. 备份数据
mysqldump -u root -p mydb orders > orders_backup.sql

-- 2. 转换引擎
ALTER TABLE orders ENGINE=InnoDB;

-- 3. 验证
SHOW CREATE TABLE orders;
SELECT * FROM orders LIMIT 10;

-- 4. 优化表
OPTIMIZE TABLE orders;

6. 其他存储引擎

6.1 Memory(HEAP)

特点

  • 数据存储在内存中,重启后丢失
  • 支持哈希索引和B+树索引
  • 表级锁,不支持事务

适用场景

  • 临时数据、会话数据
  • 缓存表、中间结果表
  • 实时统计

示例

CREATE TABLE sessions (
    session_id VARCHAR(64) PRIMARY KEY,
    user_id BIGINT,
    login_time DATETIME,
    INDEX idx_user (user_id) USING HASH
) ENGINE=Memory;

-- 优点:查询极快
SELECT * FROM sessions WHERE session_id = 'abc123'; -- 微秒级

-- 缺点:重启丢失
-- 重启后sessions表变为空表

替代方案

  • 使用Redis、Memcached(更可靠)
  • 使用InnoDB + Buffer Pool(持久化)

6.2 CSV

特点

  • 数据以CSV格式存储
  • 可直接用文本编辑器查看
  • 不支持索引

适用场景

  • 数据导入/导出
  • 与其他系统交换数据

示例

CREATE TABLE logs_export (
    id INT,
    message TEXT,
    created_at DATETIME
) ENGINE=CSV;

INSERT INTO logs_export VALUES (1, 'Error message', NOW());

-- 文件位置:/var/lib/mysql/mydb/logs_export.CSV
-- 内容:
-- 1,"Error message","2025-11-02 10:00:00"

6.3 Archive

特点

  • 高压缩比(zlib压缩)
  • 仅支持INSERTSELECT
  • 不支持UPDATE、DELETE
  • 不支持索引(除了AUTO_INCREMENT)

适用场景

  • 日志归档
  • 历史数据存储

示例

CREATE TABLE access_logs (
    id INT AUTO_INCREMENT PRIMARY KEY,
    ip VARCHAR(15),
    url TEXT,
    created_at DATETIME
) ENGINE=Archive;

-- 插入1亿条日志
INSERT INTO access_logs (ip, url, created_at) VALUES ...;

-- 优点:占用空间极小(压缩比10:1甚至更高)
-- 缺点:无法修改和删除

6.4 Blackhole

特点

  • 数据写入后立即丢弃(黑洞)
  • 不存储任何数据
  • 仅记录Binary Log

适用场景

  • 主从复制(过滤特定表)
  • 性能测试(测试SQL语句本身)

示例

CREATE TABLE test_blackhole (
    id INT,
    message TEXT
) ENGINE=Blackhole;

INSERT INTO test_blackhole VALUES (1, 'This will be discarded');
SELECT * FROM test_blackhole; -- 返回空(数据被丢弃)

6.5 Federated

特点

  • 访问远程MySQL服务器的表
  • 类似于数据库链接(Database Link)
  • 数据不在本地存储

适用场景

  • 跨数据库查询
  • 分布式数据库

示例

-- 远程服务器(192.168.1.100)有表orders
CREATE TABLE orders (
    id INT PRIMARY KEY,
    status VARCHAR(20)
) ENGINE=InnoDB;

-- 本地服务器创建Federated表
CREATE TABLE remote_orders (
    id INT PRIMARY KEY,
    status VARCHAR(20)
) ENGINE=Federated
CONNECTION='mysql://user:password@192.168.1.100:3306/mydb/orders';

-- 查询本地表,实际查询远程表
SELECT * FROM remote_orders WHERE id = 1;

缺点

  • 性能差(网络延迟)
  • 不支持事务
  • 不推荐生产环境使用

7. 存储引擎选择策略

7.1 决策树

是否需要事务?
├─ 是 → InnoDB ✅
└─ 否 → 是否需要高并发写入?
    ├─ 是 → InnoDB ✅
    └─ 否 → 是否需要持久化?
        ├─ 是 → InnoDB ✅
        └─ 否 → 是否需要极高性能?
            ├─ 是 → Memory ⚠️(考虑Redis)
            └─ 否 → 是否仅归档?
                ├─ 是 → Archive ⚠️
                └─ 否 → InnoDB ✅(默认)

7.2 实际建议

99%的场景:使用InnoDB

特殊场景

  • 会话存储:Redis(不推荐Memory)
  • 日志归档:InnoDB + 定期压缩(不推荐Archive)
  • 临时数据:InnoDB临时表(不推荐Memory)

混合使用(同一个库):

-- 核心业务表:InnoDB
CREATE TABLE orders (
    id BIGINT PRIMARY KEY,
    user_id BIGINT,
    amount DECIMAL(10,2)
) ENGINE=InnoDB;

-- 用户信息表:InnoDB
CREATE TABLE users (
    id BIGINT PRIMARY KEY,
    username VARCHAR(50),
    email VARCHAR(100)
) ENGINE=InnoDB;

-- 会话表:Memory(谨慎)
CREATE TABLE sessions (
    session_id VARCHAR(64) PRIMARY KEY,
    user_id BIGINT,
    expire_time DATETIME
) ENGINE=Memory;

-- 操作日志(归档):Archive(谨慎)
CREATE TABLE access_logs_archive (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT,
    action VARCHAR(50),
    created_at DATETIME
) ENGINE=Archive;

8. 总结

8.1 核心存储引擎对比

引擎 事务 锁粒度 外键 崩溃恢复 适用场景 推荐度
InnoDB 行锁 所有OLTP场景 ⭐⭐⭐⭐⭐
MyISAM 表锁 ⚠️ 历史遗留系统
Memory 表锁 临时数据(不推荐) ⭐⭐
CSV 表锁 ⚠️ 数据导入导出 ⭐⭐
Archive 行锁 日志归档 ⭐⭐

8.2 InnoDB核心优势

  1. 事务支持:ACID保证数据一致性
  2. 行级锁:高并发场景性能好
  3. MVCC:读写不冲突
  4. 崩溃恢复:Redo Log保证持久性
  5. 外键约束:保证数据完整性

8.3 面试要点

简洁回答

MySQL主要存储引擎是InnoDB(默认)和MyISAM。InnoDB支持事务、行级锁、外键和MVCC,适合高并发写入和需要数据一致性的场景;MyISAM不支持事务,使用表级锁,适合只读场景,但现在已很少使用。实际开发中,建议统一使用InnoDB

进阶回答

InnoDB的核心优势在于:

  1. 事务支持:通过Redo Log和Undo Log实现ACID
  2. 行级锁:配合MVCC实现高并发读写
  3. 聚簇索引:数据按主键顺序存储,范围查询性能好
  4. 崩溃恢复:WAL机制保证数据不丢失

MyISAM的表级锁和不支持事务使其在现代应用中几乎被淘汰。特殊场景可以考虑Memory(会话数据)或Archive(日志归档),但更推荐使用Redis和压缩策略。

源码层面

  • InnoDB存储引擎源码位于storage/innobase/
  • 核心模块包括:Buffer Pool、Lock、Transaction、Redo Log、Undo Log、B+Tree索引
  • 可重点关注trx0trx.cc(事务)、lock0lock.cc(锁)、log0log.cc(Redo Log)