1. SQL查询优化核心思路解析
从事数据库开发十年来,我处理过上千个性能瓶颈案例,其中80%都与低效SQL查询有关。上周刚帮一个电商平台将订单查询从12秒优化到0.3秒,关键就在于重写了三个核心查询语句。SQL优化不是简单的加索引,而是需要系统化的思维框架。
1.1 查询执行原理深度剖析
数据库引擎处理SQL查询时,会经历解析→优化→执行三个阶段。在MySQL中通过EXPLAIN命令可以看到优化器生成的执行计划,以下是一个典型分析案例:
EXPLAIN SELECT o.order_id, c.name FROM orders o JOIN customers c ON o.customer_id = c.id WHERE o.create_time > '2023-01-01' ORDER BY o.amount DESC LIMIT 100;执行计划中需要特别关注:
type列:从最优到最差依次是 system > const > eq_ref > ref > range > index > ALLkey列:实际使用的索引rows列:预估扫描行数Extra列:Using filesort、Using temporary 都是危险信号
经验:当看到
type=ALL或rows超过1000时,这个查询就必须要优化了
1.2 索引设计的黄金法则
去年优化过一个物流系统,2000万条数据的查询从8秒降到80毫秒,靠的就是正确的索引策略:
- 最左前缀原则:联合索引
(a,b,c)只能用于a=?、a=? AND b=?等条件 - 选择性原则:优先为区分度高的列建索引(如手机号比性别更适合)
- 覆盖索引:查询列都包含在索引中时,无需回表
- 避免冗余:已有
(a,b)索引时,单独的a索引就是多余的
-- 错误示例:索引失效 SELECT * FROM users WHERE YEAR(create_time) = 2023; -- 正确写法:使用范围查询 SELECT * FROM users WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31';2. 实战优化技巧大全
2.1 语句重写艺术
在金融系统优化中,曾将月报表生成从45分钟缩短到3分钟,关键改写技巧包括:
用JOIN代替子查询
-- 低效写法 SELECT * FROM orders WHERE customer_id IN (SELECT id FROM customers WHERE vip=1); -- 高效写法 SELECT o.* FROM orders o JOIN customers c ON o.customer_id=c.id WHERE c.vip=1;**避免SELECT ***
只查询需要的列,特别是TEXT/BLOB字段分页优化
不要使用LIMIT 10000,20,改为:SELECT * FROM orders WHERE id > 10000 ORDER BY id LIMIT 20;
2.2 参数调优秘籍
MySQL配置文件中这些参数直接影响查询性能:
# 缓冲池大小(建议内存的70%-80%) innodb_buffer_pool_size = 8G # 日志文件大小(至少1G) innodb_log_file_size = 2G # 并发连接数 innodb_thread_concurrency = 16上周帮一个游戏公司调整后,并发查询吞吐量提升了3倍。关键是要用SHOW STATUS监控运行指标:
-- 查看索引命中率 SHOW STATUS LIKE 'Handler_read%'; -- 查看临时表使用情况 SHOW STATUS LIKE 'Created_tmp%';3. 高级优化方案
3.1 查询缓存陷阱
MySQL的查询缓存看似美好实则坑多:
- 任何表有更新就会使该表所有缓存失效
- 高并发下缓存争用反而降低性能
- 建议在
my.cnf中关闭:query_cache_type = 0 query_cache_size = 0
3.2 分区表实战
处理过1.2亿条日志数据的分区优化案例,按时间范围分区后查询速度提升20倍:
CREATE TABLE logs ( id BIGINT NOT NULL, log_time DATETIME NOT NULL, content TEXT, PRIMARY KEY (id, log_time) ) PARTITION BY RANGE (TO_DAYS(log_time)) ( PARTITION p202301 VALUES LESS THAN (TO_DAYS('2023-02-01')), PARTITION p202302 VALUES LESS THAN (TO_DAYS('2023-03-01')), PARTITION pmax VALUES LESS THAN MAXVALUE );注意:分区字段必须包含在主键中,且每个分区数据量建议不超过500万
4. 性能监控体系
4.1 慢查询日志配置
在my.cnf中开启慢查询监控:
slow_query_log = 1 slow_query_log_file = /var/log/mysql/mysql-slow.log long_query_time = 1 # 超过1秒的查询 log_queries_not_using_indexes = 1然后用pt-query-digest工具分析:
pt-query-digest /var/log/mysql/mysql-slow.log > slow_report.txt4.2 实时诊断技巧
遇到生产环境突发慢查询时,立即执行:
-- 查看当前运行中的查询 SHOW FULL PROCESSLIST; -- 查看表锁定情况 SHOW OPEN TABLES WHERE In_use > 0; -- 查看InnoDB状态(包含锁信息) SHOW ENGINE INNODB STATUS;去年双十一期间,就是用这些命令在3分钟内定位到一个未提交的事务阻塞了整个订单系统。
5. 避坑指南
- 隐式类型转换:
WHERE varchar_col=123会导致索引失效 - OR条件优化:
WHERE a=1 OR b=2可以改写成UNION ALL - 函数陷阱:
WHERE DATE(create_time)='2023-01-01'无法使用索引 - JOIN顺序:小表驱动大表,数据量小的表放在前面
有次排查一个每秒超时的查询,发现是因为开发在循环里执行SQL,改成批量插入后性能提升1000倍:
// 错误做法 for(Order order : orders) { jdbcTemplate.update("INSERT..."); } // 正确做法 jdbcTemplate.batchUpdate("INSERT...", batchArgs);优化永无止境,关键是要建立完整的监控-分析-优化闭环。我习惯每周五下午专门处理慢查询日志,持续优化才能保持系统健康。