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. 返回连接IDconnection_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表的所有查询缓存

为什么废弃?

  1. 更新频繁:任何表的更新都会清空该表的缓存
  2. 缓存命中率低:SQL稍有不同就无法命中
  3. 维护成本高:缓存管理开销大

替代方案:应用层缓存(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》
  • 推荐:《查询优化器:如何生成最优执行计划》