SQL 存储过程性能优化方法

1次阅读

select * 在存储过程中严重伤性能,因其强制读取传输全部字段、加剧i/o与网络开销,易致执行计划缓存失效及参数嗅探错配,且join顺序错误、事务过大、未显式提交均会放大问题。

SQL 存储过程性能优化方法

为什么 SELECT * 在存储过程中特别伤性能

因为存储过程通常被高频调用,而 SELECT * 会强制数据库读取、传输、解析整行所有字段,哪怕只用其中一两个。尤其当表里有 TEXTjson 或大 BLOB 字段时,I/O 和网络开销会指数级上升。

  • 明确列出需要的字段,比如把 SELECT * 改成 SELECT id, name, status
  • 如果下游应用其实只查 idname,但表里还有 3 个 2MB 的 JSON 字段,不改写法等于每次都在搬石头过河
  • sql Server 中,SELECT * 还可能让执行计划缓存失效——列顺序或数量一变,旧计划就作废,触发重复编译

参数嗅探(Parameter Sniffing)导致执行计划“错配”

SQL Server 默认会基于首次传入的参数值生成并缓存执行计划。如果第一次查的是热门用户(返回 10 行),生成了索引查找计划;第二次查冷门用户(返回 50 万行),仍复用该计划,就会慢得离谱。

  • 临时方案:在存储过程里加 OPTION (RECOMPILE),比如 SELECT ... WHERE user_id = @uid OPTION (RECOMPILE)
  • 更稳的做法:用局部变量“断开”参数嗅探,如 DECLARE @local_uid int = @uid; SELECT ... WHERE user_id = @local_uid
  • 注意:mysqlpostgresql 没这问题,但 SQL Server 和 oracle 用户得绷紧这根弦

JOIN 顺序和驱动表选错,索引全白建

存储过程中多表 JOIN 如果没控制好驱动表(即外层循环表),优化器可能选错执行路径,比如本该用小表驱动大表,结果拿大表做外层,导致嵌套循环次数爆炸。

  • 先看 WHERE 条件中哪个表的过滤性最强(比如 status = 'active' 筛掉 95% 行),让它当驱动表
  • 确保驱动表的 JOIN 字段上有索引,且类型一致——INTVARCHAR 会导致隐式转换,索引直接失效
  • EXPLAIN(MySQL)或 SET STATISTICS xml ON(SQL Server)看实际执行计划,别信“应该走索引”这种直觉

事务范围过大,锁住不该锁的资源

存储过程里把一无关操作包进一个事务,比如查数据 + 写日志 + 调外部 API + 更新主表,中间任一环节卡住,整个事务就挂着,阻塞其他读写。

  • 只把真正需要原子性的语句放进 BEGIN TRANSACTION,比如“扣库存 + 记订单”必须一起成功或失败
  • 日志记录、通知类操作尽量挪到事务外,或用异步队列解耦
  • 避免在事务里做 WAITFOR DELAYSLEEP() 或调远程 http 接口——这些不是数据库该干的事

最常被忽略的一点:存储过程里没显式 COMMITROLLBACK,靠客户端自动提交,一旦连接异常中断,事务可能长期挂起,锁住关键表。写完务必检查每个分支是否都覆盖了事务终点。

text=ZqhQzanResources