前言
在学习 MySQL 的时候,我们经常会听到一个词:存储引擎。
很多初学者刚开始会觉得,MySQL 不就是用来建表、插入数据、查询数据的吗?为什么还要单独去理解存储引擎?
原因很简单:存储引擎决定了一张表的数据到底怎么存、怎么查、怎么加锁、是否支持事务、崩溃后能不能恢复。
也就是说,SQL 只是我们操作数据库的方式,而真正负责把数据落到磁盘、维护索引、处理事务和并发的,是底层的存储引擎。
这一篇文章我们先了解存储引擎的作用,然后重点学习 MySQL 中最常用的 InnoDB,最后简单了解 MyISAM 和 MEMORY,并对它们做一个对比。
一、存储引擎的作用
MySQL 的存储引擎可以理解为:表的底层数据管理方式。
不同存储引擎的核心差异主要体现在下面几个方面:
- 是否支持事务
- 是否支持行级锁
- 是否支持外键
- 索引和数据如何存储
- 崩溃后是否支持恢复
- 适合读多还是写多场景
- 数据是存在磁盘还是内存中
我们可以通过下面的 SQL 查看当前 MySQL 支持哪些存储引擎:
sql
SHOW ENGINES;
创建表时,也可以手动指定存储引擎:
sql
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
age INT
) ENGINE = InnoDB;
如果不指定,在现代 MySQL 中,默认一般会使用 InnoDB。
二、重点理解 InnoDB 存储引擎
InnoDB 是 MySQL 中最重要、最常用的存储引擎。我们平时开发项目,只要涉及订单、用户、支付、库存、审批、后台管理系统等业务,基本都会优先使用 InnoDB。
它最大的特点可以概括为一句话:
InnoDB 适合需要事务、安全性和高并发读写的业务场景。
1. InnoDB 支持事务
事务是 InnoDB 最核心的能力之一。
事务可以保证一组 SQL 要么全部成功,要么全部失败。比如转账场景:
sql
START TRANSACTION;
UPDATE account SET balance = balance - 100 WHERE id = 1;
UPDATE account SET balance = balance + 100 WHERE id = 2;
COMMIT;
如果中间某一步失败,就可以回滚:
sql
ROLLBACK;
事务主要保证四个特性,也就是常说的 ACID:
- 原子性:事务中的操作要么都成功,要么都失败
- 一致性:事务执行前后,数据必须保持合理状态
- 隔离性:多个事务之间互不干扰
- 持久性:事务提交后,数据要真正保存下来
这也是为什么真实业务系统中很少使用不支持事务的存储引擎。
2. InnoDB 支持行级锁
InnoDB 支持行级锁,也就是只锁住需要修改的那一行数据。
例如:
sql
UPDATE user SET age = 20 WHERE id = 1;
如果 id 是索引字段,InnoDB 通常只会锁住 id = 1 这一行,而不是把整张表都锁住。
这对高并发非常重要。
假设有很多用户同时修改自己的个人信息,如果每次修改都锁整张表,效率会非常低。而行级锁可以让不同用户的数据修改互不影响,从而提升并发能力。
不过这里要注意一点:行级锁依赖索引。如果条件没有命中索引,可能会导致锁范围扩大,甚至接近表锁效果。
3. InnoDB 支持 MVCC
InnoDB 的并发能力不仅来自行级锁,还来自 MVCC,也就是多版本并发控制。
简单理解,MVCC 的作用是:
读操作不用一直等写操作,写操作也不用一直阻塞普通读操作。
比如一个事务正在修改某条数据,另一个事务想查询这条数据。InnoDB 不一定让查询一直等待,而是可能读取这条数据之前的历史版本。
这样可以减少锁冲突,提高数据库并发性能。
MVCC 主要依赖:
- 隐藏字段
- undo log
- Read View
这些机制配合起来,使 InnoDB 在可重复读等隔离级别下,能够实现比较好的并发读写效果。
4. InnoDB 使用聚簇索引
InnoDB 的表数据是按照主键组织存储的,这种结构叫做聚簇索引。
简单来说:
InnoDB 的主键索引叶子节点中存放的就是整行数据。
例如一张用户表:
sql
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
age INT
) ENGINE = InnoDB;
这里的 id 是主键,InnoDB 会按照 id 来组织整张表的数据。
如果通过主键查询:
sql
SELECT * FROM user WHERE id = 1;
查询效率通常会非常高,因为通过主键索引就可以直接找到完整数据。
但是如果通过普通索引查询,比如:
sql
SELECT * FROM user WHERE name = 'Tom';
普通索引叶子节点中存的是主键值,找到主键后,还需要再回到主键索引中查询整行数据,这个过程叫做回表。
所以在 InnoDB 中,主键设计非常重要。一般建议使用短小、稳定、递增的字段作为主键。
5. InnoDB 支持崩溃恢复
InnoDB 还有一个很重要的能力:崩溃恢复。
如果数据库正在写入数据时服务器突然宕机,InnoDB 可以通过日志机制尽量保证数据不出错。
这里主要涉及两个日志:
- redo log:保证事务提交后的数据可以恢复
- undo log:用于事务回滚和 MVCC
redo log 解决的是"提交后的数据不能丢"的问题。
undo log 解决的是"事务失败后能回到原来的状态"的问题。
正因为有这些机制,InnoDB 才适合承载真实业务系统中的核心数据。
6. InnoDB 支持外键
InnoDB 支持外键约束,例如:
sql
CREATE TABLE dept (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50)
) ENGINE = InnoDB;
CREATE TABLE emp (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
dept_id INT,
CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES dept(id)
) ENGINE = InnoDB;
外键可以保证数据之间的关联关系,比如员工必须属于一个已存在的部门。
不过在实际项目开发中,很多团队虽然使用 InnoDB,但不一定会大量使用数据库外键,而是把关联校验放在业务代码中处理。这样做的好处是业务逻辑更灵活,缺点是需要开发者自己保证数据一致性。
三、简单了解 MyISAM
MyISAM 是 MySQL 早期比较常见的存储引擎。
它的特点是:
- 不支持事务
- 不支持外键
- 使用表级锁
- 查询速度在某些读多写少场景下比较快
- 崩溃恢复能力不如 InnoDB
创建 MyISAM 表:
sql
CREATE TABLE article (
id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(100),
content TEXT
) ENGINE = MyISAM;
MyISAM 更适合一些历史场景,比如读多写少、对事务要求不高的数据表。
但是对于现在大多数业务系统来说,只要涉及增删改、并发、事务一致性,一般都不会优先选择 MyISAM。
四、简单了解 MEMORY
MEMORY 存储引擎的特点是:数据存储在内存中。
它的优点很明显:访问速度快。
创建 MEMORY 表:
sql
CREATE TABLE temp_data (
id INT PRIMARY KEY,
name VARCHAR(50)
) ENGINE = MEMORY;
但是它也有明显限制:
- 数据存储在内存中,服务重启后数据会丢失
- 不适合保存重要业务数据
- 表大小受内存限制
- 更适合临时数据、缓存类数据
所以 MEMORY 不是用来替代 InnoDB 的,它更像是某些特殊场景下的临时加速方案。
五、InnoDB、MyISAM、MEMORY 对比
| 对比项 | InnoDB | MyISAM | MEMORY |
|---|---|---|---|
| 是否支持事务 | 支持 | 不支持 | 不支持 |
| 锁粒度 | 行级锁 | 表级锁 | 表级锁 |
| 是否支持外键 | 支持 | 不支持 | 不支持 |
| 数据存储位置 | 磁盘 | 磁盘 | 内存 |
| 崩溃恢复能力 | 强 | 较弱 | 重启数据丢失 |
| 并发写入能力 | 强 | 较弱 | 一般 |
| 适合场景 | 核心业务表 | 读多写少的历史场景 | 临时数据、缓存数据 |
| 是否推荐日常开发使用 | 推荐 | 不推荐作为首选 | 只适合特殊场景 |
从这个表可以看出,InnoDB 的综合能力最强,尤其是在事务、并发和数据安全方面。
MyISAM 的优势主要在一些读多写少、对事务要求不高的旧场景中。
MEMORY 的特点是快,但数据不持久,所以不能保存重要数据。
六、实际开发中怎么选择
实际开发中,选择存储引擎可以记住下面几句话:
- 普通业务表,优先使用 InnoDB
- 涉及事务、订单、库存、用户、支付,必须优先考虑 InnoDB
- 对数据安全有要求,不要使用 MEMORY 保存核心数据
- MyISAM 了解即可,现在新项目一般不作为首选
- 不清楚该选什么时,选 InnoDB 基本不会错
比如下面这些表,就很适合使用 InnoDB:
sql
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
total_amount DECIMAL(10, 2) NOT NULL,
status TINYINT NOT NULL,
create_time DATETIME NOT NULL
) ENGINE = InnoDB;
订单表涉及金额、状态、用户关系和事务一致性,所以必须使用支持事务和崩溃恢复能力更强的存储引擎。
七、总结
这一篇主要学习了 MySQL 存储引擎的作用,并重点分析了 InnoDB。
存储引擎不是一个可有可无的概念,它决定了表底层的数据管理方式。InnoDB 之所以成为现在最常用的存储引擎,是因为它支持事务、行级锁、MVCC、聚簇索引、崩溃恢复和外键,整体更适合真实业务系统。
MyISAM 和 MEMORY 也有自己的特点,但使用场景比较有限。MyISAM 更偏向早期读多写少场景,MEMORY 更适合临时数据和缓存数据。
所以在日常开发中,我们可以先记住一个结论:
只要是正式业务表,优先选择 InnoDB。