欢迎光临
我们一直在努力

【大白话说Java面试题 第96题】【Mysql篇】第26题:日常工作中你是怎么优化 SQL 的?

📌 PDF:大白话说Java面试题 — 03-Mysql篇

第26题:日常工作中你是怎么优化 SQL 的

📚 回答:

  • 核心考点: 大厂面试要求系统性地阐述SQL优化的完整流程(发现问题→分析原因→制定方案→验证效果),掌握常用优化手段,并能结合实际案例说明。面试官常追问:“慢查询怎么定位?”、“EXPLAIN主要看哪些字段?”、“索引建了为什么查询还是慢?”

1. SQL优化完整流程

核心方法论:定位 → 分析 → 优化 → 验证

慢查询日志/业务反馈 → EXPLAIN分析执行计划 → 定位瓶颈 → 制定优化方案 → 上线验证 → 持续监控

2. 第一步:定位慢查询

2.1 开启慢查询日志

— 查看慢查询配置
SHOW VARIABLES LIKE 'slow_query_log%';
SHOW VARIABLES LIKE 'long_query_time';

— 开启慢查询日志(生产环境需谨慎)
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 1; — 超过1秒记录
SET GLOBAL log_queries_not_using_indexes = ON; — 记录未走索引的查询

— 慢查询日志文件位置
SHOW VARIABLES LIKE 'slow_query_log_file';

2.2 分析慢查询日志

# 使用mysqldumpslow工具分析
mysqldumpslow -s t -t 10 /var/lib/mysql/slow.log

# 输出:按查询时间排序,取前10条最慢的SQL

2.3 性能监控指标

指标命令说明
慢查询数量 SHOW STATUS LIKE 'Slow_queries' 累计慢查询数
查询缓存命中率 SHOW STATUS LIKE 'Qcache%' 缓存命中率低需优化
临时表使用 SHOW STATUS LIKE 'Created_tmp%' 大量临时表需优化
索引使用情况 SHOW STATUS LIKE 'Handler_read%' 判断索引效率
3. 第二步:EXPLAIN分析执行计划

3.1 重点关注的字段

字段好差说明
type const/ref/range ALL/index 访问类型,至少range
key 使用了索引 NULL 实际使用的索引
rows 大(接近表总行数) 估算扫描行数
filtered 高(>50%) 低(<10%) 过滤比例
Extra Using index Using filesort/Using temporary 额外信息

3.2 典型问题识别

— 问题1:全表扫描
EXPLAIN SELECT * FROM users WHERE name = '张三';
— type=ALL, key=NULL → 需要建索引

— 问题2:文件排序
EXPLAIN SELECT * FROM users ORDER BY create_time;
— Extra=Using filesort → 需要为create_time建索引

— 问题3:临时表
EXPLAIN SELECT DISTINCT age FROM users GROUP BY age;
— Extra=Using temporary → 优化GROUP BY/DISTINCT

— 问题4:索引失效
EXPLAIN SELECT * FROM users WHERE YEAR(birthday)=1990;
— type=ALL, key=NULL → 函数导致索引失效

4. 第三步:常见优化手段

4.1 索引优化(最高优先级)

优化手段示例效果
添加索引 CREATE INDEX idx_name ON users(name) 全表扫描→索引查找
覆盖索引 CREATE INDEX idx_covering ON users(name, age) 避免回表
联合索引顺序 (user_id, create_time)等值在前,范围在后 索引最大化利用
删除冗余索引 idx_name和idx_name_age保留后者 减少写开销
前缀索引 CREATE INDEX idx_content ON articles(content(20)) 减少索引大小

实战案例:

— 原SQL:查询某用户最近10条订单
SELECT * FROM orders WHERE user_id = 123 ORDER BY create_time DESC LIMIT 10;

— 原索引:无 或 只有(user_id)
— 问题:file sort

— 优化:创建联合索引(user_id, create_time)
CREATE INDEX idx_user_time ON orders(user_id, create_time);

— 效果:type=ref, Extra=Using index condition(无需filesort)

4.2 查询语句优化

优化手段低效写法高效写法提升原因
**避免SELECT *** SELECT * FROM users SELECT id,name FROM users 减少网络传输,可能用覆盖索引
深分页优化 LIMIT 100000,10 主键范围查询或覆盖索引+JOIN 避免扫描大量无用数据
批量操作 逐条INSERT INSERT INTO t VALUES (…), (…) 减少网络往返
避免函数 WHERE YEAR(date)=2024 WHERE date BETWEEN '2024-01-01' AND '2024-12-31' 索引生效
避免隐式转换 WHERE phone=13800138000 WHERE phone='13800138000' 索引生效
用UNION ALL代替UNION UNION(去重) UNION ALL(不去重) 避免去重开销
用EXISTS代替IN(子查询数据量大时) WHERE id IN (SELECT …) WHERE EXISTS (SELECT 1 …) 减少结果集

深分页优化详解:

— 低效:扫描100010行,丢弃100000行
SELECT * FROM orders ORDER BY id LIMIT 100000, 10;

— 方案1:主键范围查询(id连续)
SELECT * FROM orders WHERE id > 100000 ORDER BY id LIMIT 10;

— 方案2:覆盖索引+JOIN(通用)
SELECT o.* FROM orders o
INNER JOIN (
SELECT id FROM orders ORDER BY id LIMIT 100000, 10
) tmp ON o.id = tmp.id;

4.3 表结构优化

优化手段说明示例
拆分大字段 将不常查询的TEXT/BLOB分到扩展表 用户主表+用户详情表
分区表 按时间分区,便于删除和查询历史数据 PARTITION BY RANGE (YEAR(create_time))
数据类型优化 用INT代替VARCHAR、用NOT NULL status TINYINT代替status VARCHAR(20)
合理使用冗余 减少JOIN查询 订单表冗余商品名称

4.4 系统配置优化

参数推荐值作用
innodb_buffer_pool_size 物理内存70-80% 缓存数据和索引
innodb_log_file_size 1-2GB Redo日志大小
innodb_flush_log_at_trx_commit 1(强一致)/2(高性能) 刷盘策略
max_connections 500-1000 最大连接数
query_cache_size 0(MySQL 8.0已移除) MySQL 5.7可禁用

4.5 应用层优化

优化手段说明示例
缓存 Redis缓存热点数据 用户会话、商品详情
读写分离 主库写,从库读 报表查询走从库
异步处理 消息队列削峰 订单完成后发积分
连接池 HikariCP/Druid 减少连接创建开销
5. 实战案例分析

案例1:订单列表查询优化

原始SQL:

SELECT * FROM orders
WHERE user_id = 123
ORDER BY create_time DESC
LIMIT 10;

问题定位:

  • 无索引,type=ALL,全表扫描100万行
  • Extra=Using filesort,需要额外排序

优化方案:

— 1. 创建联合索引
CREATE INDEX idx_user_time ON orders(user_id, create_time);

— 2. 修改查询(只查必要字段)
SELECT id, order_no, amount, create_time FROM orders
WHERE user_id = 123
ORDER BY create_time DESC
LIMIT 10;

效果:

  • 扫描行数:100万 → 86
  • 查询时间:1.2秒 → 0.01秒
  • Extra:Using index condition(无filesort)

案例2:报表统计优化

原始SQL:

SELECT DATE(create_time) AS dt, COUNT(*)
FROM orders
WHERE create_time BETWEEN '2024-01-01' AND '2024-12-31'
GROUP BY DATE(create_time);

问题定位:

  • WHERE条件没有使用索引(函数处理create_time)
  • 全表扫描

优化方案:

— 1. 改写WHERE条件,使用范围查询
SELECT DATE(create_time) AS dt, COUNT(*)
FROM orders
WHERE create_time >= '2024-01-01' AND create_time < '2025-01-01'
GROUP BY DATE(create_time);

— 2. 添加覆盖索引
CREATE INDEX idx_time ON orders(create_time);

— 3. 考虑使用汇总表(每日提前统计)
CREATE TABLE order_daily_stats (
stat_date DATE PRIMARY KEY,
order_count INT
);

效果:

  • 扫描行数:500万 → 365(索引扫描)
  • 查询时间:8秒 → 0.2秒
6. 优化checklist(面试速记)
类别检查项命令/方法
慢查询 是否开启慢查询日志 slow_query_log=ON
执行计划 type是否为ALL/index EXPLAIN
索引 是否使用覆盖索引 Extra=Using index
索引 是否有冗余索引 pt-duplicate-key-checker
查询 是否SELECT * 改为具体字段
查询 深分页是否优化 避免大offset
查询 是否使用函数/隐式转换 检查WHERE条件
表结构 字段类型是否合理 INT vs VARCHAR
表结构 是否有分区需求 大表按时间分区
配置 innodb_buffer_pool_size是否足够 物理内存70-80%
7. 面试官追问与高分回答

Q1:索引建了,查询还是慢,可能的原因?

A:

  • 索引失效:函数、隐式转换、不遵循最左前缀
  • 区分度低:优化器选择全表扫描
  • 统计信息过期:执行ANALYZE TABLE
  • 需要回表:查询列不在索引中,尝试覆盖索引
  • 数据倾斜:某个值占比过高

Q2:如何验证优化效果?

A:

  • 执行EXPLAIN对比优化前后(type、rows、Extra)
  • 执行SHOW PROFILE查看详细耗时
  • 压测对比QPS和RT
  • 上线后观察慢查询日志变化
  • Q3:联合索引顺序怎么确定?

    A:

  • 等值查询列放前面,范围查询列放后面
  • 高区分度列放前面
  • 频繁查询列放前面
  • 考虑索引复用((a,b)可覆盖(a))
  • Q4:表数据量特别大(亿级),怎么优化?

    A:

    • 分区表(按时间、按哈希)
    • 分库分表(ShardingSphere、MyCAT)
    • 冷热数据分离(历史数据归档)
    • 使用列式存储(ClickHouse)做分析查询
    • 读写分离 + 缓存

    Q5:优化过程中最常用的工具?

    A:

    • EXPLAIN:分析执行计划
    • 慢查询日志 + mysqldumpslow:定位慢SQL
    • SHOW PROFILE:查看各阶段耗时
    • pt-query-digest:分析慢查询日志
    • SHOW ENGINE INNODB STATUS:查看锁信息
    • performance_schema:深度性能分析

    Q6:优化SQL的原则是什么?

    A:

  • 减少扫描行数(用索引、减少SELECT字段)
  • 减少回表次数(覆盖索引)
  • 减少排序和临时表(用索引排序)
  • 减少网络传输(批量操作、避免SELECT *)

  • 💡 面试官想要的满分总结:

    "SQL优化是一个系统性工作,我的优化流程是:定位 → 分析 → 优化 → 验证。

    第一步:定位慢查询

    • 开启慢查询日志(long_query_time=1)
    • 使用mysqldumpslow或pt-query-digest分析

    第二步:EXPLAIN分析执行计划

    • 重点关注:type(至少range)、key(不为NULL)、rows(小)、Extra(无filesort/temporary)
    • 识别全表扫描、索引失效、文件排序等问题

    第三步:制定优化方案

    • 索引优化:添加/优化索引、覆盖索引、联合索引顺序(等值在前,范围在后)
    • 查询优化:避免SELECT *、深分页优化、批量操作、避免函数/隐式转换
    • 表结构优化:拆分大字段、分区表、合理数据类型
    • 配置优化:innodb_buffer_pool_size(70-80%内存)
    • 应用层:缓存、读写分离

    第四步:验证效果

    • 重新EXPLAIN对比
    • 压测验证QPS/RT
    • 上线后监控慢查询日志

    一句话:SQL优化的本质是减少扫描行数和减少回表次数,核心工具是EXPLAIN,核心手段是索引优化。"


    觉得对您有帮助,麻烦点点关注啦,您的关注是我创作的最大动力~ 🎯

    赞(0)
    未经允许不得转载:171主机测评 » 【大白话说Java面试题 第96题】【Mysql篇】第26题:日常工作中你是怎么优化 SQL 的?
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址