222 lines
7.0 KiB
Markdown
222 lines
7.0 KiB
Markdown
---
|
||
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/06-InnoDB 深度解析]] — InnoDB 五大核心组件完整文档
|
||
- [[hhs/GORM/13-多数据库支持]] — GORM 在不同数据库间的移植注意事项
|