Init
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
---
|
||||
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 在不同数据库间的移植注意事项
|
||||
Reference in New Issue
Block a user