SQL 性能分析
定位慢 SQL 的四个入口:执行频次 → 慢查询日志 → profile → explain 执行计划。
一、SQL 执行频率
-- 查看当前库各类语句的访问频次(Com 后 7 个下划线,_ 为单字符通配)
SHOW [SESSION | GLOBAL] STATUS LIKE 'Com_______';
重点看 Com_select、Com_insert、Com_update、Com_delete 的数值。
不写默认
SESSION(当前会话),GLOBAL表示全局。
二、慢查询日志
记录执行时间超过 long_query_time(默认 10 秒)的 SQL,用于定位慢查询。
-- 是否开启
SHOW VARIABLES LIKE 'slow_query_log';
-- 开启(需 SUPER 权限;该方式重启失效,永久生效需改配置文件后重启)
SET GLOBAL slow_query_log = 1;
-- 超时阈值(秒)
SHOW VARIABLES LIKE 'long_query_time';
SET GLOBAL long_query_time = 2;
-- 日志文件位置
SHOW VARIABLES LIKE 'slow_query_log_file';
慢查询日志的完整参数(扫描行数阈值、是否记录管理语句 / 未走索引的语句)见 查询日志与慢查询日志。
三、profile
查看单条 SQL 各阶段(解析、执行、发送数据等)的耗时,只对当前会话有效。
SELECT @@have_profiling; -- 当前 MySQL 是否支持 profile
SELECT @@profiling; -- 是否已开启
SET profiling = 1; -- 开启
-- 执行若干 SQL 后
SHOW PROFILES; -- 列出各条 SQL 的 Query_ID 与总耗时
SHOW PROFILE FOR QUERY 1; -- 查看指定 Query_ID 的各阶段耗时
四、explain 执行计划
EXPLAIN SELECT ...; -- 等价写法:DESC SELECT ...
在 SQL 前加 explain 即可查看(不会真正执行查询),用于判断索引是否命中、扫描了多少行。
| 字段 | 含义 |
|---|---|
id | select 查询的序列号。id 相同则从上往下执行;id 不同则值越大越先执行 |
select_type | SELECT 的类型,常见:SIMPLE(无连接/子查询)、PRIMARY(外层主查询)、SUBQUERY(where 后的子查询)、MATERIALIZED(物化子查询)、UNION |
type | 连接类型,性能由好到差:NULL > system > const > eq_ref > ref > range > index > all |
possible_keys | 可能用到的索引(一个或多个) |
key | 实际使用的索引;为 NULL 表示未使用索引 |
key_len | 使用索引的字节数,越短越好(联合索引中可判断实际用到了几列) |
ref | 索引列的比较对象(const / 某列 / 函数) |
rows | 预估扫描的行数,越小越好 |
filtered | 返回行数占扫描行数的百分比 |
Extra | 额外信息,如 Using index、Using where、Using index condition、Using join buffer |
-- 主键等值查询:type=const,key=PRIMARY
explain select * from tb_user where id = 1;
-- 无表查询:type=NULL,Extra=No tables used
explain select 'A';
type 关注点:
const/ref/range说明用上了索引,index(全索引扫描)、all(全表扫描)说明效率差。
Extra 常见取值
| 取值 | 含义 |
|---|---|
Using index | 使用了索引,且需要的数据都在索引列中,不需要回表 |
Using index condition | 使用了索引,但需要回表查询数据 |
Using where | 需要回表后用 where 过滤 |
Using join buffer (hash join) | 被驱动表没有用到索引,用了 join buffer |
No tables used | 没有涉及表 |