SQL 高级 SQL 调试与分析方法

7次阅读

关键指标是rows/estimated_rows、type/node type及using temporary/filesort;rows远大于返回行数需更新统计信息,type为all/index需检查索引,函数条件需改写或建函数索引,join应让过滤后小表驱动大表,query_time与lock_time差异反映计算/i/o或锁瓶颈。

SQL 高级 SQL 调试与分析方法

EXPLAIN 执行计划怎么看关键指标

EXPLAIN 不是“看看有没有用上索引”就完事。真正要盯住的是 rowsmysql)或 estimated_rowspostgresql)、type(MySQL)或 Node Type(PG),以及是否出现 Using temporaryUsing filesort

  • rows 值远大于实际返回行数?说明统计信息过期,需执行 ANALYZE table(MySQL)或 VACUUM ANALYZE(PG)
  • typeALLindex?基本等于全表/全索引扫描,优先检查 WHERE 条件字段是否有有效索引
  • 出现 Using temporary?GROUP BY 或 DISTINCT 涉及非索引字段,或联表顺序导致中间结果过大

示例中看到 rows=124800 但只查 10 条,大概率是驱动表选错,不是加索引能解决的。

WHERE 条件里函数导致索引失效的典型场景

WHERE YEAR(created_at) = 2023WHERE UPPER(name) = 'ABC',哪怕 created_atname 都建了索引,也基本白搭。

  • MySQL 8.0+ 支持函数索引,但必须显式创建:CREATE INDEX idx_created_year ON t ((YEAR(created_at)))
  • 更稳妥做法是改写条件:用 created_at >= '2023-01-01' AND created_at
  • PostgreSQL 对函数表达式更友好,但 TO_CHAR(created_at, 'YYYY') 依然无法走普通 B-tree 索引,得配 functional index

别信“数据库会自动优化”,它不会把函数逆向推成范围条件。

JOIN 顺序和驱动表选择怎么影响性能

MySQL 的 STRAIGHT_JOIN、PostgreSQL 的 ENABLE_SEQSCAN = off 都是事后补救。真正该做的是让优化器自己选对驱动表。

  • 小表驱动大表仍是基本原则,但“小”指过滤后结果集小,不是物理行数少
  • 在多表 JOIN 中,先被 WHERE 过滤的表,才更可能成为驱动表;没过滤条件的表容易被误选为驱动表
  • 如果发现 EXPLAIN 显示大表在前、小表在后,且 rows 巨大,可尝试用子查询提前收窄:select * FROM large_table JOIN (SELECT id FROM small_table WHERE status = 'done') AS s ON ...

别依赖 STRAIGHT_JOIN 锁死顺序,它会让后续数据分布变化时性能雪崩。

慢查询日志里 Query_timeLock_time 差异意味着什么

Query_time 长但 Lock_time 接近 0?说明瓶颈在计算或 I/O,不是锁争用。反过来,Lock_time 占比高,就得查阻塞源头。

  • MySQL 中 Lock_time 包含等待表锁、MDL 锁、行锁的时间;PostgreSQL 则记录在 pg_stat_activitywait_event 字段
  • 常见陷阱:误把主从延迟当成慢查询——从库 Query_time 高,其实是 relay log 回放积压,和 SQL 本身无关
  • 开启 log_queries_not_using_indexes 很有用,但别长期开着,它会漏掉“用了索引但效率极差”的查询(比如索引区分度低)

真实线上慢查,往往不是单条 SQL 写得烂,而是并发下锁等待被放大,或者统计信息偏差让执行计划彻底跑偏。

text=ZqhQzanResources