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

相关推荐
TG:@yunlaoda360 云老大2 小时前
华为云国际站代理商TaurusDB的读写分离可以应用于哪些场景?
服务器·网络·数据库·华为云
youngqqcn2 小时前
SQL中联表查询深入分析
数据库·sql
TG:@yunlaoda360 云老大2 小时前
华为云国际站代理商CSBS主要有什么作用呢?
运维·服务器·数据库·华为云
Java&Develop2 小时前
PL/SQL Developer可视化修改数据
数据库·sql
星哥说事3 小时前
SSL/TLS 证书管理,文件与数据库加密技术
数据库·网络协议·ssl
东东的脑洞3 小时前
【面试突击】深度解析:Redis 与数据库(DB)的一致性方案
数据库·redis·面试
武昌库里写JAVA3 小时前
vue+iview+node+express实现文件上传,显示上传进度条,实时计算上传速度
java·vue.js·spring boot·后端·sql
爱学大树锯3 小时前
【快刷面试】-数据库-多线程在数据库中的应用
数据库·面试·多线程
川贝枇杷膏cbppg3 小时前
oracle的trace,alert,incident,cdump,hm都是干嘛的
数据库·oracle