MySQL架构总览
MySQL采用分层架构,从上到下分为4层:
┌────────────────────────────────────────┐
│ 第1层:连接层(Connection Layer) │
│ - 连接器:管理客户端连接 │
│ - 线程池:处理并发连接 │
├────────────────────────────────────────┤
│ 第2层:SQL层(SQL Layer / Server层) │
│ - 查询缓存(已废弃) │
│ - 分析器:词法分析 + 语法分析 │
│ - 优化器:生成执行计划 │
│ - 执行器:调用存储引擎接口 │
├────────────────────────────────────────┤
│ 第3层:存储引擎层(Storage Engine) │
│ - InnoDB(默认) │
│ - MyISAM、Memory等 │
├────────────────────────────────────────┤
│ 第4层:文件系统层(File System) │
│ - 数据文件、日志文件、配置文件 │
└────────────────────────────────────────┘
1. 连接器(Connector)
作用
管理客户端连接,进行身份验证和权限验证。
连接过程
-- 客户端连接命令
mysql -h127.0.0.1 -P3306 -uroot -p
-- 连接流程
1. TCP握手建立连接
2. 验证用户名密码(authentication_string)
3. 获取用户权限列表(grant tables)
4. 返回连接ID(connection_id)
查看连接信息
-- 查看当前连接
SHOW PROCESSLIST;
-- 输出示例
+----+------+-----------+------+---------+------+----------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+------+---------+------+----------+------------------+
| 1 | root | localhost | test | Query | 0 | starting | SHOW PROCESSLIST |
| 2 | root | localhost | NULL | Sleep | 600 | NULL | NULL |
+----+------+-----------+------+---------+------+----------+------------------+
连接状态
| 状态 | 含义 | 典型场景 |
|---|---|---|
| Sleep | 空闲,等待客户端发送请求 | 连接池中的空闲连接 |
| Query | 正在执行SQL | 查询、更新、删除等 |
| Locked | 等待锁释放 | 表锁、行锁冲突 |
| Sorting | 正在排序 | ORDER BY、GROUP BY |
| Sending | 发送数据给客户端 | 大结果集传输 |
长连接与短连接
长连接:连接保持,减少连接开销
// 连接池配置(HikariCP)
HikariConfig config = new HikariConfig();
config.setMaximumPoolSize(10); // 最大连接数
config.setMinimumIdle(2); // 最小空闲连接
config.setIdleTimeout(600000); // 空闲超时(10分钟)
短连接:每次查询后断开
# 每次查询建立新连接
conn = mysql.connect()
cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
conn.close()
问题:长连接占用内存,导致OOM
-- 查看内存占用
SHOW STATUS LIKE 'Threads_connected'; -- 当前连接数
-- 定期断开空闲连接
SET SESSION wait_timeout = 3600; -- 1小时超时
2. 查询缓存(Query Cache)
状态
MySQL 8.0已彻底废弃(因为缓存失效频繁,收益低)。
原理(历史)
-- 查询时,先计算SQL的Hash
SELECT * FROM users WHERE id = 1;
-- Hash: 0x12345678
-- 查询缓存命中
1. 检查缓存中是否有相同Hash
2. 如果有,直接返回缓存结果
3. 如果没有,执行查询,将结果缓存
-- 缓存失效
UPDATE users SET name = 'Alice' WHERE id = 1;
-- 清空users表的所有查询缓存
为什么废弃?
- 更新频繁:任何表的更新都会清空该表的缓存
- 缓存命中率低:SQL稍有不同就无法命中
- 维护成本高:缓存管理开销大
替代方案:应用层缓存(Redis、Memcached)
3. 分析器(Parser)
作用
词法分析 + 语法分析,将SQL转换为抽象语法树(AST)。
词法分析
-- 原始SQL
SELECT id, name FROM users WHERE id = 1;
-- 词法分析:识别关键字、标识符、运算符
Token1: SELECT(关键字)
Token2: id(标识符)
Token3: ,(分隔符)
Token4: name(标识符)
Token5: FROM(关键字)
Token6: users(表名)
Token7: WHERE(关键字)
Token8: id(列名)
Token9: =(运算符)
Token10: 1(数值)
Token11: ;(结束符)
语法分析
-- 语法分析:构建抽象语法树(AST)
SELECT
├─ 字段列表
│ ├─ id
│ └─ name
├─ FROM子句
│ └─ users表
└─ WHERE子句
└─ id = 1
-- 语法错误检测
SELECT * FORM users; -- ❌ FORM拼写错误
-- ERROR 1064 (42000): You have an error in your SQL syntax
4. 优化器(Optimizer)
作用
生成执行计划,选择最优的执行方式。
优化内容
-- 示例SQL
SELECT * FROM orders o
JOIN users u ON o.user_id = u.id
WHERE u.name = 'Alice' AND o.status = 'PAID';
-- 优化器决策
1. 表连接顺序:先查users还是orders?
- 成本评估:users表小,users驱动orders
2. 索引选择:使用哪个索引?
- users表:idx_name索引
- orders表:idx_user_id索引
3. 条件下推:WHERE条件尽早过滤
- 先筛选users.name='Alice'
- 再关联orders表
优化示例
-- 原始SQL
SELECT * FROM t1, t2 WHERE t1.id = t2.id AND t1.status = 1;
-- 优化后
1. 扫描t1表(status=1),使用idx_status索引
2. 对每行t1数据,通过idx_id索引查找t2表
3. 返回结果
查看执行计划:
EXPLAIN SELECT * FROM orders WHERE user_id = 1;
5. 执行器(Executor)
作用
调用存储引擎接口,执行查询,返回结果。
执行流程
-- 示例SQL
SELECT * FROM users WHERE id = 1;
-- 执行器流程
1. 权限检查:当前用户是否有SELECT权限
2. 调用InnoDB接口:handler->index_read(id=1)
3. InnoDB返回数据:一行记录
4. 判断WHERE条件:id=1(满足)
5. 返回结果给客户端
-- 执行器与存储引擎的交互
┌──────────┐ ┌──────────┐
│ 执行器 │───────▶│ InnoDB │
│ │◀───────│ │
└──────────┘ └──────────┘
调用接口 返回数据
存储引擎接口
// 存储引擎接口(handler)
handler->rnd_init() // 全表扫描初始化
handler->rnd_next() // 读取下一行
handler->index_read() // 索引查找
handler->index_next() // 索引读取下一行
handler->write_row() // 插入行
handler->update_row() // 更新行
handler->delete_row() // 删除行
完整SQL执行流程
-- 客户端执行
SELECT name FROM users WHERE id = 1;
-- 完整流程
┌─────────────────────────────────────────────────┐
│ 1. 连接器:验证身份,获取权限 │
├─────────────────────────────────────────────────┤
│ 2. 查询缓存:检查缓存(MySQL 8.0已废弃) │
├─────────────────────────────────────────────────┤
│ 3. 分析器:词法分析 + 语法分析,生成AST │
├─────────────────────────────────────────────────┤
│ 4. 优化器:选择索引、连接顺序,生成执行计划 │
├─────────────────────────────────────────────────┤
│ 5. 执行器:调用InnoDB接口,读取数据 │
│ ├─ handler->index_read(id=1) │
│ ├─ InnoDB返回:(id=1, name='Alice') │
│ └─ 返回结果给客户端 │
└─────────────────────────────────────────────────┘
-- 时间分布(示例)
连接器: 0.1ms
分析器: 0.5ms
优化器: 1.0ms
执行器: 5.0ms(包含InnoDB查询)
总耗时: 6.6ms
实战建议
1. 连接池配置
# my.cnf
[mysqld]
max_connections = 200 # 最大连接数
max_connect_errors = 100 # 最大连接错误数
wait_timeout = 3600 # 空闲连接超时(1小时)
interactive_timeout = 3600 # 交互式连接超时
2. 慢查询分析
-- 查看SQL执行过程
SET profiling = 1;
SELECT * FROM users WHERE name = 'Alice';
SHOW PROFILES;
SHOW PROFILE FOR QUERY 1;
3. 避免长连接内存泄漏
// 定期重连(每1000次查询)
int queryCount = 0;
while (true) {
if (queryCount++ > 1000) {
connection.close();
connection = getNewConnection();
queryCount = 0;
}
// 执行查询
}
常见面试题
Q1: MySQL的分层架构是什么?
- 连接层、SQL层、存储引擎层、文件系统层
Q2: 分析器、优化器、执行器的作用?
- 分析器:词法分析+语法分析,生成AST
- 优化器:生成执行计划(索引选择、连接顺序)
- 执行器:调用存储引擎接口,执行查询
Q3: 为什么MySQL 8.0废弃查询缓存?
- 更新频繁,缓存失效率高,收益低
Q4: 如何查看SQL的执行流程?
EXPLAIN:查看执行计划SHOW PROFILES:查看执行耗时分布
小结
✅ 分层架构:连接层、SQL层、存储引擎层、文件系统层 ✅ 连接器:管理连接,验证权限 ✅ 分析器:词法分析+语法分析 ✅ 优化器:生成执行计划 ✅ 执行器:调用存储引擎接口
理解MySQL架构是深入学习的基础。
📚 相关阅读:
- 下一篇:《存储引擎对比:InnoDB vs MyISAM》
- 推荐:《查询优化器:如何生成最优执行计划》