Skip to content

第五阶段:MySQL 架构与存储引擎


5.1 MySQL 整体架构

5.1.1 四层架构总览

MySQL Server 整体架构(分层视图):

  客户端
  ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐
  │ mysql CLI │ │ JDBC/ODBC │ │  DBeaver  │ │ App Code  │
  └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘
        │             │             │             │
        └──────┬──────┴──────┬──────┴──────┬──────┘
               │             │             │
  ═════════════╪═════════════╪═════════════╪══════════════════
               ↓             ↓             ↓
  ┌──────────────────────────────────────────────────────────┐
  │                  第一层:连接层                            │
  │                                                          │
  │  ┌─────────────┐  ┌─────────────┐  ┌──────────────────┐ │
  │  │ 连接管理     │  │ 认证鉴权    │  │ 线程池(可选)    │ │
  │  │ TCP/Socket  │  │ 用户名密码   │  │ Thread Pool      │ │
  │  │ 连接         │  │ 权限校验    │  │ 连接池            │ │
  │  └─────────────┘  └─────────────┘  └──────────────────┘ │
  └──────────────────────────┬───────────────────────────────┘

  ┌──────────────────────────┼───────────────────────────────┐
  │                  第二层:服务层(SQL Layer)                │
  │                                                          │
  │  SQL 执行流程:                                           │
  │  ┌──────┐  ┌──────┐  ┌──────────┐  ┌──────┐  ┌──────┐  │
  │  │解析器│→│预处理 │→│ 查询优化器│→│执行器│→│结果集│  │
  │  │Parser│  │PreProc│ │Optimizer │  │Exec  │  │Result│  │
  │  └──────┘  └──────┘  └──────────┘  └──────┘  └──────┘  │
  │                                                          │
  │  其他组件:                                               │
  │  ┌──────────────┐ ┌────────────┐ ┌─────────────────────┐│
  │  │ 查询缓存      │ │ 内置函数   │ │ 存储过程/视图/触发器││
  │  │(8.0 已移除)   │ │ 日期/数学  │ │ Procedures/Views   ││
  │  └──────────────┘ └────────────┘ └─────────────────────┘│
  └──────────────────────────┬───────────────────────────────┘
                             │  Handler API
  ┌──────────────────────────┼───────────────────────────────┐
  │                  第三层:引擎层(Storage Engine Layer)     │
  │                                                          │
  │  ┌────────┐  ┌────────┐  ┌────────┐  ┌────────┐        │
  │  │ InnoDB │  │ MyISAM │  │ Memory │  │Archive │  ...   │
  │  │  ⭐    │  │        │  │        │  │        │        │
  │  └───┬────┘  └───┬────┘  └───┬────┘  └───┬────┘        │
  │      │           │           │           │              │
  └──────┼───────────┼───────────┼───────────┼──────────────┘
         │           │           │           │
  ┌──────┼───────────┼───────────┼───────────┼──────────────┐
  │      ↓           ↓           ↓           ↓              │
  │                  第四层:存储层                            │
  │                                                          │
  │  ┌──────────────────────────────────────────────────┐   │
  │  │        文件系统(磁盘 / SSD)                      │   │
  │  │                                                  │   │
  │  │  .ibd 数据文件  .frm 表结构  redo log  undo log  │   │
  │  │  binlog         relay log    slow log  error log │   │
  │  └──────────────────────────────────────────────────┘   │
  └──────────────────────────────────────────────────────────┘

5.1.2 SQL 执行全流程

一条 SQL 从发送到返回结果,经过以下完整路径:

SELECT * FROM users WHERE id = 1;  这条 SQL 的完整执行流程:

  ┌──────────┐
  │ 1. 连接器 │  验证用户名密码、获取权限
  └────┬─────┘

  ┌──────────┐
  │ 2. 查询   │  MySQL 8.0 已移除此步骤
  │   缓存    │  (8.0 之前:命中缓存直接返回)
  └────┬─────┘

  ┌──────────┐  SQL 语句 → 语法树(AST)
  │ 3. 解析器 │  
  │  Parser  │  词法分析:识别关键词 SELECT, FROM, WHERE
  │          │  语法分析:检查语法是否正确
  └────┬─────┘  → 如果语法错误,报错 ERROR 1064

  ┌──────────┐
  │ 4. 预处理 │  检查表名、列名是否存在
  │PreProcess│  检查权限
  └────┬─────┘  → 如果表/列不存在,报错

  ┌──────────┐  确定最优执行方案
  │ 5. 优化器 │  
  │Optimizer │  选择使用哪个索引
  │          │  确定 JOIN 的连接顺序
  │          │  生成执行计划
  └────┬─────┘

  ┌──────────┐  按照执行计划调用存储引擎接口
  │ 6. 执行器 │  
  │ Executor │  调用引擎的 read_row() 接口
  │          │  逐行判断是否满足条件
  │          │  将结果集返回客户端
  └────┬─────┘

  ┌──────────┐
  │ 7. 存储   │  InnoDB 引擎
  │   引擎    │  从 Buffer Pool 或磁盘读取数据
  └──────────┘
sql
-- 验证 SQL 执行流程

-- 查看连接信息
SHOW PROCESSLIST;
-- +----+------+-----------+------+---------+------+-------+------------------+
-- | Id | User | Host      | db   | Command | Time | State | Info             |
-- +----+------+-----------+------+---------+------+-------+------------------+
-- |  5 | root | localhost | test | Query   |    0 | init  | SHOW PROCESSLIST |
-- +----+------+-----------+------+---------+------+-------+------------------+

-- 查看优化器选择的执行计划
EXPLAIN SELECT * FROM users WHERE id = 1;

-- 查看优化器的优化过程
EXPLAIN FORMAT=TREE SELECT * FROM users WHERE id = 1;

-- MySQL 8.0+ 查看优化器的真实执行信息
EXPLAIN ANALYZE SELECT * FROM users WHERE id = 1;

5.2 InnoDB 存储引擎架构

5.2.1 InnoDB 架构全景

InnoDB 存储引擎内部架构:

  ┌─────────────────────────────────────────────────────────────┐
  │                     内存区域(Memory)                       │
  │                                                             │
  │  ┌───────────────────────────────────────────────────┐     │
  │  │              Buffer Pool(缓冲池)                  │     │
  │  │                                                   │     │
  │  │  ┌──────────┐ ┌──────────┐ ┌──────────────────┐  │     │
  │  │  │ 数据页    │ │ 索引页    │ │ 自适应哈希索引    │  │     │
  │  │  │ Data Page│ │Index Page│ │ Adaptive Hash    │  │     │
  │  │  └──────────┘ └──────────┘ └──────────────────┘  │     │
  │  │  ┌──────────┐ ┌──────────┐ ┌──────────────────┐  │     │
  │  │  │ Undo 页   │ │ 锁信息    │ │ 插入缓冲         │  │     │
  │  │  │ Undo Page│ │Lock Info │ │ Change Buffer    │  │     │
  │  │  └──────────┘ └──────────┘ └──────────────────┘  │     │
  │  │                                                   │     │
  │  │  管理:LRU 链表(young/old 区域) + Free 链表       │     │
  │  └───────────────────────────────────────────────────┘     │
  │                                                             │
  │  ┌──────────────────┐  ┌──────────────────────────────┐   │
  │  │  Log Buffer       │  │  额外内存池                    │   │
  │  │  日志缓冲          │  │  数据字典、锁、等待队列等       │   │
  │  │  (redo log 缓冲)  │  │                              │   │
  │  └────────┬─────────┘  └──────────────────────────────┘   │
  │           │                                                │
  └───────────┼────────────────────────────────────────────────┘
              │ 刷盘
  ┌───────────┼────────────────────────────────────────────────┐
  │           ↓           磁盘区域(Disk)                      │
  │                                                             │
  │  ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐   │
  │  │ 系统表空间     │ │ 独立表空间    │ │ 通用表空间         │   │
  │  │ ibdata1      │ │ *.ibd       │ │ General TS       │   │
  │  │              │ │ (每表一个)    │ │                  │   │
  │  │ • 数据字典    │ │ • 表数据      │ │                  │   │
  │  │ • Undo Log   │ │ • 索引数据    │ │                  │   │
  │  │ • Change Buf │ │              │ │                  │   │
  │  └──────────────┘ └──────────────┘ └──────────────────┘   │
  │                                                             │
  │  ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐   │
  │  │ Redo Log      │ │ Undo Log     │ │ Temp Tablespace  │   │
  │  │ ib_logfile0  │ │ undo_001     │ │ ibtmp1           │   │
  │  │ ib_logfile1  │ │ undo_002     │ │ (临时表空间)      │   │
  │  └──────────────┘ └──────────────┘ └──────────────────┘   │
  └─────────────────────────────────────────────────────────────┘

5.2.2 Buffer Pool 详解

Buffer Pool 是 InnoDB 最重要的内存结构,用于缓存数据页和索引页。

Buffer Pool 工作原理:

  ┌──────────────────────────────────────────────────────┐
  │                  Buffer Pool                          │
  │                                                      │
  │  LRU 链表(改进版):                                  │
  │                                                      │
  │  ←──────── 热数据(young 区,5/8)──────────→         │
  │  ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐                │
  │  │ P1 │→│ P5 │→│ P3 │→│ P8 │→│ P2 │  频繁访问      │
  │  └────┘ └────┘ └────┘ └────┘ └────┘                │
  │                              midpoint               │
  │  ←──────── 冷数据(old 区,3/8)──────────→          │
  │  ┌────┐ ┌────┐ ┌────┐ ┌────┐                       │
  │  │ P9 │→│ P4 │→│ P7 │→│ P6 │  不常访问             │
  │  └────┘ └────┘ └────┘ └────┘                       │
  │                              ↓                       │
  │                           被淘汰                      │
  │                                                      │
  │  新页面先进入 old 区头部                                │
  │  → 在 old 区存活超过 1 秒且再次被访问                   │
  │  → 移入 young 区头部                                  │
  │  → 防止全表扫描污染 Buffer Pool                        │
  └──────────────────────────────────────────────────────┘

  脏页刷盘:
  ┌──────────┐       ┌──────────┐
  │ Buffer   │ flush │  磁盘     │
  │ Pool 中  │──────→│  .ibd    │
  │ 脏页     │       │  文件     │
  └──────────┘       └──────────┘
  
  刷盘时机:
  1. Redo Log 空间不足
  2. Buffer Pool 空间不足
  3. 后台线程定期刷盘
  4. MySQL 正常关闭
sql
-- ==================== Buffer Pool 相关配置 ====================

-- 查看 Buffer Pool 大小(默认 128MB,生产建议设为物理内存的 60-80%)
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
-- +-------------------------+-----------+
-- | Variable_name           | Value     |
-- +-------------------------+-----------+
-- | innodb_buffer_pool_size | 134217728 |  ← 128MB
-- +-------------------------+-----------+

-- 动态调整(MySQL 5.7.5+)
SET GLOBAL innodb_buffer_pool_size = 1073741824;  -- 1GB

-- 查看 Buffer Pool 使用状态
SHOW ENGINE INNODB STATUS\G
-- 关注 BUFFER POOL AND MEMORY 部分

-- 查看 Buffer Pool 命中率
SHOW STATUS LIKE 'Innodb_buffer_pool%';
-- Innodb_buffer_pool_read_requests  ← 逻辑读(从 BP 读)
-- Innodb_buffer_pool_reads          ← 物理读(从磁盘读)
-- 命中率 = 1 - (reads / read_requests) × 100%
-- 生产环境命中率应 > 99%

-- 查看 Buffer Pool 实例数(多实例减少锁竞争)
SHOW VARIABLES LIKE 'innodb_buffer_pool_instances';
-- BP >= 1GB 时建议设为 8

5.2.3 Change Buffer

Change Buffer(变更缓冲):

  适用场景:对非唯一二级索引的 INSERT/UPDATE/DELETE
  
  ┌─────────────────────────────────────────────────────┐
  │  没有 Change Buffer:                                │
  │                                                     │
  │  INSERT → 读取二级索引页到 BP → 修改 → 脏页刷盘     │
  │            ↑ 随机磁盘 IO(慢!)                     │
  │                                                     │
  │  有 Change Buffer:                                  │
  │                                                     │
  │  INSERT → 记录到 Change Buffer(内存)               │
  │            ↑ 无需磁盘 IO(快!)                     │
  │                                                     │
  │  后续读取该页时:                                     │
  │  读取磁盘页 → merge Change Buffer 中的变更 → 完成    │
  └─────────────────────────────────────────────────────┘

  ⚠️ 只适用于非唯一二级索引!
  因为唯一索引必须读取磁盘判断唯一性,无法延迟。

5.3 InnoDB 日志系统

5.3.1 日志系统全景

InnoDB 三大日志及其作用:

  ┌─────────────────────────────────────────────────────┐
  │                                                     │
  │  ┌──────────┐  ┌──────────┐  ┌──────────────────┐ │
  │  │ Redo Log │  │ Undo Log │  │     Binlog       │ │
  │  │ 重做日志  │  │ 回滚日志  │  │   二进制日志      │ │
  │  ├──────────┤  ├──────────┤  ├──────────────────┤ │
  │  │ 保证     │  │ 保证     │  │ 用于             │ │
  │  │ 持久性(D)│  │ 原子性(A)│  │ 主从复制         │ │
  │  │         │  │ + MVCC   │  │ 数据恢复         │ │
  │  ├──────────┤  ├──────────┤  ├──────────────────┤ │
  │  │ InnoDB  │  │ InnoDB   │  │ MySQL Server     │ │
  │  │ 引擎层   │  │ 引擎层   │  │ 服务层           │ │
  │  ├──────────┤  ├──────────┤  ├──────────────────┤ │
  │  │ 物理日志 │  │ 逻辑日志  │  │ 逻辑日志         │ │
  │  │ 记录页面 │  │ 记录行   │  │ 记录 SQL/行变更  │ │
  │  │ 的修改   │  │ 的旧值   │  │                  │ │
  │  ├──────────┤  ├──────────┤  ├──────────────────┤ │
  │  │ 循环写入 │  │ 随事务   │  │ 追加写入         │ │
  │  │ 固定大小 │  │ 创建/清理│  │ 无限增长         │ │
  │  └──────────┘  └──────────┘  └──────────────────┘ │
  └─────────────────────────────────────────────────────┘

5.3.2 Redo Log(重做日志)

Redo Log 的作用 —— WAL(Write-Ahead Logging)机制:

  核心思想:先写日志,再写磁盘

  ┌──────────────────────────────────────────────────────┐
  │  UPDATE accounts SET balance = 800 WHERE id = 1;     │
  │                                                      │
  │  步骤:                                               │
  │  1. 从 Buffer Pool 中找到 id=1 的数据页               │
  │     (不在则从磁盘加载到 BP)                          │
  │  2. 在 Buffer Pool 中修改数据页(内存中修改)          │
  │  3. 生成 Redo Log 写入 Log Buffer                    │
  │  4. COMMIT 时 Redo Log 刷盘  ← 顺序写,速度很快      │
  │  5. 脏页异步刷盘              ← 随机写,后台慢慢做     │
  │                                                      │
  │  如果 Step 4 后宕机:                                 │
  │  → 重启后用 Redo Log 恢复未刷盘的脏页 ← 保证持久性!   │
  └──────────────────────────────────────────────────────┘

  Redo Log 循环写入:
  ┌──────────────────────────────────────────────┐
  │                                              │
  │  ib_logfile0        ib_logfile1              │
  │  ┌───────────────┐  ┌───────────────┐       │
  │  │ ██████████░░░ │  │ ░░░░░░░░░░░░░│       │
  │  └───────────────┘  └───────────────┘       │
  │         ↑ write pos       ↑ checkpoint      │
  │                                              │
  │  write pos: 当前写入位置(顺时针推进)        │
  │  checkpoint: 已刷盘到数据文件的位置            │
  │                                              │
  │  write pos 追上 checkpoint → 暂停写入,      │
  │  等待 checkpoint 推进(刷脏页)               │
  └──────────────────────────────────────────────┘
sql
-- ==================== Redo Log 配置 ====================

-- 查看 Redo Log 文件大小和数量
SHOW VARIABLES LIKE 'innodb_log_file_size';
-- +----------------------+-----------+
-- | Variable_name        | Value     |
-- +----------------------+-----------+
-- | innodb_log_file_size | 50331648  |  ← 48MB(默认)
-- +----------------------+-----------+

SHOW VARIABLES LIKE 'innodb_log_files_in_group';
-- +--------------------------+-------+
-- | Variable_name            | Value |
-- +--------------------------+-------+
-- | innodb_log_files_in_group| 2     |  ← 默认 2 个文件
-- +--------------------------+-------+

-- 总 Redo Log 空间 = innodb_log_file_size × innodb_log_files_in_group
-- 建议生产设置:256MB~1GB per file

-- Redo Log 刷盘策略
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
-- +--------------------------------+-------+
-- | Variable_name                  | Value |
-- +--------------------------------+-------+
-- | innodb_flush_log_at_trx_commit | 1     |  ← 默认
-- +--------------------------------+-------+
--
-- = 0:每秒写入 OS cache 并 fsync(可能丢 1 秒数据)
-- = 1:每次 COMMIT 都 fsync(最安全,默认,推荐)
-- = 2:每次 COMMIT 写入 OS cache,每秒 fsync(折中)

5.3.3 Undo Log(回滚日志)

Undo Log 的两大作用:

  1. 事务回滚(保证原子性)
  ┌──────────────────────────────────────────────┐
  │  BEGIN;                                      │
  │  UPDATE SET balance = 800;  → Undo 记录旧值  │
  │  → Undo Log: (id=1, balance=1000)            │
  │                                              │
  │  ROLLBACK;                                   │
  │  → 读取 Undo Log,恢复 balance = 1000        │
  └──────────────────────────────────────────────┘

  2. MVCC 版本链(多版本并发控制)
  ┌──────────────────────────────────────────────┐
  │  事务 A(快照读):                            │
  │  → 当前版本不可见?                            │
  │  → 沿 Undo Log 版本链找到可见的旧版本          │
  │  → 返回历史版本数据                            │
  │  (详见第四阶段 MVCC 部分)                     │
  └──────────────────────────────────────────────┘

  Undo Log 类型:
  ┌─────────────────┬──────────────────────────┐
  │ insert undo log │ INSERT 操作产生的 Undo    │
  │                 │ 事务提交后可立即删除       │
  │                 │(INSERT 的行对其他事务不可见)│
  ├─────────────────┼──────────────────────────┤
  │ update undo log │ UPDATE/DELETE 产生的 Undo │
  │                 │ 需等待所有快照读不再需要    │
  │                 │ 后才能被 purge 线程清理    │
  └─────────────────┴──────────────────────────┘

5.3.4 Binlog(二进制日志)

Binlog 的作用:

  ┌──────────────────────────────────────────────────┐
  │  1. 主从复制:                                    │
  │  ┌──────┐  binlog  ┌──────┐  relay log  ┌──────┐│
  │  │Master│ ───────→│Slave │ ─────────→ │Slave ││
  │  │主库  │         │从库  │  回放执行   │数据  ││
  │  └──────┘         └──────┘            └──────┘│
  │                                                │
  │  2. 数据恢复:                                   │
  │  全量备份 + Binlog 增量 → 恢复到任意时间点        │
  └──────────────────────────────────────────────────┘

  Binlog 三种格式:
  ┌─────────────┬──────────────────────────────────────┐
  │  Statement  │ 记录原始 SQL 语句                     │
  │  (默认 5.7) │ 优点:日志量小                        │
  │             │ 缺点:NOW() 等不确定函数可能主从不一致  │
  ├─────────────┼──────────────────────────────────────┤
  │  Row         │ 记录每行数据的变化(修改前后的值)      │
  │  (推荐)      │ 优点:精确,不会主从不一致             │
  │             │ 缺点:日志量大                        │
  ├─────────────┼──────────────────────────────────────┤
  │  Mixed      │ 混合模式:普通 SQL 用 Statement,      │
  │             │ 不确定函数用 Row                       │
  └─────────────┴──────────────────────────────────────┘
sql
-- ==================== Binlog 配置 ====================

-- 查看是否开启 Binlog
SHOW VARIABLES LIKE 'log_bin';
-- +---------------+-------+
-- | Variable_name | Value |
-- +---------------+-------+
-- | log_bin       | ON    |
-- +---------------+-------+

-- 查看 Binlog 格式
SHOW VARIABLES LIKE 'binlog_format';
-- +---------------+-------+
-- | Variable_name | Value |
-- +---------------+-------+
-- | binlog_format | ROW   |  ← 推荐 ROW
-- +---------------+-------+

-- 查看所有 Binlog 文件
SHOW BINARY LOGS;
-- +------------------+-----------+
-- | Log_name         | File_size |
-- +------------------+-----------+
-- | mysql-bin.000001 |   1073742 |
-- | mysql-bin.000002 |    524288 |
-- +------------------+-----------+

-- 查看 Binlog 事件
SHOW BINLOG EVENTS IN 'mysql-bin.000001' LIMIT 10;

-- 使用 mysqlbinlog 工具解析
-- mysqlbinlog --base64-output=decode-rows -v mysql-bin.000001

-- Binlog 过期自动清理
SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';
-- 默认 2592000(30 天),8.0+ 使用此参数

5.3.5 两阶段提交(2PC)

Redo Log 和 Binlog 的两阶段提交:

  为什么需要?
  → 确保 Redo Log 和 Binlog 的一致性
  → 防止主从数据不一致

  UPDATE accounts SET balance = 800 WHERE id = 1;

  ┌──────────────────────────────────────────────────────┐
  │                                                      │
  │  1. 执行器调用 InnoDB 修改数据                         │
  │     ↓                                                │
  │  2. InnoDB 修改 Buffer Pool 中的数据页                 │
  │     ↓                                                │
  │  3. InnoDB 写 Redo Log(prepare 状态)   ← 第一阶段    │
  │     ↓                                                │
  │  4. 执行器写 Binlog 到磁盘                             │
  │     ↓                                                │
  │  5. InnoDB 将 Redo Log 改为 commit 状态  ← 第二阶段    │
  │                                                      │
  │  ┌─────────┐    ┌─────────┐    ┌─────────┐          │
  │  │ Redo Log│    │ Binlog  │    │ Redo Log│          │
  │  │ prepare │ →  │  写入   │ →  │ commit  │          │
  │  └─────────┘    └─────────┘    └─────────┘          │
  │                                                      │
  │  崩溃恢复规则:                                        │
  │  • Redo = prepare + Binlog 完整 → 提交                │
  │  • Redo = prepare + Binlog 不完整 → 回滚              │
  │  • Redo = commit → 提交                               │
  └──────────────────────────────────────────────────────┘

5.4 存储引擎对比

5.4.1 InnoDB vs MyISAM

特性InnoDBMyISAM
事务✅ 支持❌ 不支持
行级锁✅ 支持❌ 只有表级锁
外键✅ 支持❌ 不支持
MVCC✅ 支持❌ 不支持
崩溃恢复✅ Redo Log 自动恢复❌ 需手动修复
全文索引✅ 5.6+ 支持✅ 支持
COUNT(*)需遍历(MVCC 无法缓存)有计数器,O(1)
主键必须有(没有则自动生成)可以没有
索引实现聚簇索引(数据和索引一起)非聚簇(数据和索引分离)
存储文件.ibd(数据+索引).MYD(数据)+ .MYI(索引)
适用场景几乎所有场景(默认推荐)只读/极少写的报表统计
文件存储对比:

  InnoDB(聚簇索引):
  ┌─────────────────────────┐
  │  table_name.ibd          │
  │  ┌─────────────────────┐│
  │  │ 主键索引 B+ 树       ││
  │  │ 叶子节点存完整数据   ││
  │  └─────────────────────┘│
  │  ┌─────────────────────┐│
  │  │ 二级索引 B+ 树       ││
  │  │ 叶子节点存主键值     ││
  │  └─────────────────────┘│
  └─────────────────────────┘

  MyISAM(非聚簇):
  ┌───────────────┐ ┌───────────────┐
  │ table.MYD     │ │ table.MYI     │
  │ ┌───────────┐ │ │ ┌───────────┐ │
  │ │ 数据行 1   │ │ │ │索引 → 指针 │ │
  │ │ 数据行 2   │ │ │ │索引 → 指针 │ │
  │ │ 数据行 3   │ │ │ │索引 → 指针 │ │
  │ └───────────┘ │ │ └───────────┘ │
  └───────────────┘ └───────────────┘
  数据和索引分开存储    索引指向数据文件中的偏移量

5.4.2 其他存储引擎

存储引擎特点适用场景
Memory数据存内存,重启丢失,表级锁临时表、缓存、会话数据
CSV数据以 CSV 格式存储,可直接编辑数据交换、日志存储
Archive只支持 INSERT 和 SELECT,高压缩归档数据、日志存储
Blackhole写入即丢弃,不存储数据Binlog 中继、测试
NDB Cluster分布式存储引擎MySQL Cluster 集群
Federated访问远程 MySQL 表跨数据库查询
sql
-- ==================== 存储引擎操作 ====================

-- 查看支持的存储引擎
SHOW ENGINES;

-- 查看默认存储引擎
SHOW VARIABLES LIKE 'default_storage_engine';
-- +------------------------+--------+
-- | Variable_name          | Value  |
-- +------------------------+--------+
-- | default_storage_engine | InnoDB |
-- +------------------------+--------+

-- 查看某个表的存储引擎
SHOW TABLE STATUS LIKE 'accounts'\G
-- 或
SELECT TABLE_NAME, ENGINE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'test';

-- 创建表时指定引擎
CREATE TABLE logs (
    id INT AUTO_INCREMENT PRIMARY KEY,
    message TEXT,
    created_at DATETIME
) ENGINE=Archive;

-- 修改表的存储引擎(会重建表,耗时!)
ALTER TABLE old_table ENGINE=InnoDB;

5.5 数据库设计

5.5.1 范式理论

数据库范式(Normal Form)— 从低到高:

  ┌─────────────────────────────────────────────────────┐
  │  1NF(第一范式):列不可再分                          │
  │                                                     │
  │  ❌ 违反 1NF:                                       │
  │  ┌─────┬──────────────────┐                         │
  │  │ id  │ 联系方式          │                         │
  │  ├─────┼──────────────────┤                         │
  │  │ 1   │ 手机:138, QQ:123 │  ← 一列存了多个值        │
  │  └─────┴──────────────────┘                         │
  │                                                     │
  │  ✅ 满足 1NF:                                       │
  │  ┌─────┬─────────┬────────┐                         │
  │  │ id  │ phone   │ qq     │                         │
  │  ├─────┼─────────┼────────┤                         │
  │  │ 1   │ 138xxx  │ 123    │  ← 每列只存一个值        │
  │  └─────┴─────────┴────────┘                         │
  └─────────────────────────────────────────────────────┘

  ┌─────────────────────────────────────────────────────┐
  │  2NF(第二范式):满足 1NF + 非主键列完全依赖主键     │
  │                                                     │
  │  ❌ 违反 2NF(联合主键下的部分依赖):                 │
  │  ┌──────────┬───────────┬──────────┬───────┐       │
  │  │ student  │ course    │ score    │ dept  │       │
  │  │ (主键1)  │ (主键2)   │          │       │       │
  │  ├──────────┼───────────┼──────────┼───────┤       │
  │  │ 张三     │ 数学      │ 90       │ 计算机│       │
  │  └──────────┴───────────┴──────────┴───────┘       │
  │  dept 只依赖 student,不依赖 course → 部分依赖       │
  │                                                     │
  │  ✅ 拆分为两张表:                                    │
  │  students(student, dept)                             │
  │  scores(student, course, score)                      │
  └─────────────────────────────────────────────────────┘

  ┌─────────────────────────────────────────────────────┐
  │  3NF(第三范式):满足 2NF + 非主键列不传递依赖主键   │
  │                                                     │
  │  ❌ 违反 3NF(传递依赖):                            │
  │  ┌─────┬─────────┬───────────┐                      │
  │  │ id  │ dept_id │ dept_name │                      │
  │  ├─────┼─────────┼───────────┤                      │
  │  │ 1   │ D01     │ 计算机    │                      │
  │  └─────┴─────────┴───────────┘                      │
  │  id → dept_id → dept_name(传递依赖)               │
  │                                                     │
  │  ✅ 拆分:                                           │
  │  employees(id, dept_id)                              │
  │  departments(dept_id, dept_name)                     │
  └─────────────────────────────────────────────────────┘

5.5.2 反范式设计

反范式:适当增加冗余,用空间换时间

  ┌─────────────────────────────────────────────────────┐
  │  严格 3NF(需要 JOIN):                              │
  │                                                     │
  │  orders 表        users 表                           │
  │  ┌────┬─────┐    ┌────┬──────┐                     │
  │  │ id │ uid │    │ id │ name │                     │
  │  └────┴─────┘    └────┴──────┘                     │
  │                                                     │
  │  查询订单+用户名:                                    │
  │  SELECT o.*, u.name FROM orders o                   │
  │  JOIN users u ON o.uid = u.id;                      │
  │                                                     │
  │  反范式(冗余用户名,避免 JOIN):                     │
  │                                                     │
  │  orders 表                                          │
  │  ┌────┬─────┬───────────┐                           │
  │  │ id │ uid │ user_name │  ← 冗余字段               │
  │  └────┴─────┴───────────┘                           │
  │                                                     │
  │  直接查询,无需 JOIN:                                │
  │  SELECT * FROM orders WHERE id = 1;                 │
  └─────────────────────────────────────────────────────┘

  适用场景:
  • 读远多于写
  • 频繁 JOIN 影响性能
  • 冗余字段变化频率低(如用户名很少改)
  
  代价:
  • 更新时需同步更新冗余字段
  • 可能导致数据不一致

5.5.3 E-R 模型与建模

E-R(Entity-Relationship)模型:

  实体(Entity):矩形表示
  属性(Attribute):椭圆表示
  关系(Relationship):菱形表示

  电商数据库 E-R 示意:

  ┌──────┐   1    ┌──────┐   N    ┌──────┐
  │ 用户 │───────│ 下单 │───────│ 订单 │
  └──────┘        └──────┘        └──────┘

                                     │ 1

                                  ┌──────┐
                                  │ 包含 │
                                  └──────┘

                                     │ N

                                  ┌──────┐   N    ┌──────┐
                                  │ 订单 │───────│ 商品 │
                                  │ 明细 │        └──────┘
                                  └──────┘
                                              ┌──────┐
                                  ┌──────┐    │ 属于 │
                                  │ 品类 │────│  1:N │
                                  └──────┘    └──────┘

  关系类型:
  1:1  一对一(用户 ↔ 用户详情)
  1:N  一对多(用户 → 订单)
  M:N  多对多(订单 ↔ 商品,通过中间表实现)

5.5.4 命名规范与设计最佳实践

┌─────────────────────────────────────────────────────────────┐
│                 数据库设计最佳实践                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  命名规范:                                                  │
│  • 表名:小写 + 下划线,复数形式 (users, order_items)        │
│  • 字段名:小写 + 下划线 (user_name, created_at)            │
│  • 索引名:idx_表名_字段名 (idx_users_email)                │
│  • 主键:id(自增 BIGINT)                                  │
│  • 外键字段:关联表_id (user_id, order_id)                  │
│  • 布尔字段:is_ 开头 (is_active, is_deleted)              │
│  • 时间字段:created_at, updated_at, deleted_at            │
│                                                             │
│  字段类型选择:                                              │
│  • 整数:优先 INT / BIGINT,避免 TINYINT 后期扩容           │
│  • 金额:DECIMAL(10,2),绝不用 FLOAT/DOUBLE                │
│  • 字符串:固定长度用 CHAR,变长用 VARCHAR                   │
│  • 时间:DATETIME(范围大) 或 TIMESTAMP(自动时区转换)     │
│  • 布尔:TINYINT(1)                                        │
│  • 大文本:TEXT(不参与索引)                                │
│  • JSON:MySQL 5.7+ 的 JSON 类型                           │
│                                                             │
│  表设计原则:                                                │
│  • 每张表必须有主键(推荐自增 BIGINT)                       │
│  • 字段尽量 NOT NULL,使用 DEFAULT 值                       │
│  • 预留 created_at、updated_at 字段                        │
│  • 软删除用 deleted_at(或 is_deleted)而非物理删除          │
│  • 大表预估数据量,提前规划分表策略                           │
│  • 避免使用外键约束(应用层保证一致性)                       │
│  • 注释清晰:表注释 + 字段注释                               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

5.6 实战练习

实战 Demo 1:查看 MySQL 运行状态与配置

sql
-- ==================== 1. 查看 MySQL 版本与系统信息 ====================
SELECT VERSION();
SHOW VARIABLES LIKE 'version%';

-- ==================== 2. 查看 InnoDB 关键配置 ====================
-- Buffer Pool
SHOW VARIABLES LIKE 'innodb_buffer_pool%';
-- 关注:
-- innodb_buffer_pool_size        → 缓冲池大小
-- innodb_buffer_pool_instances   → 缓冲池实例数

-- Redo Log
SHOW VARIABLES LIKE 'innodb_log%';
-- 关注:
-- innodb_log_file_size           → 单个 redo log 大小
-- innodb_log_files_in_group      → redo log 文件数量

-- 刷盘策略
SHOW VARIABLES LIKE 'innodb_flush%';
-- 关注:
-- innodb_flush_log_at_trx_commit → 0/1/2
-- innodb_flush_method            → 刷盘方式

-- ==================== 3. 查看 MySQL 运行状态 ====================
-- Buffer Pool 命中率
SHOW STATUS LIKE 'Innodb_buffer_pool%';
-- 计算命中率:
SELECT
    (1 - Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests) * 100
    AS buffer_pool_hit_rate
FROM (
    SELECT
        VARIABLE_VALUE AS Innodb_buffer_pool_reads
    FROM performance_schema.global_status
    WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads'
) a, (
    SELECT
        VARIABLE_VALUE AS Innodb_buffer_pool_read_requests
    FROM performance_schema.global_status
    WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests'
) b;

-- ==================== 4. 查看连接信息 ====================
SHOW STATUS LIKE 'Threads%';
-- Threads_connected: 当前连接数
-- Threads_running:   当前活跃线程
-- Threads_cached:    缓存的线程数

SHOW VARIABLES LIKE 'max_connections';
-- 最大连接数(默认 151)

-- ==================== 5. 查看 InnoDB 引擎状态 ====================
SHOW ENGINE INNODB STATUS\G
-- 关注以下部分:
-- SEMAPHORES        → 锁等待
-- TRANSACTIONS      → 活跃事务
-- FILE I/O          → IO 状况
-- BUFFER POOL       → 缓冲池状态
-- LOG               → 日志状态
-- ROW OPERATIONS    → 行操作统计

实战 Demo 2:电商数据库设计

sql
-- ==================== 电商数据库完整设计 ====================

CREATE DATABASE IF NOT EXISTS ecommerce DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE ecommerce;

-- ==================== 1. 用户表 ====================
CREATE TABLE users (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID',
    username VARCHAR(50) NOT NULL COMMENT '用户名',
    email VARCHAR(100) NOT NULL COMMENT '邮箱',
    phone VARCHAR(20) DEFAULT NULL COMMENT '手机号',
    password_hash VARCHAR(255) NOT NULL COMMENT '密码哈希',
    nickname VARCHAR(50) DEFAULT '' COMMENT '昵称',
    avatar_url VARCHAR(500) DEFAULT '' COMMENT '头像URL',
    is_active TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否激活: 0-否 1-是',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    deleted_at DATETIME DEFAULT NULL COMMENT '删除时间(软删除)',
    UNIQUE KEY uk_username (username),
    UNIQUE KEY uk_email (email),
    KEY idx_phone (phone),
    KEY idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

-- ==================== 2. 用户地址表 ====================
CREATE TABLE user_addresses (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '地址ID',
    user_id BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
    receiver_name VARCHAR(50) NOT NULL COMMENT '收货人姓名',
    phone VARCHAR(20) NOT NULL COMMENT '收货人手机',
    province VARCHAR(50) NOT NULL COMMENT '省份',
    city VARCHAR(50) NOT NULL COMMENT '城市',
    district VARCHAR(50) NOT NULL COMMENT '区/县',
    detail_address VARCHAR(200) NOT NULL COMMENT '详细地址',
    is_default TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否默认: 0-否 1-是',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    KEY idx_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户地址表';

-- ==================== 3. 商品分类表 ====================
CREATE TABLE categories (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '分类ID',
    parent_id BIGINT UNSIGNED DEFAULT 0 COMMENT '父分类ID, 0表示顶级分类',
    name VARCHAR(100) NOT NULL COMMENT '分类名称',
    sort_order INT NOT NULL DEFAULT 0 COMMENT '排序值(越小越靠前)',
    is_active TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    KEY idx_parent_id (parent_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品分类表';

-- ==================== 4. 商品表 ====================
CREATE TABLE products (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '商品ID',
    category_id BIGINT UNSIGNED NOT NULL COMMENT '分类ID',
    name VARCHAR(200) NOT NULL COMMENT '商品名称',
    description TEXT COMMENT '商品描述',
    price DECIMAL(10,2) NOT NULL COMMENT '销售价格',
    original_price DECIMAL(10,2) DEFAULT NULL COMMENT '原价',
    stock INT NOT NULL DEFAULT 0 COMMENT '库存数量',
    sales INT NOT NULL DEFAULT 0 COMMENT '销量',
    main_image VARCHAR(500) NOT NULL COMMENT '主图URL',
    images JSON DEFAULT NULL COMMENT '商品图片(JSON数组)',
    status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 0-下架 1-上架',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    KEY idx_category_id (category_id),
    KEY idx_status_created (status, created_at),
    KEY idx_price (price)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';

-- ==================== 5. 订单表 ====================
CREATE TABLE orders (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '订单ID',
    order_no VARCHAR(32) NOT NULL COMMENT '订单编号',
    user_id BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
    total_amount DECIMAL(10,2) NOT NULL COMMENT '订单总金额',
    pay_amount DECIMAL(10,2) NOT NULL COMMENT '实付金额',
    freight_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '运费',
    status TINYINT NOT NULL DEFAULT 0 COMMENT '状态: 0-待支付 1-已支付 2-已发货 3-已完成 4-已取消 5-已退款',
    payment_method TINYINT DEFAULT NULL COMMENT '支付方式: 1-微信 2-支付宝 3-银行卡',
    payment_time DATETIME DEFAULT NULL COMMENT '支付时间',
    shipping_time DATETIME DEFAULT NULL COMMENT '发货时间',
    completion_time DATETIME DEFAULT NULL COMMENT '完成时间',
    receiver_name VARCHAR(50) NOT NULL COMMENT '收货人',
    receiver_phone VARCHAR(20) NOT NULL COMMENT '收货人手机',
    receiver_address VARCHAR(300) NOT NULL COMMENT '收货地址',
    remark VARCHAR(500) DEFAULT '' COMMENT '备注',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uk_order_no (order_no),
    KEY idx_user_id_status (user_id, status),
    KEY idx_created_at (created_at),
    KEY idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';

-- ==================== 6. 订单明细表 ====================
CREATE TABLE order_items (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '明细ID',
    order_id BIGINT UNSIGNED NOT NULL COMMENT '订单ID',
    product_id BIGINT UNSIGNED NOT NULL COMMENT '商品ID',
    product_name VARCHAR(200) NOT NULL COMMENT '商品名称(快照)',
    product_image VARCHAR(500) NOT NULL COMMENT '商品图片(快照)',
    price DECIMAL(10,2) NOT NULL COMMENT '购买时单价(快照)',
    quantity INT NOT NULL COMMENT '数量',
    total_price DECIMAL(10,2) NOT NULL COMMENT '小计金额',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    KEY idx_order_id (order_id),
    KEY idx_product_id (product_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细表';

-- ==================== 7. 支付记录表 ====================
CREATE TABLE payments (
    id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '支付ID',
    order_id BIGINT UNSIGNED NOT NULL COMMENT '订单ID',
    payment_no VARCHAR(64) NOT NULL COMMENT '支付流水号',
    amount DECIMAL(10,2) NOT NULL COMMENT '支付金额',
    method TINYINT NOT NULL COMMENT '支付方式: 1-微信 2-支付宝',
    status TINYINT NOT NULL DEFAULT 0 COMMENT '0-待支付 1-成功 2-失败 3-退款',
    paid_at DATETIME DEFAULT NULL COMMENT '支付成功时间',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY uk_payment_no (payment_no),
    KEY idx_order_id (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付记录表';

-- ==================== 插入测试数据 ====================

-- 分类
INSERT INTO categories (parent_id, name, sort_order) VALUES
    (0, '电子产品', 1),
    (0, '图书', 2),
    (1, '手机', 1),
    (1, '电脑', 2),
    (2, '技术书', 1),
    (2, '文学书', 2);

-- 商品
INSERT INTO products (category_id, name, description, price, original_price, stock, main_image) VALUES
    (3, 'iPhone 15 Pro', '最新款 iPhone', 8999.00, 9999.00, 100, '/images/iphone15.jpg'),
    (3, '小米 14', '小米旗舰手机', 3999.00, 4299.00, 200, '/images/mi14.jpg'),
    (4, 'MacBook Pro 14', 'M3 Pro 芯片', 14999.00, 16999.00, 50, '/images/mbp14.jpg'),
    (5, '高性能MySQL(第4版)', 'MySQL 权威指南', 149.00, NULL, 500, '/images/hp-mysql.jpg'),
    (5, 'MySQL是怎样运行的', '从根儿上理解MySQL', 89.00, 99.00, 300, '/images/mysql-run.jpg');

-- 查看表关系
SELECT
    TABLE_NAME AS '表名',
    TABLE_ROWS AS '估计行数',
    ENGINE AS '引擎',
    TABLE_COMMENT AS '说明'
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'ecommerce'
ORDER BY TABLE_NAME;

5.7 常见问题 QA

Q1:为什么 MySQL 8.0 移除了查询缓存?

A:查询缓存(Query Cache)在实际场景中弊大于利:

  • 只要表有任何修改,相关缓存全部失效(写多读少场景命中率极低)
  • 维护缓存的开销(加锁、失效检查)超过了收益
  • 在高并发下查询缓存的锁竞争成为瓶颈
  • 替代方案:使用应用层缓存(Redis)或 ProxySQL 缓存

Q2:InnoDB 为什么必须有主键?

A:InnoDB 是索引组织表(IOT),数据存储在聚簇索引的 B+ 树叶子节点中。主键就是聚簇索引的 key:

  • 如果定义了 PRIMARY KEY → 使用 PRIMARY KEY
  • 如果没有 PRIMARY KEY → 使用第一个 UNIQUE NOT NULL 索引
  • 如果都没有 → InnoDB 自动生成一个隐藏的 6 字节 row_id(但你无法使用它)
  • 建议:总是显式定义主键,推荐自增 BIGINT

Q3:innodb_flush_log_at_trx_commit 设为 0、1、2 有什么区别?

A

行为安全性性能适用场景
0每秒写 Log Buffer → OS cache 并 fsync可能丢 1 秒数据最高非核心数据
1每次 COMMIT 都 fsync 到磁盘不丢数据最低生产默认推荐
2每次 COMMIT 写 OS cache,每秒 fsyncOS 崩溃可能丢 1 秒可接受少量丢失

Q4:Binlog 和 Redo Log 的区别是什么?

A

对比项Redo LogBinlog
层级InnoDB 引擎层MySQL Server 层
内容物理日志(页面的修改)逻辑日志(SQL 或行变化)
写入方式循环写入,固定大小追加写入,无限增长
用途崩溃恢复主从复制、数据恢复
事务相关有,InnoDB 专用有,所有引擎通用

Q5:Buffer Pool 设多大合适?

A

  • 单实例服务器:设为物理内存的 60%-80%
  • 共享服务器:根据实际情况分配,留足 OS 和其他程序的内存
  • 查看当前命中率:命中率 < 99% 说明 Buffer Pool 可能不够大
  • 示例:16GB 内存的服务器,可设 innodb_buffer_pool_size = 10G

Q6:为什么推荐用自增主键?

A

  • 顺序写入:自增 ID 保证新数据总是追加到 B+ 树尾部,避免页分裂
  • 索引效率:自增主键短小(BIGINT 8 字节),二级索引存储主键值,越小越省空间
  • 不推荐 UUID:UUID 随机插入导致频繁页分裂,且 36 字节太长
  • 分布式场景:可使用 Snowflake 算法生成趋势递增的分布式 ID

Q7:什么是页分裂和页合并?

A

  • 页分裂:当一个数据页满了,需要插入新记录时,InnoDB 将页分裂为两个,各存一半数据。随机插入(如 UUID 主键)频繁页分裂,性能差
  • 页合并:当删除大量数据导致页利用率低于阈值(默认 50%),InnoDB 将相邻页合并
  • 优化:使用自增主键避免频繁页分裂

Q8:如何选择 DATETIME 和 TIMESTAMP?

A

对比项DATETIMETIMESTAMP
范围1000-01-01 ~ 9999-12-311970-01-01 ~ 2038-01-19
存储8 字节4 字节
时区存什么显示什么自动转换为 UTC 存储
默认值5.6+ 支持 CURRENT_TIMESTAMP支持 CURRENT_TIMESTAMP
推荐日期范围大、不需要时区转换需要时区转换、存储空间敏感

Q9:生产环境中应该使用外键约束吗?

A一般不推荐在生产环境使用外键约束:

  • 外键检查有额外开销,影响写入性能
  • 大表 DDL 变更时外键是阻碍
  • 分库分表后无法使用外键
  • 外键级联删除可能导致意外数据丢失
  • 推荐:在应用层保证引用完整性,在 E-R 设计文档中标注关系

Q10:MySQL 一行数据最大能存多少?

A

  • InnoDB 行大小限制:约 65535 字节(不含 TEXT/BLOB)
  • 单个页面 16KB,一行数据不能超过页面的一半(约 8KB)
  • 超过限制的 VARCHAR/TEXT/BLOB 会存到溢出页(overflow page)
  • 建议:大文本/大字段用 TEXT/BLOB 类型,让 InnoDB 自动管理溢出

5.8 命令速查表

┌───────────────────────────────────────────────────────────────┐
│                MySQL 架构与引擎 命令速查                        │
├───────────────────────────────────────────────────────────────┤
│  系统信息                                                     │
│  SELECT VERSION()                    查看 MySQL 版本          │
│  SHOW VARIABLES LIKE '...'           查看系统变量             │
│  SHOW STATUS LIKE '...'              查看运行状态             │
│  SHOW PROCESSLIST                    查看当前连接             │
│  SHOW ENGINE INNODB STATUS\G         查看 InnoDB 状态         │
├───────────────────────────────────────────────────────────────┤
│  Buffer Pool                                                  │
│  innodb_buffer_pool_size             缓冲池大小               │
│  innodb_buffer_pool_instances        缓冲池实例数             │
│  Innodb_buffer_pool_read_requests    逻辑读次数               │
│  Innodb_buffer_pool_reads            物理读次数               │
├───────────────────────────────────────────────────────────────┤
│  日志相关                                                     │
│  innodb_log_file_size                Redo Log 文件大小        │
│  innodb_flush_log_at_trx_commit      Redo 刷盘策略 (0/1/2)   │
│  log_bin                             Binlog 是否开启          │
│  binlog_format                       Binlog 格式              │
│  SHOW BINARY LOGS                    查看 Binlog 文件列表     │
│  SHOW BINLOG EVENTS IN '...'         查看 Binlog 事件         │
│  binlog_expire_logs_seconds          Binlog 过期时间          │
├───────────────────────────────────────────────────────────────┤
│  存储引擎                                                     │
│  SHOW ENGINES                        查看支持的引擎           │
│  SHOW TABLE STATUS LIKE '...'        查看表的引擎信息         │
│  ALTER TABLE t ENGINE=InnoDB         修改表引擎               │
│  default_storage_engine              默认引擎                 │
├───────────────────────────────────────────────────────────────┤
│  数据库设计                                                   │
│  SHOW CREATE TABLE t\G               查看建表语句             │
│  DESCRIBE t / DESC t                 查看表结构               │
│  information_schema.TABLES           表元数据                 │
│  information_schema.COLUMNS          列元数据                 │
│  information_schema.STATISTICS       索引元数据               │
├───────────────────────────────────────────────────────────────┤
│  配置文件 (my.cnf) 关键参数                                   │
│  [mysqld]                                                     │
│  innodb_buffer_pool_size = 10G       缓冲池(物理内存60-80%) │
│  innodb_log_file_size = 256M         Redo Log 大小            │
│  innodb_flush_log_at_trx_commit = 1  安全刷盘                 │
│  max_connections = 500               最大连接数               │
│  binlog_format = ROW                 Binlog 格式              │
│  character-set-server = utf8mb4      默认字符集               │
│  innodb_file_per_table = ON          独立表空间               │
└───────────────────────────────────────────────────────────────┘