文章目录
- 前言
- [一、InnoDB 概览](#一、InnoDB 概览)
- 二、核心组件与工作流
-
- [1. Buffer Pool(缓冲池)](#1. Buffer Pool(缓冲池))
- [2. Redo Log(重做日志)](#2. Redo Log(重做日志))
- [3. Undo Log(回滚日志)](#3. Undo Log(回滚日志))
- [4. MVCC(多版本并发控制)](#4. MVCC(多版本并发控制))
- [5. 锁机制](#5. 锁机制)
- [6. 崩溃恢复](#6. 崩溃恢复)
- 三、事务与隔离
-
- [1. 建表示例(使用 InnoDB)](#1. 建表示例(使用 InnoDB))
- [2. 事务示例:转账(原子性演示)](#2. 事务示例:转账(原子性演示))
- [3. MVCC 快照读取 / 加锁读取](#3. MVCC 快照读取 / 加锁读取)
- [4. 隔离级别与幻读(next-key lock)](#4. 隔离级别与幻读(next-key lock))
- 四、并发控制与死锁排查
-
- [1. 常见并发问题](#1. 常见并发问题)
- [2. 死锁诊断](#2. 死锁诊断)
- [3. 实践建议避免死锁](#3. 实践建议避免死锁)
- 五、崩溃恢复与持久性参数
-
- [1. my.cnf](#1. my.cnf)
- [2. 崩溃恢复流程](#2. 崩溃恢复流程)
- 六、性能监控与诊断命令
前言
随着互联网应用对数据一致性、并发和恢复能力的要求不断上升,MySQL 中最常用的事务型存储引擎 InnoDB 成为生产环境的默认选择。
一、InnoDB 概览
InnoDB 是 MySQL 的事务型存储引擎,主要特性:
- 支持 ACID 事务(原子性、一致性、隔离性、持久性)。
- 支持行级锁(row-level locking)和多版本并发控制(MVCC),以提高并发吞吐。
- 支持崩溃恢复(undo / redo 日志 + checkpoint)。
- 支持外键约束(foreign key)。
- 主键聚簇索引(clustered index):表数据按主键存储在 B+Tree 上。
核心目标是用较低成本在高并发场景下提供正确的事务语义与高可用性。
二、核心组件与工作流
1. Buffer Pool(缓冲池)
- 内存组件,用于缓存数据页(页大小通常为16KB)。
- 读写操作尽量在内存中完成,减少磁盘 IO。
- 关键参数:
innodb_buffer_pool_size(生产环境通常设置为机器内存的 60%~80%)。
2. Redo Log(重做日志)
- 用于保证事务的持久性(durability),记录物理修改(redo)。
- 写到 redo log 后可视为"持久化",即使数据页尚未刷到磁盘。
- 参数:
innodb_log_file_size、innodb_log_files_in_group、innodb_flush_log_at_trx_commit。
3. Undo Log(回滚日志)
- 用于事务回滚和 MVCC(为旧版本行提供快照)。
- Undo 存在于 undo 表空间(可配置为独立 undo tablespaces)。
4. MVCC(多版本并发控制)
- 通过在行中维护事务信息(每行有创建事务 id /回退信息),读取操作可以得到一致性快照,从而实现可重复读等隔离级别而不总是加锁。
- InnoDB 的默认隔离级别是 REPEATABLE READ(实现方式包括隐藏的 next-key locks 来避免幻读)。
5. 锁机制
- 行级锁(基于索引的记录锁)和间隙锁(gap lock / next-key lock)。
- 读锁和写锁:普通 SELECT 在 MVCC 下通常是无锁快照读取;SELECT ... FOR UPDATE 会加锁。
6. 崩溃恢复
- 在重启时,InnoDB 使用 redo log 恢复已提交但未刷回的数据页;并对未提交事务进行回滚(undo)。
- checkpoint(脏页刷新)影响恢复时间与性能。
三、事务与隔离
下面通过 SQL 示例演示事务行为与隔离级别差异。
1. 建表示例(使用 InnoDB)
sql
CREATE DATABASE IF NOT EXISTS demo;
USE demo;
CREATE TABLE users_innodb (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
balance DECIMAL(10,2) NOT NULL DEFAULT 0.00,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2. 事务示例:转账(原子性演示)
sql
-- 事务 A:转账 $100 从 user 1 到 user 2
START TRANSACTION;
UPDATE users_innodb SET balance = balance - 100 WHERE id = 1;
UPDATE users_innodb SET balance = balance + 100 WHERE id = 2;
COMMIT;
如果中间任一步失败(例如违反约束或网络断开),执行 ROLLBACK; 可以回退 undo 日志。
3. MVCC 快照读取 / 加锁读取
在 session1:
sql
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT balance FROM users_innodb WHERE id = 1; -- 快照读取
-- 不提交,保持事务打开
在 session2:
sql
UPDATE users_innodb SET balance = balance + 50 WHERE id = 1;
COMMIT;
回到 session1:
sql
SELECT balance FROM users_innodb WHERE id = 1; -- 仍然看到事务开始时的值(快照)
COMMIT;
若 session1 使用 SELECT ... FOR UPDATE,则会对该行加写锁,session2 的 UPDATE 会被阻塞或造成死锁。
4. 隔离级别与幻读(next-key lock)
InnoDB 在 REPEATABLE READ 用 next-key lock 防止幻读。下面是一个简化场景:
- session1 在范围内做
SELECT * FROM users_innodb WHERE id BETWEEN 10 AND 20 FOR UPDATE,InnoDB 会对范围加间隙锁,阻止其他事务插入到该范围,从而避免幻读。
四、并发控制与死锁排查
1. 常见并发问题
- 竞争热点(单行/单页频繁更新)。
- 长事务导致 undo 压力与 MVCC 读/view 保留,阻塞 purge(清理旧版本)。
- 不合理索引导致大量锁升级或全表扫描导致锁范围扩大。
2. 死锁诊断
MySQL 提供 SHOW ENGINE INNODB STATUS\G,其中包含最近的死锁信息(victim、事务 id、锁等待链等)。示例:
sql
SHOW ENGINE INNODB STATUS\G
解析死锁信息:关注 LATEST DETECTED DEADLOCK 段,查看各事务持有与等待的锁、SQL 语句。
3. 实践建议避免死锁
- 保持统一的访问顺序(多个事务访问多表/多行时按固定顺序访问)。
- 使用合适的索引,避免范围锁误用。
- 尽量缩短事务时长(避免在事务中执行用户交互或网络 IO)。
- 对热点更新使用队列或分片(减少集中写)。
五、崩溃恢复与持久性参数
1. my.cnf
ini
[mysqld]
innodb_buffer_pool_size = 8G
innodb_log_file_size = 1G
innodb_log_files_in_group = 2
innodb_flush_log_at_trx_commit = 1
innodb_flush_method = O_DIRECT
innodb_file_per_table = 1
innodb_undo_tablespaces = 2
innodb_max_dirty_pages_pct = 75
说明:
-
innodb_buffer_pool_size:缓存越大读写命中越高,但不能超过物理内存。 -
innodb_log_file_size:较大的 redo log 可减少 checkpoint 频率,但恢复时间可能增加。 -
innodb_flush_log_at_trx_commit:1:每次提交将日志写入并刷盘(最安全,最慢)。2:每次提交写入操作系统缓存,1s 刷盘(可能丢失1s数据)。0:每秒刷盘(最快但风险最大)。
-
innodb_flush_method = O_DIRECT:避免双重缓存(减少文件系统缓存竞争)。
2. 崩溃恢复流程
- 启动时读 redo log 并应用已提交但未刷盘的数据页面。
- 对未提交事务应用 undo 回滚。
- purge 过程清理旧版本行并回收空间。
六、性能监控与诊断命令
常用命令与技巧:
- 查看 InnoDB 状态:
sql
SHOW ENGINE INNODB STATUS\G
- 查看重要变量:
sql
SHOW GLOBAL VARIABLES LIKE 'innodb_%';
- 查看缓冲池使用情况(MySQL 5.6+):
sql
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool%';
- 查看锁等待与事务:
sql
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
- 慢查询分析:启用
slow_query_log并分析慢 SQL。