MySQL启动流程
启动命令:
mysqld --defaults-file=/etc/my.cnf
完整流程(8个阶段):
1. 读取配置文件
2. 初始化文件系统
3. 初始化InnoDB存储引擎
4. 加载系统表
5. 执行崩溃恢复(如需要)
6. 启动后台线程
7. 监听客户端连接
8. 完成启动
1. 读取配置文件
查找顺序
# MySQL配置文件查找顺序(Linux)
1. /etc/my.cnf
2. /etc/mysql/my.cnf
3. /usr/etc/my.cnf
4. ~/.my.cnf
5. --defaults-file指定的文件
# 查看实际读取的配置
mysqld --verbose --help | grep -A 1 "Default options"
关键配置
[mysqld]
# 数据目录
datadir = /var/lib/mysql
# 端口
port = 3306
# 字符集
character-set-server = utf8mb4
# Buffer Pool大小
innodb_buffer_pool_size = 8G
# 日志文件
log-error = /var/log/mysql/error.log
2. 初始化文件系统
# 检查数据目录
1. 检查datadir是否存在
2. 检查datadir权限(mysql用户可读写)
3. 创建临时目录(tmpdir)
# 检查必要文件
1. ibdata1(系统表空间)
2. ib_logfile0/ib_logfile1(redo log)
3. mysql/(系统数据库目录)
# 如果文件不存在
→ 初始化数据库:mysqld --initialize
3. 初始化InnoDB存储引擎
步骤:
1. 分配Buffer Pool内存
├─ 默认128MB,生产环境建议50-80%内存
└─ innodb_buffer_pool_size = 8G
2. 打开表空间文件
├─ ibdata1(系统表空间)
└─ *.ibd(独立表空间)
3. 打开redo log文件
├─ ib_logfile0
└─ ib_logfile1
4. 打开undo表空间(MySQL 8.0+)
├─ undo_001
└─ undo_002
5. 初始化Change Buffer、Adaptive Hash Index
4. 加载系统表
-- 加载系统数据库
mysql/
├─ user.frm/ibd -- 用户表
├─ db.frm/ibd -- 数据库权限
├─ tables_priv.frm/ibd -- 表权限
└─ ...
-- 加载存储引擎信息
information_schema/
performance_schema/
sys/
5. 崩溃恢复(Crash Recovery)
检查是否需要恢复
判断条件:
IF MySQL未正常关闭(如kill -9、断电) THEN
执行崩溃恢复
ELSE
跳过恢复
END IF
-- 判断依据:redo log的checkpoint LSN
恢复流程
1. 读取redo log
├─ 从checkpoint开始读取redo log
└─ 找到所有未刷盘的数据页修改
2. 重做(Redo)
├─ 对于已提交的事务
└─ 应用redo log,恢复数据
3. 回滚(Undo)
├─ 对于未提交的事务
└─ 使用undo log回滚
4. 清理
├─ 清理临时表
└─ 清理未完成的DDL操作
-- 恢复时间:取决于redo log大小和修改量
-- 通常:几秒到几分钟
示例:
-- 崩溃前状态
事务A:已提交,redo log已写入,但数据页未刷盘
事务B:未提交,部分修改在内存中
-- 恢复后状态
事务A:重做(应用redo log,恢复数据)✅
事务B:回滚(使用undo log,撤销修改)✅
6. 启动后台线程
InnoDB后台线程:
1. Master Thread(主线程)
├─ 每秒刷新日志缓冲
├─ 合并Change Buffer
└─ 刷新脏页
2. IO Thread(IO线程)
├─ Read Thread(读线程,4个)
└─ Write Thread(写线程,4个)
3. Page Cleaner Thread(页清理线程)
├─ 刷新脏页到磁盘
└─ innodb_page_cleaners = 4
4. Purge Thread(清理线程)
├─ 清理undo log
└─ innodb_purge_threads = 4
7. 监听客户端连接
启动连接器:
1. 绑定端口(默认3306)
└─ bind-address = 0.0.0.0
2. 监听连接请求
└─ max_connections = 200(最大连接数)
3. 创建线程池(MySQL 8.0企业版)
└─ thread_pool_size = 16
8. 完成启动
-- 启动成功日志
[Note] InnoDB: Buffer pool(s) load completed at 230115 17:00:00
[Note] InnoDB: 5.7.40 started; log sequence number 123456789
[Note] mysqld: ready for connections.
Version: '5.7.40' socket: '/tmp/mysql.sock' port: 3306 MySQL Community Server (GPL)
MySQL关闭流程
1. 正常关闭(Clean Shutdown)
# 关闭命令
mysqladmin -uroot -p shutdown
# 或
systemctl stop mysqld
# 流程:
1. 停止接受新连接
2. 等待当前连接完成(最多wait_timeout秒)
3. 刷新所有脏页到磁盘
4. 刷新redo log到磁盘
5. 关闭表空间文件
6. 关闭后台线程
7. 退出进程
2. 快速关闭(Fast Shutdown,默认)
-- innodb_fast_shutdown参数
0:完全关闭(刷新所有脏页,最慢)
1:快速关闭(标记脏页,默认)
2:强制关闭(不刷新脏页,最快,不推荐)
-- 设置
SET GLOBAL innodb_fast_shutdown = 1;
| 模式 | innodb_fast_shutdown | 刷新脏页 | 关闭时间 | 下次启动 |
|---|---|---|---|---|
| 完全关闭 | 0 | ✅ 全部 | 慢(分钟级) | 快(秒级) |
| 快速关闭 | 1(默认) | ⚠️ 部分 | 快(秒级) | 慢(需恢复) |
| 强制关闭 | 2 | ❌ 不刷新 | 极快(秒级) | 慢(需恢复) |
3. 强制关闭(Crash)
# ❌ 不推荐
kill -9 $(pidof mysqld)
# 问题:
1. 脏页未刷盘
2. redo log未完整
3. 事务未提交或回滚
# 后果:
下次启动需要崩溃恢复,耗时较长
关闭流程详解
正常关闭流程(innodb_fast_shutdown=1):
┌──────────────────────────────────────┐
│ 1. 停止接受新连接 │
├──────────────────────────────────────┤
│ 2. 等待当前事务完成(最多10秒) │
├──────────────────────────────────────┤
│ 3. 刷新Buffer Pool脏页(部分) │
│ └─ Change Buffer合并 │
├──────────────────────────────────────┤
│ 4. 刷新redo log到磁盘 │
├──────────────────────────────────────┤
│ 5. 关闭表空间文件 │
├──────────────────────────────────────┤
│ 6. 关闭后台线程 │
├──────────────────────────────────────┤
│ 7. 记录checkpoint LSN │
├──────────────────────────────────────┤
│ 8. 退出mysqld进程 │
└──────────────────────────────────────┘
实战建议
1. 启动优化
# my.cnf(加快启动速度)
[mysqld]
# 预热Buffer Pool
innodb_buffer_pool_dump_at_shutdown = 1 # 关闭时保存
innodb_buffer_pool_load_at_startup = 1 # 启动时加载
# 减少redo log恢复时间
innodb_log_file_size = 512M # 不要太大
2. 关闭优化
# 快速关闭(生产环境推荐)
SET GLOBAL innodb_fast_shutdown = 1;
mysqladmin shutdown
# 升级/维护前(完全关闭)
SET GLOBAL innodb_fast_shutdown = 0;
mysqladmin shutdown
3. 监控启动时间
# 查看启动日志
grep "ready for connections" /var/log/mysql/error.log
# 输出
2025-01-15T17:00:00.123456Z 0 [Note] mysqld: ready for connections.
# 启动时间:从进程启动到此行出现的时间差
故障排查
1. 启动失败
# 查看错误日志
tail -f /var/log/mysql/error.log
# 常见错误
1. [ERROR] Can't start server: Bind on TCP/IP port: Address already in use
→ 端口占用,检查:netstat -tulnp | grep 3306
2. [ERROR] InnoDB: Unable to lock ./ibdata1, error: 11
→ 文件锁定,检查:ps aux | grep mysqld
3. [ERROR] InnoDB: Corrupted tablespace
→ 表空间损坏,恢复备份
2. 启动慢
-- 原因:崩溃恢复耗时
-- 解决:
1. 查看redo log大小:ls -lh ib_logfile*
2. 减小redo log:innodb_log_file_size = 256M
3. 避免kill -9强制关闭
常见面试题
Q1: MySQL启动流程的关键步骤?
- 读取配置 → 初始化InnoDB → 崩溃恢复 → 启动后台线程 → 监听连接
Q2: 崩溃恢复的过程?
- 读取redo log → 重做已提交事务 → 回滚未提交事务
Q3: innodb_fast_shutdown的作用?
- 0:完全关闭(刷新所有脏页)
- 1:快速关闭(部分刷新,默认)
- 2:强制关闭(不刷新,不推荐)
Q4: 如何加快MySQL启动速度?
- 开启Buffer Pool预热(dump_at_shutdown + load_at_startup)
- 减小redo log大小
- 避免kill -9强制关闭
小结
✅ 启动流程:配置 → 初始化 → 崩溃恢复 → 后台线程 → 监听连接 ✅ 崩溃恢复:redo log重做 + undo log回滚 ✅ 关闭流程:停止连接 → 刷新脏页 → 关闭文件 → 退出进程 ✅ 优化建议:Buffer Pool预热、合理设置innodb_fast_shutdown
理解启动关闭流程有助于故障排查和性能优化。
📚 相关阅读:
- 下一篇:《查询优化器:如何生成最优执行计划》
- 推荐:《redo log:崩溃恢复的保障》