当前位置: 首页 > news >正文

SQL优化案例分析:十个常见性能问题

纸上谈兵没意思,直接上案例。

这篇整理了十个我遇到过的SQL性能问题,每个都是真实场景,看看你踩过几个。


案例1:SELECT * 的代价

问题SQL

SELECT*FROMordersWHEREuser_id=123;

问题:orders表有30个字段,但业务只需要3个字段。

优化后

SELECTorder_id,amount,statusFROMordersWHEREuser_id=123;

效果:查询时间从120ms降到35ms。

原因

  • 减少了数据传输量
  • 可能用上覆盖索引,避免回表

案例2:隐式类型转换

问题SQL

SELECT*FROMusersWHEREphone=13800138000;

phone字段是varchar类型,传入数字。

EXPLAIN结果type: ALL,全表扫描。

优化后

SELECT*FROMusersWHEREphone='13800138000';

EXPLAIN结果type: ref,走索引。

原因:类型不匹配时,MySQL会把字符串转成数字比较,导致索引失效。


案例3:函数导致索引失效

问题SQL

SELECT*FROMordersWHEREDATE(create_time)='2024-01-15';

create_time上有索引,但没用上。

优化后

SELECT*FROMordersWHEREcreate_time>='2024-01-15 00:00:00'ANDcreate_time<'2024-01-16 00:00:00';

效果:从全表扫描变成范围扫描,快了100倍。

原因:对索引列使用函数,优化器无法使用索引。


案例4:深分页问题

问题SQL

SELECT*FROMordersORDERBYidLIMIT500000,20;

问题:要扫描50万+20行,然后丢掉前50万行。

优化方案一:用上一页的ID

SELECT*FROMordersWHEREid>500000ORDERBYidLIMIT20;

优化方案二:延迟关联

SELECTo.*FROMorders oINNERJOIN(SELECTidFROMordersORDERBYidLIMIT500000,20)tONo.id=t.id;

子查询只查主键,速度快。

效果:从5秒降到50毫秒。


案例5:OR条件优化

问题SQL

SELECT*FROMordersWHEREuser_id=123ORorder_no='ABC123';

user_id有索引,order_no也有索引,但MySQL只能用一个。

优化后

SELECT*FROMordersWHEREuser_id=123UNIONSELECT*FROMordersWHEREorder_no='ABC123';

效果:两个查询分别走各自的索引,然后合并。

注意:如果确定没有重复数据,用UNION ALL更快。


案例6:EXISTS vs IN

问题SQL

-- 查询有订单的用户SELECT*FROMusersWHEREidIN(SELECTuser_idFROMorders);

orders表很大,子查询返回大量数据。

优化后

SELECT*FROMusers uWHEREEXISTS(SELECT1FROMorders oWHEREo.user_id=u.id);

什么时候用什么

  • 子查询结果集小 → 用IN
  • 子查询结果集大,外层表小 → 用EXISTS
  • 现代MySQL优化器通常能自动转换,但复杂SQL还是要注意

案例7:JOIN顺序优化

问题SQL

SELECT*FROMorders oLEFTJOINusers uONo.user_id=u.idWHEREu.status=1;

orders表100万,users表10万。

问题:大表驱动小表,效率低。

优化后

SELECT*FROMusers uINNERJOINorders oONu.id=o.user_idWHEREu.status=1;

小表users先过滤,再关联大表orders。

原则

  • 小表驱动大表
  • 把过滤条件尽量放在驱动表
  • LEFT JOIN改成INNER JOIN(如果业务允许)

案例8:COUNT优化

问题SQL

SELECTCOUNT(*)FROMordersWHEREstatus=1;

orders表1000万,status=1的有800万,每次统计要扫描800万行。

优化方案

方案一:加索引

CREATEINDEXidx_statusONorders(status);-- 走索引扫描,但还是要扫描800万个索引项

方案二:汇总表

-- 创建汇总表CREATETABLEorder_stats(statusINTPRIMARYKEY,cntINT,updated_atDATETIME);-- 定时任务更新(或触发器)UPDATEorder_statsSETcnt=(SELECTCOUNT(*)FROMordersWHEREstatus=1),updated_at=NOW()WHEREstatus=1;-- 查询直接读汇总表SELECTcntFROMorder_statsWHEREstatus=1;

方案三:近似值

-- 如果不需要精确值,用EXPLAIN的rows估算EXPLAINSELECT*FROMordersWHEREstatus=1;-- rows字段就是估算值

案例9:ORDER BY优化

问题SQL

SELECT*FROMordersWHEREuser_id=123ORDERBYcreate_timeDESC;

有idx_user_id索引,但排序还是用了filesort。

EXPLAINExtra: Using filesort

优化后

CREATEINDEXidx_user_timeONorders(user_id,create_time);

EXPLAINExtra: Using index,不再filesort。

原因:联合索引里包含了排序字段,数据已经有序。


案例10:UPDATE优化

问题SQL

-- 批量更新状态UPDATEordersSETstatus=2WHEREstatus=1ANDcreate_time<'2024-01-01';

符合条件的有10万条,一次性更新锁表时间长。

优化后:分批更新

-- 每次更新1000条UPDATEordersSETstatus=2WHEREstatus=1ANDcreate_time<'2024-01-01'LIMIT1000;-- 循环执行直到影响行数为0

或者用存储过程:

DELIMITER//CREATEPROCEDUREbatch_update()BEGINDECLAREaffected_rowsINTDEFAULT1;WHILEaffected_rows>0DOUPDATEordersSETstatus=2WHEREstatus=1ANDcreate_time<'2024-01-01'LIMIT1000;SETaffected_rows=ROW_COUNT();-- 稍微等一下,让其他事务有机会执行DOSLEEP(0.1);ENDWHILE;END//DELIMITER;CALLbatch_update();

效果:避免长时间锁表,其他业务可以正常执行。


优化检查清单

每次写SQL前,过一遍这个清单:

□ 是否用了SELECT *?改成只查需要的列 □ 有没有隐式类型转换?字符串加引号 □ 索引列上有没有函数?改写成范围查询 □ 有没有深分页?用ID游标或延迟关联 □ OR条件能否改成UNION? □ IN子查询是否可以用EXISTS或JOIN替代? □ JOIN顺序对不对?小表驱动大表 □ ORDER BY能否利用索引? □ 大批量UPDATE/DELETE是否要分批? □ 最后:跑一遍EXPLAIN确认

总结

SQL优化说到底就几个原则:

  1. 减少扫描行数:用好索引
  2. 减少回表:覆盖索引
  3. 减少排序:索引里包含排序字段
  4. 减少锁冲突:批量操作分批执行
  5. 减少数据传输:只查需要的列

遇到慢SQL,先EXPLAIN看执行计划,找到问题再针对性优化。


有问题评论区聊。

http://www.jsqmd.com/news/343411/

相关文章:

  • emuelec核心模拟器设置:手把手教程优化启动项
  • 2026年主题酒店全息投影设备源头厂家有哪些? - 品牌企业推荐师(官方)
  • 企业维护场景下DDU批量清理NVIDIA驱动方案
  • 深入解析I2S协议工作原理:时序与信号同步机制
  • 2026年KTV回收厂家最新推荐:制冷设备回收、办公座椅回收、办公设备回收、大型卖场回收、工厂设备回收、工地二手空调采购选择指南 - 优质品牌商家
  • 精确控制STM32 I2C时序:寄存器级操作指南
  • Git-RSCLIP实战:快速搭建你的第一个图像文本相似度检测应用
  • SSD1306中文手册图解说明:快速掌握初始化流程
  • PLC与单片机RS485通信对接:实战案例
  • Vivado2025针对UltraScale+的功耗分析工具图解说明
  • Qwen3-ASR-0.6B环境部署:CSDN GPU实例7860端口Web服务一键配置实操
  • DeepSeek-OCR-2 GPU显存优化部署教程:A10服务器环境下24GB显存极限压测实录
  • YOLOv8与DeepSeek-OCR-2联合部署:文档元素检测识别系统
  • JLink驱动安装系统学习:兼容Win10/Win11烧录环境
  • 上位机是什么意思?手把手教你识别其功能边界
  • Whisper-large-v3实战教程:利用whisper-timestamps实现逐句时间戳对齐
  • SDXL 1.0绘图工坊实战案例:用‘Cyberpunk’预设3分钟生成动态海报
  • 开发者必备:用侠客行测试语音指令识别率的完整指南
  • Qwen3-ASR-1.7B一文详解:FP16量化对WER(词错误率)影响实测分析
  • IAR使用教程:外设寄存器调试的实战应用
  • STM32CubeMX点亮LED灯操作指南(初学者适用)
  • 使用qserialport实现串口数据实时绘图:项目应用
  • Python爬虫结合RMBG-2.0:自动采集并处理图片
  • 全面认识环境标志产品认证
  • Virtual Serial Port Driver权限配置与安全策略设置
  • 蜂鸣器在STM32报警系统中的实践应用
  • Screen to Gif新手避坑指南:常见问题全面讲解
  • LVGL图形界面开发教程:图表组件绘制深度剖析
  • React Native for OpenHarmony:贪吃蛇游戏的开发与跨平台适配实践
  • fastboot驱动在Qualcomm设备OTA升级中的角色一文说清