小红书Java面试被问:SQL语句的执行过程解析

一、SQL语句完整执行流程图

text

复制

下载

复制代码
┌─────────────────────────────────────────────────────┐
│                   客户端发起SQL请求                   │
└─────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────┐
│           连接器:建立连接、权限验证                  │
└─────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────┐
│         查询缓存:命中则直接返回(MySQL 8.0移除)     │
└─────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────┐
│         分析器:词法分析、语法分析、语义分析           │
└─────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────┐
│         优化器:生成最优执行计划                       │
└─────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────┐
│         执行器:调用存储引擎执行计划                   │
└─────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────┐
│         存储引擎:磁盘读写、索引查找等                 │
└─────────────────────────────────────────────────────┘

二、各阶段详细解析

1. 连接器阶段(Connector)

🔌 连接建立过程

sql

复制

下载

复制代码
-- 客户端连接命令
mysql -h$ip -P$port -u$user -p

-- 连接建立后的内部处理
1. TCP三次握手建立连接
2. 验证用户名和密码
3. 权限验证(读取user表权限信息)
4. 分配连接线程(线程池管理)
📊 连接状态管理

sql

复制

下载

复制代码
-- 查看当前连接
SHOW PROCESSLIST;
-- 结果示例:
-- Id   User    Host           db   Command Time State      Info
-- 5    root    localhost:1234 test Query   0    starting   SELECT * FROM users

-- 连接参数配置
-- wait_timeout:非交互连接超时时间(默认8小时)
-- interactive_timeout:交互连接超时时间(默认8小时)
-- max_connections:最大连接数(默认151)

-- 连接资源消耗
-- 每个连接占用约256KB内存
-- 连接过多会导致内存耗尽和上下文切换开销

2. 查询缓存阶段(Query Cache) - MySQL 8.0已移除

🗂️ 缓存机制原理

sql

复制

下载

复制代码
-- 缓存键的生成
-- Key = 查询语句本身 + 当前数据库 + 客户端协议版本等
-- Value = 查询结果集

-- 缓存命中条件
SELECT * FROM users WHERE id = 1;  -- 命中
SELECT * FROM users WHERE id = 1;  -- 完全相同的语句

SELECT * FROM users WHERE id = 2;  -- 不命中(参数不同)
SELECT * FROM USERS WHERE ID = 1;  -- 不命中(大小写不同)

-- 导致缓存失效的操作
INSERT INTO users ...    -- 相关表的所有缓存失效
UPDATE users ...         -- 相关表的所有缓存失效  
DELETE FROM users ...    -- 相关表的所有缓存失效
ALTER TABLE users ...    -- 表结构变更,缓存失效
缓存性能问题

sql

复制

下载

复制代码
-- 为什么MySQL 8.0移除了查询缓存?
-- 1. 缓存失效过于频繁
-- 2. 并发场景下缓存锁竞争严重
-- 3. 内存占用与实际收益不成正比
-- 4. 更推荐使用应用层缓存(如Redis)

3. 分析器阶段(Parser)

🔍 词法分析(Lexical Analysis)

sql

复制

下载

复制代码
-- 原始SQL语句
SELECT id, name FROM users WHERE age > 18;

-- 词法分析结果(Token流):
[关键字:SELECT] [标识符:id] [逗号:] [标识符:name] 
[关键字:FROM] [标识符:users] [关键字:WHERE] 
[标识符:age] [操作符:>] [数字:18] [分号:;]
📐 语法分析(Syntax Analysis)

sql

复制

下载

复制代码
-- 构建语法树(AST - Abstract Syntax Tree)
SELECT
├── COLUMNS
│   ├── id
│   └── name
├── FROM
│   └── users
└── WHERE
    └── age > 18

-- 语法检查规则
-- 1. 关键字顺序是否正确
-- 2. 表名、列名是否存在
-- 3. WHERE条件格式是否正确
-- 4. GROUP BY/HAVING/ORDER BY顺序

-- 常见语法错误
SELECT * FORM users;      -- 错误:FORM应为FROM
SELECT * FROM user WHERE; -- 错误:WHERE后缺少条件
🎯 语义分析(Semantic Analysis)

sql

复制

下载

复制代码
-- 语义检查内容
-- 1. 数据类型兼容性检查
SELECT name + age FROM users;  -- 字符串+数字,可能报错

-- 2. 权限验证(表级、列级)
SELECT salary FROM employees;  -- 检查是否有salary列的查询权限

-- 3. 完整性约束检查
INSERT INTO orders(user_id) VALUES(999);  -- 检查外键约束

-- 4. 视图展开
-- 如果查询视图,将视图定义展开为基表查询

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

4. 优化器阶段(Optimizer)

⚙️ 优化器工作流程

sql

复制

下载

复制代码
-- 基于成本的优化器(CBO - Cost-Based Optimizer)
1. 收集统计信息
   - 表的数据量(rows)
   - 列的基数(cardinality)
   - 索引的选择性
   - 数据分布直方图

2. 生成候选执行计划
   -- 示例查询
   SELECT * FROM orders o
   JOIN customers c ON o.customer_id = c.id
   WHERE o.status = 'SHIPPED' AND c.country = 'US';

   -- 可能的执行计划:
   -- Plan A: 先过滤orders,再join customers
   -- Plan B: 先过滤customers,再join orders
   -- Plan C: 使用不同的join顺序和算法

3. 估算每个计划的成本
   -- 成本因素:
   -- IO成本:磁盘读取次数
   -- CPU成本:计算复杂度
   -- 内存成本:临时表、排序等
   -- 网络成本(分布式数据库)

4. 选择成本最低的执行计划
🔄 优化器优化策略
1. 查询重写优化

sql

复制

下载

复制代码
-- 原始查询
SELECT * FROM users WHERE id IN (1, 2, 3);

-- 优化后等价查询
SELECT * FROM users WHERE id = 1 
UNION ALL 
SELECT * FROM users WHERE id = 2
UNION ALL 
SELECT * FROM users WHERE id = 3;

-- 其他重写优化:
-- 视图合并(View Merging)
-- 谓词下推(Predicate Pushdown)
-- 子查询优化(Subquery Optimization)
-- 消除冗余(Redundancy Elimination)
2. 访问路径优化

sql

复制

下载

复制代码
-- 不同访问路径的成本估算
EXPLAIN SELECT * FROM users WHERE age > 20;

-- 可能的访问路径:
-- 1. 全表扫描(Table Scan)
--    成本:表数据页数 × 读取单页成本

-- 2. 索引扫描(Index Scan)
--    成本:索引高度 + 索引叶节点数 + 回表成本

-- 3. 索引覆盖扫描(Covering Index)
--    成本:索引高度 + 索引叶节点数(无需回表)

-- 4. 索引范围扫描(Index Range Scan)
-- 5. 索引快速全扫描(Index Fast Full Scan)
-- 6. 索引跳跃扫描(Index Skip Scan)
3. 连接优化

sql

复制

下载

复制代码
-- 连接算法选择
-- 示例查询
SELECT * FROM orders o JOIN order_items i ON o.id = i.order_id;

-- 可选的连接算法:
-- 1. Nested Loop Join(嵌套循环连接)
--    适用:小表驱动大表,有高效索引

-- 2. Hash Join(哈希连接)
--    适用:无索引,等值连接,内存充足
--    步骤:
--       a. 构建阶段:扫描小表,构建哈希表
--       b. 探测阶段:扫描大表,在哈希表中查找匹配

-- 3. Sort Merge Join(排序合并连接)
--    适用:非等值连接,数据已排序或可排序

-- 连接顺序优化
-- 多表连接时,优化器会尝试不同的连接顺序
-- 对于n个表,可能的连接顺序数 = (2n-2)! / (n-1)!
-- 优化器使用动态规划算法选择最优顺序
4. 排序和分组优化

sql

复制

下载

复制代码
-- 排序优化
SELECT * FROM users ORDER BY name;

-- 排序算法:
-- 1. 内存排序(Quick Sort/Heap Sort)
--    适用:数据量小,能放入排序缓冲区(sort_buffer_size)

-- 2. 外部归并排序(External Merge Sort)
--    适用:数据量大,需使用磁盘临时文件

-- 3. 使用索引避免排序
--    如果ORDER BY列有索引,可以直接按索引顺序读取

-- 分组优化
SELECT department, COUNT(*) FROM employees GROUP BY department;

-- 分组算法:
-- 1. 松散索引扫描(Loose Index Scan)
--    适用:GROUP BY列是索引前缀

-- 2. 紧凑索引扫描(Tight Index Scan)
--    适用:GROUP BY列不是索引前缀但可通过索引访问

-- 3. 临时表分组(Temporary Table Grouping)
--    适用:无法使用索引,数据量较大
📈 优化器统计信息

sql

复制

下载

复制代码
-- MySQL统计信息收集
ANALYZE TABLE users;  -- 收集表的统计信息

-- 查看统计信息
SHOW TABLE STATUS LIKE 'users';
-- Rows: 表的估计行数
-- Data_length: 数据大小
-- Index_length: 索引大小

-- 查看索引统计信息
SHOW INDEX FROM users;
-- Cardinality: 索引的唯一值数量
-- 选择性 = Cardinality / 总行数

-- 直方图统计(MySQL 8.0+)
ANALYZE TABLE users UPDATE HISTOGRAM ON age;
-- 用于优化非等值查询
SELECT * FROM users WHERE age > 30;

5. 执行器阶段(Executor)

🚀 执行器工作流程

sql

复制

下载

复制代码
-- 执行器调用接口
-- 以InnoDB引擎为例:
1. 打开表获取表定义信息
2. 调用存储引擎接口读取数据
3. 应用WHERE条件过滤
4. 执行连接、排序、分组等操作
5. 返回结果给客户端

-- 执行器与存储引擎的交互
-- 执行器遵循"火山模型"(Volcano Model)
-- 每次调用next()方法获取下一行数据
🔧 执行计划解读

sql

复制

下载

复制代码
-- 使用EXPLAIN查看执行计划
EXPLAIN FORMAT=JSON 
SELECT * FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE o.amount > 1000 AND c.country = 'US';

-- 关键信息解读:
{
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "1024.50"  -- 查询总成本
    },
    "nested_loop": [           -- 连接算法
      {
        "table": {
          "table_name": "o",   -- 驱动表
          "access_type": "range",  -- 访问类型
          "possible_keys": ["idx_amount"],
          "key": "idx_amount",     -- 使用的索引
          "rows_examined_per_scan": 5000,
          "rows_produced_per_join": 1000,
          "filtered": "20.00",     -- 过滤比例
          "cost_info": {
            "read_cost": "500.00",
            "eval_cost": "200.00",
            "prefix_cost": "700.00"  -- 累积成本
          }
        }
      },
      {
        "table": {
          "table_name": "c",   -- 被驱动表
          "access_type": "eq_ref",
          "possible_keys": ["PRIMARY"],
          "key": "PRIMARY",
          "rows_examined_per_scan": 1,
          "filtered": "100.00",
          "cost_info": {
            "read_cost": "324.50",
            "eval_cost": "0.00",
            "prefix_cost": "1024.50"  -- 最终成本
          }
        }
      }
    ]
  }
}

6. 存储引擎阶段(Storage Engine)

💾 数据读取过程

sql

复制

下载

复制代码
-- InnoDB数据读取示例
SELECT * FROM users WHERE id = 1;

-- 读取步骤:
1. 根据id=1计算B+树查找路径
2. 从根节点开始,逐层查找
3. 在叶节点找到对应记录
4. 如果使用聚簇索引,直接获取行数据
5. 如果使用二级索引,需要回表查询

-- 数据页结构
-- 每个数据页默认16KB,包含:
-- 页头(Page Header):页的元信息
-- 行记录(Row Records):实际数据
-- 页目录(Page Directory):行记录的槽位
-- 页尾(Page Tailer):校验和
缓冲池优化

sql

复制

下载

复制代码
-- InnoDB缓冲池(Buffer Pool)
-- 内存缓存池,减少磁盘IO

-- 查看缓冲池状态
SHOW ENGINE INNODB STATUS\G
-- Buffer pool size: 缓冲池总大小
-- Free buffers: 空闲页数量
-- Database pages: 数据页数量
-- Modified db pages: 脏页数量

-- 缓冲池算法:LRU(最近最少使用)
-- 新读取的页放入LRU列表的midpoint位置
-- 经常访问的页会移到LRU列表的热端

-- 关键配置参数:
-- innodb_buffer_pool_size: 缓冲池大小(推荐物理内存的70-80%)
-- innodb_buffer_pool_instances: 缓冲池实例数(减少锁竞争)

三、不同SQL类型的执行差异

1. SELECT查询

sql

复制

下载

复制代码
-- 简单查询
SELECT * FROM users WHERE id = 1;
-- 执行路径:索引查找 → 返回数据

-- 复杂查询
SELECT u.name, COUNT(o.id) 
FROM users u 
LEFT JOIN orders o ON u.id = o.user_id 
WHERE u.age > 18 
GROUP BY u.id 
HAVING COUNT(o.id) > 5 
ORDER BY COUNT(o.id) DESC;
-- 执行路径:过滤 → 连接 → 分组 → 过滤 → 排序 → 返回

2. INSERT操作

sql

复制

下载

复制代码
-- 单条插入
INSERT INTO users(name, age) VALUES('John', 25);
-- 执行路径:
-- 1. 检查约束(主键、唯一、外键)
-- 2. 申请自增ID(如果有自增列)
-- 3. 写入undo log(用于回滚)
-- 4. 写入redo log(持久化保证)
-- 5. 修改缓冲池数据页
-- 6. 写入binlog(主从复制)

-- 批量插入
INSERT INTO users(name, age) VALUES('A',20),('B',21),('C',22);
-- 优化:减少事务提交次数,使用批量写入

3. UPDATE操作

sql

复制

下载

复制代码
UPDATE users SET age = age + 1 WHERE id = 1;
-- 执行路径:
-- 1. 查找要更新的行(使用索引)
-- 2. 写入undo log(记录旧值)
-- 3. 写入redo log
-- 4. 修改缓冲池数据页
-- 5. 写入binlog
-- 6. 如果是InnoDB,使用行锁锁定该行

4. DELETE操作

sql

复制

下载

复制代码
DELETE FROM users WHERE id = 1;
-- 执行路径:
-- 1. 查找要删除的行
-- 2. 写入undo log
-- 3. 写入redo log
-- 4. 标记行为删除状态(不会立即物理删除)
-- 5. Purge线程异步清理删除标记的行

四、性能调优关注点

1. 执行计划分析

sql

复制

下载

复制代码
-- 使用EXPLAIN分析
EXPLAIN SELECT * FROM users WHERE age > 20;

-- 关键字段解读:
-- type: 访问类型(const, eq_ref, ref, range, index, ALL)
-- key: 实际使用的索引
-- rows: 估计需要检查的行数
-- filtered: 条件过滤的百分比
-- Extra: 额外信息(Using index, Using temporary, Using filesort)

-- 使用EXPLAIN ANALYZE(MySQL 8.0.18+)
EXPLAIN ANALYZE SELECT * FROM users WHERE age > 20;
-- 显示实际执行时间、循环次数等详细信息

2. 索引优化

sql

复制

下载

复制代码
-- 创建合适的索引
-- 示例:复合索引的最左前缀原则
CREATE INDEX idx_name_age ON users(name, age);

-- 以下查询能使用索引:
SELECT * FROM users WHERE name = 'John';
SELECT * FROM users WHERE name = 'John' AND age = 25;

-- 以下查询不能使用索引:
SELECT * FROM users WHERE age = 25;  -- 不符合最左前缀

-- 索引选择策略:
-- 1. 选择性高的列在前
-- 2. 等值查询列在前,范围查询列在后
-- 3. 避免冗余索引

3. 查询优化

sql

复制

下载

复制代码
-- 避免全表扫描
-- 坏:无索引条件
SELECT * FROM users WHERE phone LIKE '%1234%';

-- 好:使用索引前缀
SELECT * FROM users WHERE phone LIKE '1234%';

-- 避免使用SELECT *
-- 坏:读取所有列
SELECT * FROM users WHERE id = 1;

-- 好:只读取需要的列
SELECT name, age FROM users WHERE id = 1;

-- 使用LIMIT分页优化
-- 坏:偏移量大时性能差
SELECT * FROM users LIMIT 1000000, 20;

-- 好:使用索引覆盖+游标分页
SELECT * FROM users WHERE id > 1000000 LIMIT 20;

五、高级特性与优化

1. 查询执行计划缓存

sql

复制

下载

复制代码
-- Prepared Statement执行计划缓存
PREPARE stmt FROM 'SELECT * FROM users WHERE id = ?';
SET @id = 1;
EXECUTE stmt USING @id;

-- 优点:
-- 1. 避免重复解析和优化
-- 2. 防止SQL注入
-- 3. 执行计划重用

-- 查看执行计划缓存
SHOW STATUS LIKE 'Qcache%';  -- 查询缓存状态

2. 并行查询(MySQL 8.0+)

sql

复制

下载

复制代码
-- 启用并行查询
SET SESSION innodb_parallel_read_threads = 4;

-- 适合并行查询的场景:
-- 1. 全表扫描
-- 2. 大表统计查询
-- 3. 无索引的聚合查询

-- 查看并行查询使用情况
EXPLAIN ANALYZE 
SELECT COUNT(*) FROM large_table;

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

3. 物化视图与查询重写

sql

复制

下载

复制代码
-- MySQL 8.0+ 支持物化视图
CREATE TABLE sales_summary AS
SELECT product_id, SUM(amount) as total
FROM sales
GROUP BY product_id;

-- 定期刷新物化视图
REFRESH MATERIALIZED VIEW sales_summary;

-- 查询重写优化
-- 优化器可能将查询重写为使用物化视图
SELECT product_id, SUM(amount) 
FROM sales 
GROUP BY product_id;
-- 可能被重写为:
SELECT product_id, total FROM sales_summary;

六、监控与诊断工具

1. 性能监控

sql

复制

下载

复制代码
-- 查看慢查询
SHOW VARIABLES LIKE 'slow_query_log%';
SHOW VARIABLES LIKE 'long_query_time';

-- 查看当前执行查询
SHOW FULL PROCESSLIST;

-- 查看锁信息
SHOW ENGINE INNODB STATUS\G
-- 查看LOCK WAIT部分

-- 查看IO统计
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';

2. 性能诊断

sql

复制

下载

复制代码
-- 使用Performance Schema
-- 查看等待事件
SELECT * FROM performance_schema.events_waits_current;

-- 查看语句统计
SELECT * FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC LIMIT 10;

-- 使用sys schema(MySQL 5.7+)
-- 查看冗余索引
SELECT * FROM sys.schema_redundant_indexes;

-- 查看未使用索引
SELECT * FROM sys.schema_unused_indexes;

七、最佳实践总结

优化要点:

  1. 理解执行计划:使用EXPLAIN分析查询

  2. 合理设计索引:遵循最左前缀原则

  3. 避免全表扫描:为WHERE条件建立索引

  4. 减少数据传输:避免SELECT *,使用LIMIT

  5. 优化连接查询:小表驱动大表,使用索引

  6. 合理使用事务:避免长事务,及时提交

  7. 监控慢查询:定期分析优化慢SQL

📊 性能指标参考:

  • 响应时间:简单查询<10ms,复杂查询<100ms

  • QPS:根据业务需求设定目标

  • 连接数:避免超过max_connections的70%

  • 缓冲池命中率:>95%

  • 索引命中率:>95%

记住:SQL优化是一个系统工程,需要结合业务特点、数据量、硬件资源等多方面因素综合考虑。最好的优化往往发生在设计阶段,合理的数据库设计和索引策略比后期的调优更重要。

相关推荐
素玥20 小时前
实训5 python连接mysql数据库
数据库·python·mysql
jnrjian20 小时前
text index 查看index column index定义 index 刷新频率 index视图
数据库·oracle
瀚高PG实验室21 小时前
审计策略修改
网络·数据库·瀚高数据库
言慢行善21 小时前
sqlserver模糊查询问题
java·数据库·sqlserver
韶博雅21 小时前
emcc24ai
开发语言·数据库·python
有想法的py工程师21 小时前
PostgreSQL 分区表排序优化:Append Sort 优化为 Merge Append
大数据·数据库·postgresql
迷枫7121 天前
达梦数据库的体系架构
数据库·oracle·架构
夜晚打字声1 天前
9(九)Jmeter如何连接数据库
数据库·jmeter·oracle
Chasing__Dreams1 天前
Mysql--基础知识点--95--为什么避免使用长事务
数据库·mysql
NineData1 天前
NineData 智能数据管理平台新功能发布|2026 年 3 月
数据库·oracle·架构·dba·ninedata·数据复制·数据迁移工具