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 loginnodb_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:崩溃恢复的保障》