Files
cs-note/hhs/MySQL/04-存储引擎/20-其他存储引擎概览.md
2026-05-24 11:42:38 +08:00

222 lines
7.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [MySQL, MyISAM, Memory, Archive, 存储引擎]
create time: 2026-05-16 00:00
---
# MyISAM / Memory / Archive 引擎概览
## 概述
虽然生产环境几乎全部使用 InnoDB,但了解其他存储引擎的特点对于特定场景决策和理解 MySQL 历史演进仍然有价值。
## MyISAM
MyISAM 是 MySQL 4.x 时代的默认引擎,在 5.5 版本之后被 InnoDB 取代。
```mermaid
flowchart TB
subgraph FS["MyISAM 文件结构"]
MYD[".MYD — 数据文件"]
MYI[".MYI — 索引文件"]
FRM[".FRM — 表结构文件"]
end
subgraph FEAT["核心特性"]
T1["❌ 不支持事务"]
T2["❌ 不支持外键"]
T3["❌ 只有表锁<br/>无行锁"]
T4["✅ 压缩存储<br/>(myisampack)"]
T5["✅ 全文索引<br/>(8.0 前唯一选择)"]
T6["✅ 读密集型场景较快"]
end
style FS fill:#F8EFBA,color:#000
style FEAT fill:#D0E8FD,color:#000
```
> [!QUOTE] 思考一下
> MyISAM 将数据和索引分存为两个独立文件。这种设计看似简洁,但正是**缺乏事务支持(InnoDB 的 Redo Log + Undo Log)**和**行级锁**的根本原因——没有机制保证原子性和并发安全。
### MyISAM 的表锁机制
```mermaid
sequenceDiagram
participant C1 as Client A (WRITE)
participant S as Server
participant C2 as Client B (READ)
C1->>S: LOCK TABLES users WRITE
S->>S: 获取表级写锁
C1->>S: INSERT INTO users ...
C1->>S: UPDATE users SET ...
C1->>S: UNLOCK TABLES
C2->>S: SELECT * FROM users
Note over S: ⚠️ 阻塞!等待写锁释放
S-->>C2: (等待中...)
C1->>S: UNLOCK TABLES
S-->>C2: 返回结果集
```
> [!WARNING] 表锁的危害
> MyISAM 的表锁意味着**一个写操作会阻塞所有其他操作**(包括读)。在并发场景中这是灾难性的——即使是纯 SELECT 也会被阻塞。这就是为什么现代项目不应该再用 MyISAM。
### MyISAM 还能用在什么地方?
极少数场景:
- **静态只读日志**:导入一次、长期查询、绝不更新
- **GIS 数据**:MyISAM 的 spatial index 在某些老版本上更快
- **遗留迁移**:老旧系统的临时兼容层
> [!CAUTION] MyISAM 全文索引已过时
> MySQL 5.6+ 起 InnoDB 已支持 FULLTEXT 索引,8.0 后更是全面强化。MyISAM 作为"唯一全文索引选择"的优势已基本消失。
**结论:新项目不用 MyISAM。**
> [!TIP] 面试高频考点
> 面试官问"MyISAM vs InnoDB"时,核心差异就是三点:**锁粒度、事务、外键**。只要答出这三点,基本就拿满分了。
## Memory(HEAP)引擎
Memory 引擎将所有数据存储在内存中,表结构存在于磁盘 `.frm` 文件中。
```mermaid
flowchart TD
A["CREATE TABLE ... ENGINE=MEMORY"] --> B["数据全在内存"]
B --> C["极速读写 O(1)"]
B --> D["重启后数据丢失 ⚠️"]
C --> E["适合场景"]
E --> E1["临时聚合计算结果"]
E --> E2["字典表缓存"]
E --> E3["会话级临时查找表"]
style B fill:#EE5A24,color:#fff
style E3 fill:#00D866,color:#fff
```
```sql
-- Memory 引擎使用示例
CREATE TABLE session_lookup (
session_id CHAR(32) PRIMARY KEY,
user_id BIGINT,
last_access TIMESTAMP,
payload JSON
) ENGINE=Memory;
-- ⚠️ 注意限制
-- 1. VARCHAR/TEXT/BLOB 会使用 MEMORY 的内部映射转为固定长度
-- 2. 不支持 AUTO_INCREMENT
-- 3. 受 max_heap_table_size 和 tmp_table_size 限制
-- 4. 表在 MySQL 重启或 flush tables 时消失
-- 5. HASH 索引是默认值,BRIN 索引可通过 explicit index type 指定(MySQL 8.0+)
-- 💡 调优:调整上限
SET SESSION max_heap_table_size = 512 * 1024 * 1024; -- 512MB
SET GLOBAL tmp_table_size = 512 * 1024 * 1024;
```
> [!NOTE] Memory vs Redis
> 很多人问"能不能用 Memory 引擎代替 Redis 做缓存"。答案是:通常不建议。
> - Redis 有更丰富的数据结构(Sorted Set、Bitmap、Stream)
> - Redis 支持持久化和集群
> - Redis 有成熟的驱动和生态
> - MySQL Memory 引擎在连接断开时也会丢数据
## Archive 引擎
Archive 引擎专为"存而不查"的场景设计,采用行级锁 + 压缩存储。
```mermaid
flowchart LR
A["INSERT 数据"] --> B["Row-level compression<br/>每行独立压缩"]
B --> C["仅支持 SELECT / INSERT"]
C --> D["❌ 不支持 DELETE"]
C --> E["❌ 不支持 UPDATE"]
C --> F["❌ 不使用索引"]
style C fill:#FF9F43,color:#000
```
### 适用场景
| 场景 | 说明 |
|------|------|
| 日志归档 | 系统日志、审计日志只写不读 |
| 数据仓库 ETL | 海量事实表的增量导入 |
| 统计报表底表 | 定期导入后供离线分析 |
```sql
-- Archive 引擎示例
CREATE TABLE audit_log (
id BIGINT AUTO_INCREMENT, -- Archive 允许自增但不作索引查找用
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
action VARCHAR(50),
details TEXT,
PRIMARY KEY (id) -- 仅用于自增,不参与查找
) ENGINE=Archive;
-- ⚠️ MySQL 8.0+ 不再需要 ROW_FORMAT=COMPRESSED
-- Archive 引擎默认就是压缩存储,显式指定会报语法错误
-- 典型用法:按月分区归档
ALTER TABLE audit_log PARTITION BY RANGE (YEAR(created_at)) (
PARTITION p2025 VALUES LESS THAN (2026),
PARTITION p2026 VALUES LESS THAN (2027)
);
```
> [!NOTE] 为什么 Archive 不支持 UPDATE / DELETE?
> Archive 的行是**连续压缩存储**的——每一行都依赖于前一行解压后的位置。如果随机修改或删除中间某行,整个压缩链就要重新计算,这在设计上是不可接受的。所以它干脆禁止了这些操作,只保留 INSERT + SELECT。
## 引擎切换指南
### 如何查看当前支持的引擎?
```sql
SHOW ENGINES;
-- 关注 Support 列:YES(默认)、DEFAULT、NO(不支持)
```
### 如何修改引擎?
```sql
-- 创建新表时指定
CREATE TABLE archive_data (...) ENGINE=Archive;
-- 已存在表的转换(在线执行可能需要较长时间)
ALTER TABLE old_table ENGINE=InnoDB;
-- ⚠️ ALTER TABLE 原理
-- 1. 创建临时表(新引擎)
-- 2. 逐行拷贝旧表数据到新表
-- 3. 加排他锁,替换文件名
-- 4. 删除旧表
-- 大表转换会占用双倍空间和锁定时间
```
### 为什么生产环境几乎只用 InnoDB
```mermaid
flowchart TD
Check{"需要事务?"}
Check -->|是| INNODB["✅ InnoDB"]
Check -->|否| Q2{"高读低写?"}
Q2 -->|是| MEM{"✅ Memory(缓存)<br/>或 MyISAM(静态)"}
Q2 -->|否| ARCH{"纯追加场景?"}
ARCH -->|是| ARC["✅ Archive"]
ARCH -->|否| INNODB
style INNODB fill:#00D866,color:#fff
style MEM fill:#FF9F43,color:#000
style ARC fill:#C44569,color:#fff
```
> [!TIP] 决策原则
> 记住一句话:**"默认 InnoDB,特殊情况再考虑其他"**。InnoDB 是 MySQL 设计者的首选推荐,只有在性能极端优化或有特殊需求时,才值得切换引擎。
## 关联笔记
- [[hhs/MySQL/04-存储引擎/19-InnoDB 深度解析]] — InnoDB 五大核心组件完整文档
- [[hhs/GORM/13-多数据库支持]] — GORM 在不同数据库间的移植注意事项