This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
2026-05-21 12:20:51 +08:00

7.1 KiB
Raw Permalink Blame History

tags, create time
tags create time
MySQL
MyISAM
Memory
Archive
存储引擎
2026-05-16 00:00

MyISAM / Memory / Archive 引擎概览

概述

虽然生产环境几乎全部使用 InnoDB,但了解其他存储引擎的特点对于特定场景决策和理解 MySQL 历史演进仍然有价值。

MyISAM

MyISAM 是 MySQL 4.x 时代的默认引擎,在 5.5 版本之后被 InnoDB 取代。

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 的表锁机制

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 文件中。

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
-- 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 引擎专为"存而不查"的场景设计,采用行级锁 + 压缩存储。

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 海量事实表的增量导入
统计报表底表 定期导入后供离线分析
-- 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。

引擎切换指南

如何查看当前支持的引擎?

SHOW ENGINES;
-- 关注 Support 列:YES(默认)、DEFAULT、NO(不支持)

如何修改引擎?

-- 创建新表时指定
CREATE TABLE archive_data (...) ENGINE=Archive;

-- 已存在表的转换(在线执行可能需要较长时间)
ALTER TABLE old_table ENGINE=InnoDB;

-- ⚠️ ALTER TABLE 原理
-- 1. 创建临时表(新引擎)
-- 2. 逐行拷贝旧表数据到新表
-- 3. 加排他锁,替换文件名
-- 4. 删除旧表
-- 大表转换会占用双倍空间和锁定时间

为什么生产环境几乎只用 InnoDB

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 设计者的首选推荐,只有在性能极端优化或有特殊需求时,才值得切换引擎。

关联笔记