MySQL查询性能优化核心知识点总结

一、第一步:精准定位"待优化查询"

优化的前提是"找到问题查询",而非盲目优化。需结合日志与工具,筛选出"影响最大"的查询,优先处理高价值目标。

  1. 发现慢查询:日志与工具双管齐下

(1)日志定位:常规查询日志 vs 慢速查询日志

MySQL通过两种日志记录查询行为,核心关注慢速查询日志(聚焦低效查询):

| 日志类型 | 作用 | 配置与使用 |

| 常规查询日志 | 记录所有SQL请求(SELECT/INSERT/UPDATE等),用于审计或全量查询追踪| 配置:`general_log=ON`,日志路径`general_log_file=/var/log/mysql/general.log`;<br>注意:生产环境慎用(日志量大),仅临时排查时开启。 |

| 慢速查询日志 | 记录执行时间超过`long_query_time`(默认1秒)的查询,是优化核心依据 | 配置:`slow_query_log=ON`,`long_query_time=1`(可设为0.1秒捕捉更多慢查询),日志路径`slow_query_log_file=/var/log/mysql/slow.log`;<br>关键参数:`log_queries_not_using_indexes=ON`(记录未用索引的查询)。 |

(2)实时工具:快速捕捉活跃慢查询

  • SHOW PROCESSLIST`:查看当前活跃连接的SQL执行状态,识别"长期阻塞"或"耗时查询": SHOW PROCESSLIST\G

  • 关键字段:`Time`(执行时间,超过10秒需关注)、`State`(状态,如`Sending data`可能是慢查询)、`Info`(具体SQL)。

  • `sys.statement_analysis`:查看"规范化查询"的聚合统计(如执行次数、总耗时),定位高频查询: SELECT query, exec_count, total_latency FROM sys.statement_analysis ORDER BY total_latency DESC LIMIT 10;

  • 优势:将相似查询(如`WHERE id=1`和`WHERE id=2`)合并,避免重复统计。

  1. 选择优化对象:不是"最慢"而是"影响最大"

优化的核心原则:优先优化"执行频繁的查询",而非仅关注"单次最慢的查询"。

二、第二步:用EXPLAIN剖析"查询执行计划"

`EXPLAIN`是MySQL查询优化的"显微镜"------它不执行SQL,仅输出优化器的"执行计划",帮你判断查询是否用对索引、是否有全表扫描等问题。

  1. EXPLAIN基本用法

支持`SELECT`/`INSERT`/`UPDATE`/`DELETE`语句,语法简单:

-- 分析SELECT语句:EXPLAIN SELECT * FROM employees WHERE emp_no=10001\G

-- 分析UPDATE语句:EXPLAIN UPDATE salaries SET salary=50000 WHERE emp_no=10001\G

  1. EXPLAIN输出字段详解(核心必看)

`EXPLAIN`输出12个关键字段,每个字段都对应优化的突破口,重点关注标红部分:

| 字段名 | 含义与优化价值 |

|`id`| 查询语句的编号,复杂查询(子查询、JOIN)会有多行不同id,标识表的处理顺序。|

| `select_type` | 查询类型:<br>- `SIMPLE`:无子查询/UNION;<br>- `SUBQUERY`:子查询;<br>- `UNION`:UNION语句;<br>无需优化,仅用于理解查询结构。 |

| `table` | 该计划对应的表名,JOIN查询会有多行(对应不同表)。 |

| `type` | 索引/连接类型,最核心的性能指标,从优到差排序:<br>`const`(主键/唯一键匹配常量)>`eq_ref`(JOIN时主键匹配)>`ref`(非唯一索引匹配)>`range`(索引范围扫描)>`index`(全索引扫描)>`ALL`(全表扫描)。 |

| `possible_keys` | 优化器认为"可能有用"的索引,不代表实际使用。 |

| key` | 优化器"实际使用"的索引,若为`NULL`,表示未用索引(需优化)。 |

| `key_len` | 实际使用的索引长度(字节),越长表示使用的索引列越多(如联合索引用了前2列)。 |

| `ref` | 与索引对比的"参考值":<br>- `const`:常量(如`WHERE id=1`);<br>- `表名.列名`:JOIN时的关联列。 |

| `rows` | 优化器预估的"扫描行数",数值越小越好(全表扫描时等于表总行数)。 |

| `filtered` | 按WHERE条件筛选后的行百分比(%),越高表示筛选效果越好。 |

| `Extra` | 额外信息,高频优化点:<br>- `Using filesort`:需优化(用索引避免排序);<br>- `Using temporary`:需优化(用索引避免临时表);<br>- `Using index`:最优(覆盖索引,无需回表);<br>- `Using where`:需结合索引判断是否全表扫描。 |

三、第三步:索引优化------查询性能的"加速器"

索引是解决"慢查询"的核心手段,但需选对类型、正确创建,避免"无效索引"浪费资源。

  1. MySQL支持的索引类型

不同场景需用不同索引,避免一概而论:

| 索引类型 | 特点与适用场景 |

| 非唯一索引 | 默认类型,值可重复;适用于`WHERE`条件查询(如`WHERE dept_no='d001'`)。 |

| 唯一索引 | 值唯一(允许NULL);适用于`WHERE`条件+唯一性约束(如`WHERE email='a@xxx.com'`)。 |

| 主键索引 | 特殊的唯一索引,值非空;每个表仅1个,用于唯一标识行(如`emp_no`)。|

| 全文索引 | 针对字符串的全文搜索(如`MATCH(name) AGAINST('apple')`);不适用于精确匹配。 |

| 空间索引 | 针对空间数据类型(如`GEOMETRY`、`POINT`);适用于地理信息查询 |

  1. 索引的核心作用(3大场景)

索引不是"万能的",但能解决90%的慢查询,核心支持三类操作:

  1. 直接值匹配:精确查找(如`WHERE id=1`),通过索引直接定位行;

  2. 存在性检查:判断值是否存在(如`WHERE email='a@xxx.com'`),避免全表扫描;

  3. 范围扫描:查找区间值(如`WHERE salary BETWEEN 5000 AND 10000`),仅扫描索引区间。

  4. 索引的创建与删除

(1)创建索引

  • 给现有表加索引(`ALTER TABLE`或`CREATE INDEX`)。

  • 创建表时直接加索引。

(2)删除索引

-- 方法1:ALTER TABLE(适用于所有索引,包括主键)

ALTER TABLE employees DROP INDEX idx_emp_firstname;

-- 方法2:DROP INDEX(适用于非主键索引)

DROP INDEX idx_title_empno ON titles;

-- 删除主键(需先删主键索引,再重建)

ALTER TABLE dept DROP PRIMARY KEY, ADD PRIMARY KEY (dept_no);

(3)查看索引信息

用`SHOW INDEXES`查看表的所有索引:SHOW INDEXES FROM departments\G

关键字段:`Key_name`(索引名)、`Column_name`(索引列)、`Non_unique`(是否非唯一,0=唯一,1=非唯一)、`Cardinality`(索引基数,越大索引选择性越好)。

四、第四步:维护索引统计------让优化器"判断更准"

MySQL优化器依赖"索引统计信息"(如索引基数、数据分布)选择最优计划,若统计信息过时,可能导致"索引失效",需定期维护。

  1. 索引统计信息的自动更新

InnoDB默认自动维护统计信息,无需手动干预:

  • 触发条件:表中10%的行被修改后(如INSERT/UPDATE/DELETE);

  • 持久化存储:统计信息存在`mysql.innodb_index_stats`表,重启后不丢失;

  • 采样策略:默认采样20个数据页,可通过`innodb_stats_persistent_sample_pages`调整(越大越精准,但耗时更长)。

  1. 手动更新统计信息(`ANALYZE TABLE`)

当自动更新不及时(如批量导入数据后),需手动执行`ANALYZE TABLE`:

-- 分析单个表:ANALYZE TABLE salaries;

-- 分析多个表,不写入二进制日志(避免同步到从库):ANALYZE LOCAL TABLE employees, titles;

  • 作用:重新计算索引基数、数据分布,让优化器选择正确索引;

  • 适用场景:批量插入10万行后、表结构变更后。

  1. 索引维护进阶操作

(1)重建索引(优化碎片化)

索引长期使用后会产生碎片(如频繁删除导致空页),需重建优化:

-- 方法1:ALTER TABLE FORCE(重建全表索引,适用于InnoDB)

ALTER TABLE employees FORCE;

-- 方法2:OPTIMIZE TABLE(仅重建全文索引,需先设置参数)

SET innodb_optimize_fulltext_only = 1;

OPTIMIZE TABLE titles;

(2)用mysqlcheck工具批量维护

`mysqlcheck`是命令行工具,支持批量分析/优化表,比SQL更便捷:

分析employees库的salaries表

mysqlcheck -u root -p --analyze employees salaries

优化所有库的所有表(谨慎使用)

mysqlcheck --login-path=admin --optimize --all-databases

  • 核心选项:`--analyze`(执行ANALYZE TABLE)、`--optimize`(执行OPTIMIZE TABLE)、`--check`(检查表健康)。

(3)不可见索引(安全测试索引作用)

想测试"删除索引是否影响性能",但又怕删错?用"不可见索引"隐藏索引(优化器不使用,但DML仍更新):

-- 创建不可见索引:CREATE INDEX idx_emp_lastname ON employees (last_name) INVISIBLE;

-- 切换为可见:ALTER TABLE employees ALTER INDEX idx_emp_lastname VISIBLE;

  • 优势:无需删除重建,测试后若发现索引有用,直接设为可见即可。

(4)直方图(优化非索引列的查询)

对于非索引列,优化器无法知道数据分布(如`c_mktsegment`列的取值频率),此时创建"直方图"可提供额外信息:

-- 给customer表的c_mktsegment列创建直方图(1024个桶)

ANALYZE TABLE customer UPDATE HISTOGRAM ON c_mktsegment WITH 1024 BUCKETS;

  • 效果:案例中查询耗时从49.98秒降至6.35秒,因优化器更精准预估筛选后的行数。
相关推荐
IvorySQL11 小时前
PostgreSQL 分区表的 ALTER TABLE 语句执行机制解析
数据库·postgresql·开源
·云扬·11 小时前
MySQL 8.0 Redo Log 归档与禁用实战指南
android·数据库·mysql
IT邦德11 小时前
Oracle 26ai DataGuard 搭建(RAC到单机)
数据库·oracle
惊讶的猫12 小时前
redis分片集群
数据库·redis·缓存·分片集群·海量数据存储·高并发写
不爱缺氧i12 小时前
完全卸载MariaDB
数据库·mariadb
纤纡.12 小时前
Linux中SQL 从基础到进阶:五大分类详解与表结构操作(ALTER/DROP)全攻略
linux·数据库·sql
jiunian_cn12 小时前
【Redis】渐进式遍历
数据库·redis·缓存
橙露12 小时前
Spring Boot 核心原理:自动配置机制与自定义 Starter 开发
java·数据库·spring boot
冰暮流星12 小时前
sql语言之分组语句group by
java·数据库·sql
符哥200812 小时前
Ubuntu 常用指令集大全(附实操实例)
数据库·ubuntu·postgresql