news 2026/9/8 6:14:25

MySQL索引调优实战:从B+树原理到EXPLAIN与慢查询优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL索引调优实战:从B+树原理到EXPLAIN与慢查询优化

MySQL 索引调优不是靠背几条规范就能掌握的技能,它要求你同时理解索引的底层存储结构、优化器的选择逻辑,以及具体 SQL 的真实执行路径。这篇文章直接把“调优”和“面试”两条线合并起来讲:先建立索引体系的完整认知,再用可复现的建表、EXPLAIN、慢查询流程走一遍优化闭环,最后落到高频面试题的回答框架上。

如果你正在准备后端面试,或者手上有一条 SQL 越跑越慢、EXPLAIN 又看不懂,这篇内容可以对照着用。文章默认你已经掌握基础 SQL,所有操作示例基于 MySQL 8.0 编写,其中绝大部分语句在 MySQL 5.7 同样适用,个别差异点我会单独标注出来。

1. 核心能力速览:这篇索引调优文章覆盖什么

能力项说明
适用数据库MySQL 5.7 / 8.0,InnoDB 存储引擎
核心内容索引类型、B+ 树原理、EXPLAIN 执行计划、索引失效场景、慢查询分析
实操工具mysql 命令行、EXPLAIN、慢查询日志、performance_schema、Docker
面试覆盖回表、覆盖索引、最左前缀原则、索引下推、深分页、大表建索引
前置要求熟悉基础 SQL,能连接本地 MySQL
适合读者后端开发、DBA、准备数据库面试的工程师

从使用场景上看,这套内容既能支撑你完成一次线上慢 SQL 的排查,也能帮你把“索引相关面试题”串成体系。与其零散地记“索引失效条件”,不如先理解优化器怎么选索引,这样不管 SQL 怎么写,你都能判断它会不会走索引。

2. MySQL 索引类型与适用场景

2.1 为什么 InnoDB 索引选择 B+ 树

InnoDB 的索引底层是 B+ 树,这个结论几乎所有文章都会写,但面试真正考察的是“为什么”。

B+ 树和普通 B 树最大的区别在于:B+ 树的非叶子节点只存索引键值,不存数据行,一个 16KB 的页面能存放更多索引条目,整棵树的高度通常只有 2 到 4 层。对一次查询而言,树高基本决定了磁盘 IO 次数,树越矮,随机 IO 越少,查询越快。

同时,B+ 树的叶子节点通过双向链表串联,天然支持高效的范围扫描。数据库里WHERE id BETWEEN 100 AND 200、按索引排序这类操作非常频繁,B+ 树的这个特性正好匹配。红黑树虽然也是平衡树,但树高明显更高,节点存储密度低,磁盘 IO 次数更多,不适合作为磁盘存储结构的底层实现。

2.2 InnoDB 索引分类

MySQL 索引可以按多个维度分类,先用一张表理清基本概念:

分类维度类型说明
数据结构B+ 树索引InnoDB 默认索引结构
数据结构Hash 索引仅 Memory 引擎默认支持,InnoDB 的 Adaptive Hash Index 是自动行为
聚簇属性聚簇索引主键索引,叶子节点存放整行数据
聚簇属性二级索引非主键索引,叶子节点存放主键值
字段数量单列索引 / 联合索引联合索引涉及最左前缀原则
唯一性普通索引 / 唯一索引唯一索引约束字段值不重复
特殊场景全文索引适用于全文检索,InnoDB 支持但使用限制较多
特殊场景前缀索引只对字符串前 N 个字符建索引

大多数业务场景下,我们使用最多的是 B+ 树下的聚簇索引和二级索引。聚簇索引以主键为键值,二级索引的叶子节点存的是主键值,这就引出了下图这条完整链路:通过二级索引查找数据时,先拿到主键,再到聚簇索引回表取整行。

2.3 回表与覆盖索引

假设订单表orders有字段iduser_idorder_noamount,主键是id。我们给order_no建了一个普通索引,执行:

SELECT id, user_id, amount FROM orders WHERE order_no = 'NO20260001';

这条 SQL 的查询条件走二级索引,但二级索引的叶子节点只保存order_no和主键id,不包含user_idamount,所以 MySQL 需要先用二级索引找到主键id,再通过id去聚簇索引回表读取完整数据行,这个过程就叫回表。

回表不是错误,它是二级索引的正常工作机制。优化思路是尽可能避免回表:如果查询列本身就在索引中,那么索引扫描完成后直接返回结果,不需要再查聚簇索引,这种场景叫做覆盖索引。上面这条 SQL 如果改成只查idorder_no,二级索引就能直接覆盖,Extra 字段会显示Using index

3. 本地测试环境准备

调优不能只在脑内推演,建议准备一个本地 MySQL 实例,用小数据量跑通 EXPLAIN 和慢查询流程。下面给出一套通用准备流程,所有命令都需要根据你本机实际情况调整端口和密码。

3.1 通过 Docker 快速启动 MySQL 8.0

如果你本机没有 MySQL,用 Docker 启动是最快的隔离方案:

docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=your_password \ mysql:8.0

启动后等待几十秒,容器进入 healthy 状态就能连接。如果你不习惯用容器,也可以直接用 MySQL 官方安装包或系统包管理器安装,核心思路是一样的:拿到一个可执行 SQL 的数据库环境。

检查容器状态:

docker ps | grep mysql8

连接数据库:

mysql -h127.0.0.1 -P3306 -uroot -p

3.2 准备一张订单表和测试数据

为了演示索引效果,这里准备一张结构简单、但能覆盖大多数索引场景的订单表:

CREATE DATABASE IF NOT EXISTS demo_db DEFAULT CHARSET utf8mb4; USE demo_db; CREATE TABLE orders ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, order_no VARCHAR(64) NOT NULL, status TINYINT NOT NULL DEFAULT 0, amount DECIMAL(12, 2) NOT NULL DEFAULT 0, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_user_created (user_id, created_at), UNIQUE KEY uk_order_no (order_no) ) ENGINE=InnoDB;

如果想观察真实的数据量和执行时间,可以用存储过程插入几十万行测试数据。注意,演示环境插入的数据分布是否均匀,会直接影响优化器的索引选择判断。

DELIMITER $$ CREATE PROCEDURE insert_orders() BEGIN DECLARE i INT DEFAULT 1; WHILE i <= 200000 DO INSERT INTO orders (user_id, order_no, status, amount, created_at) VALUES (i % 5000, CONCAT('NO', LPAD(i, 10, '0')), i % 4, i % 10000, NOW() - INTERVAL i MINUTE); SET i = i + 1; END WHILE; END$$ DELIMITER ; CALL insert_orders();

这个表同时包含普通联合索引和唯一索引,足够演示最左前缀、回表、覆盖索引等核心概念。

4. 索引创建与基本操作

4.1 查看已有索引

SHOW INDEX FROM orders;

执行结果里会列出索引名称、索引字段、唯一性、基数等关键信息。Cardinality表示索引的区分度估算值,数值越高,说明重复值越少,索引选择性越好。

4.2 创建相关索引

创建普通索引:

CREATE INDEX idx_status ON orders(status);

创建唯一索引:

CREATE UNIQUE INDEX uk_user_order ON orders(user_id, order_no);

用 ALTER TABLE 方式也可以:

ALTER TABLE orders ADD INDEX idx_amount (amount);

索引不是越多越好。写操作会同时维护所有索引,索引过多会明显拖慢 INSERT、UPDATE、DELETE 的性能,同时占用更多磁盘空间。给一个字段建索引之前,先确认它是否真的处于高频查询条件或高频排序字段中。

4.3 删除索引

DROP INDEX idx_status ON orders;

删除长期不用的索引属于常规清理操作。判断索引是否冗余,一是看有没有完全被别的联合索引覆盖的前缀字段,二是看真实业务查询里是否长期没有被优化器选中的日志记录。

4.4 联合索引的字段顺序

联合索引的字段顺序非常关键,它决定这个索引能覆盖哪些查询。(user_id, created_at)这个索引可以支撑:

WHERE user_id = 123 AND created_at > '2026-01-01'

但它不能直接支撑只查询created_at的 SQL,因为created_at不是联合索引的最左前缀字段。后文面试部分会详细展开最左前缀原则,这里先记住:联合索引的字段顺序应该按照查询条件的频率和区分度来设计,而不是随意拼接。

5. 用 EXPLAIN 看索引是否真的生效

创建索引之后,验证生效的方法是看执行计划。EXPLAIN 能告诉我们优化器最终选择的访问路径,这是索引调优的第一步,也是最重要的一步。

5.1 最基础的调优闭环

先不加任何条件地查看一条查询的执行计划:

EXPLAIN SELECT * FROM orders WHERE order_no = 'NO2026000001';

因为order_no上有唯一索引uk_order_no,执行计划里type应该是constrefkey字段显示uk_order_no,这说明走了唯一索引,效率很高。

为了看到对比,我们可以模拟一个没有索引的查询字段。给status建索引之前,先执行:

EXPLAIN SELECT * FROM orders WHERE status = 1;

此时大概率看到type = ALL,说明优化器选择了全表扫描。接着创建索引:

CREATE INDEX idx_status ON orders(status); EXPLAIN SELECT * FROM orders WHERE status = 1;

创建索引后,type会从ALL变成refkey变成idx_status。这就是一个完整的最小调优闭环:建索引前后用 EXPLAIN 对比,用执行计划验证索引是否被使用。

5.2 EXPLAIN 核心字段解读

EXPLAIN 输出字段很多,调优时重点看这几列:

字段含义重要关注点
type访问类型从好到差:system > const > eq_ref > ref > range > index > ALL
possible_keys优化器候选索引表示可能有用的索引
key最终选择的索引显示 NULL 说明没走索引
rows预估扫描行数数值越小通常越好
Extra额外信息是否出现 Using filesort、Using temporary、Using index

type是最直观的判断依据。ALL是全表扫描,通常需要优化;index表示扫描了整棵索引树,虽然比 ALL 好,但仍然可能遍历大量数据;range表示范围扫描,比如><BETWEENIN这类查询,属于正常范围;refconst表示高价值的等值匹配,是大多数点查能达到的理想状态。

需要注意,EXPLAIN 的rows是基于统计信息的预估值,不是实际扫描行数。如果rows与实际差异巨大,往往说明表统计信息过期,可以执行ANALYZE TABLE orders;更新统计信息后再观察。

5.3 关注 Extra 字段

Extra字段包含大量调优线索:

  • Using index:覆盖索引扫描,不回表。
  • Using index condition:Index Condition Pushdown,索引下推生效,先过滤索引中已有字段,减少回表次数。
  • Using where:存储引擎返回记录后,Server 层又做了条件过滤。
  • Using filesort:无法利用索引完成排序,需要额外排序操作,常见于 ORDER BY 字段不在索引中。
  • Using temporary:使用了临时表,常见于 GROUP BY、DISTINCT 等操作。
  • Using join buffer:批量连接缓冲,常见于多表关联时没有走索引。

面试中如果被问到“这条 SQL 为什么慢”,第一条思路就是看typeExtraALL + Using filesort的组合,基本就是典型的全表扫描加额外排序,优化方向很明确。

6. 索引失效场景与 SQL 写法避坑

索引建了但不走,比没建索引更让人难受。下面这些场景是导致索引失效的高频原因,每一条都可以用 EXPLAIN 实测验证。

6.1 对索引列使用函数或计算

-- 索引失效 EXPLAIN SELECT * FROM orders WHERE DATE(created_at) = '2026-06-01';

date()函数作用在索引列上,优化器无法直接使用 B+ 树定位,只能全量扫描。写法改成范围查询:

-- 可走索引 EXPLAIN SELECT * FROM orders WHERE created_at >= '2026-06-01 00:00:00' AND created_at < '2026-06-02 00:00:00';

原则是:不要让索引列参与任何函数运算和算术运算。这包括DATE()YEAR()MONTH()SUBSTRING()LENGTH()等。

6.2 隐式类型转换

如果user_id是 BIGINT,却用字符串去匹配日期字段,MySQL 会对字段做隐式转换,导致索引失效:

-- 假设 order_no 是 VARCHAR EXPLAIN SELECT * FROM orders WHERE order_no = 2026000001;

字符串字段用数字匹配时,优化器会把字符串转换成数字比较,通常在索引列上发生转换,导致索引无法正常匹配。反过来,数字字段用字符串匹配也要警惕。最稳妥的办法是保持数据类型一致,代码和 SQL 都按表结构传参。

6.3 LIKE 以通配符开头

-- 索引失效 EXPLAIN SELECT * FROM orders WHERE order_no LIKE '%NO2026%'; -- 可走索引 EXPLAIN SELECT * FROM orders WHERE order_no LIKE 'NO2026%';

前缀通配会导致优化器无法从 B+ 树按顺序定位,只能扫描全部索引或全表。需要模糊搜索时,要么改成前缀匹配,要么考虑 ES 等专业检索引擎。

6.4 OR 条件包含非索引列

-- 如果 status 有索引,user_id 没有,这个查询无法充分利用索引 EXPLAIN SELECT * FROM orders WHERE status = 1 OR user_id = 123;

OR 两侧只要有一个字段没有索引,优化器就只能放弃索引选择权,改为全表扫描后逐行过滤。改用 UNION 拆开:

SELECT * FROM orders WHERE status = 1 UNION ALL SELECT * FROM orders WHERE user_id = 123;

当两侧都有索引时,MySQL 也可能使用 Index Merge 优化,但这依赖优化器成本判断,不能把所有希望都寄托在特殊优化上。

6.5 范围查询会导致右侧联合索引列失效

联合索引(user_id, created_at)中,如果user_id使用了><这类范围条件,索引列右侧的created_at往往无法继续用于精确定位。最经典的场景:

EXPLAIN SELECT * FROM orders WHERE user_id > 100 AND created_at > '2026-01-01';

user_id > 100之后,created_at列只能作为 Filter 过滤条件,无法继续作为索引检索条件。所以联合索引的字段设计要把等值查询列放在前面,范围查询列放在后面。

6.6 NOT IN、NOT BETWEEN 等否定条件

NOT INNOT EXISTS<>这类否定条件通常不容易走索引,原因是优化器认为需要扫描的数据范围过大,选择全表扫描成本更低。实际是否失效取决于数据分布和版本,最稳的方法是直接用 EXPLAIN 验证,不要凭经验下结论。

6.7 排序与分组字段不满足索引顺序

ORDER BY created_at能否走索引,取决于 created_at 是否出现在某个索引的最左前缀序列中。如果联合索引是(user_id, created_at),执行:

EXPLAIN SELECT * FROM orders WHERE user_id = 1 ORDER BY created_at;

此时排序可以用到索引的有序性,Extra 不会出现Using filesort。但如果你直接ORDER BY created_at,由于最左前缀缺失,索引无法直接提供有序结果,就会产生文件排序。

7. 慢查询日志与性能观察

EXPLAIN 解决的是“是否走索引”的问题,慢查询日志解决的是“哪些 SQL 需要调优”的问题。

7.1 开启慢查询日志

MySQL 默认可能没有开启慢查询日志,可以动态开启:

SET GLOBAL slow_query_log = ON; SET GLOBAL long_query_time = 1; SET GLOBAL slow_query_log_file = '/var/log/mysql/mysql-slow.log';

long_query_time表示超过多少秒的记录到慢日志,单位是秒。开发环境建议设置为 1 秒,生产环境则要根据业务压测结果调整,避免日志量过大。

查看是否生效:

SHOW VARIABLES LIKE 'slow_query_log'; SHOW VARIABLES LIKE 'long_query_time';

7.2 分析慢查询日志

最简单的分析工具是mysqldumpslow

mysqldumpslow -t 10 /var/log/mysql/mysql-slow.log

它会输出执行次数最多或耗时最长的 Top N 条 SQL。更专业的工具是pt-query-digest,能按维度聚合 SQL,输出响应时间占比、调用次数、锁等待时间等,适合批量分析。

慢查询日志中的每一条记录包含 Query_time、Lock_time、Rows_sent、Rows_examined 等字段。判断一条慢 SQL 是否有价值,不仅要看 Query_time,还要看 Rows_examined 与 Rows_sent 的比率。扫描 10 万行返回 10 行,说明查询选择性差,需要重点优化;扫描 20 行返回 20 行,说明问题可能不在 SQL,而在整体负载。

7.3 在线查看性能视图

MySQL 提供了 performance_schema 和 sys 库,可以直接查询 SQL 统计:

SELECT SCHEMA_NAME, DIGEST_TEXT, COUNT_STAR, AVG_TIMER_WAIT / 1000000000 AS avg_ms FROM performance_schema.events_statements_summary_by_digest ORDER BY AVG_TIMER_WAIT DESC LIMIT 10;

这条语句能快速找出当前实例中平均响应时间最长的 SQL 语句。观察索引调优效果时,可以用同一组 SQL 在优化前后对比COUNT_STARAVG_TIMER_WAITROWS_EXAMINED_AVG的变化。

7.4 资源占用观察

索引调优不仅是查询速度的问题,也要关注空间与内存占用。B+ 树索引需要磁盘空间,也会占用 InnoDB Buffer Pool。索引过多时,即使查询变快,也可能因为缓冲池命中率下降导致整体性能受损。

观察索引大小可以使用:

SELECT TABLE_NAME, INDEX_LENGTH, DATA_LENGTH FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'demo_db' AND TABLE_NAME = 'orders';

INDEX_LENGTH是所有索引占用的空间;DATA_LENGTH是数据行占用的空间。如果索引占用已经接近甚至超过数据空间,就需要评估是否有大量冗余索引。

8. 高频 MySQL 面试题与回答思路

这一章节把 MySQL 面试里出现频率最高的索引相关问题集中梳理一遍,每道题都给出回答思路和关键得分点。

8.1 为什么 InnoDB 用 B+ 树,而不是 B 树或红黑树?

回答结构拆成三点:磁盘 IO、范围查询、树高。

第一,B+ 树非叶子节点不存数据,只存索引键,每个磁盘页能存储的索引条目远多于 B 树,树高更低。一次索引查找对应一次磁盘 IO,树高直接影响查询速度。第二,B+ 树叶子节点用链表串联,范围查询和排序可以直接沿着链表顺序扫描,B 树则可能涉及回溯到父节点或兄弟节点,效率更低。第三,红黑树虽然平衡性好,但每个节点只存一个键值,高度太高,磁盘 IO 次数不可接受,它主要适用于内存数据结构。

8.2 什么是回表?如何避免?

回表是二级索引查出主键后再去聚簇索引取完整数据行的过程。避免回表的直接手段是覆盖索引,让查询所需字段全部包含在索引中。比如查询只涉及主键和order_nouk_order_no索引就能直接覆盖。面试时如果能补充一句“回表不是设计缺陷,而是二级索引的正常机制,目标不是消除回表,而是减少无效回表”,会很加分。

8.3 最左前缀原则是什么?

联合索引(a, b, c)相当于建立了a(a, b)(a, b, c)三个前缀索引。查询条件必须从最左列开始,才能利用联合索引排序和检索。WHERE b = 1 AND c = 1无法命中这个索引,因为缺失a

回答时可以举例说明设计影响:当需要频繁用b字段单独查询时,不能只依赖(a, b, c),需要额外为b建索引,这就是“联合索引不能替代所有单列索引”的原因。

8.4 索引下推 Index Condition Pushdown 是什么?

索引下推是 MySQL 5.6 引入的优化。联合索引(user_id, created_at)查询条件包含user_id = 1 AND created_at > '2026-01-01'时,在没有 ICP 的情况下,优化器会先根据user_id = 1查出所有主键,再回表逐行过滤created_at。ICP 允许在存储引擎层直接对索引中的created_at字段进行初步过滤,只对满足条件的记录回表,减少 IO 次数。EXPLAIN Extra 显示Using index condition即代表生效。

8.5 深分页查询怎么优化?

深分页慢的根本原因是 MySQL 需要扫描并丢弃前面的大量有效行。经典写法:

SELECT * FROM orders ORDER BY id LIMIT 100000, 20;

这个查询需要扫描 100020 行,然后丢弃前 100000 行。优化方式包括:使用延迟关联,或基于覆盖索引定位起始点。

基于 id 范围的分页示例:

SELECT * FROM orders WHERE id > (SELECT id FROM orders ORDER BY id LIMIT 100000, 1) ORDER BY id LIMIT 20;

子查询先通过覆盖索引拿到偏移位置的 id,再用主键范围查询取数据,避免大偏移扫描。另一种思路是业务上改为游标分页,记住上一页的最后一条主键,下一次直接WHERE id > last_id

8.6 大表加索引有什么风险?

大表加索引会面临两类风险:锁表时间和资源消耗。MySQL 8.0 支持原子 DDL,同时在很多场景下可以使用 Online DDL,不会长时间阻塞 DML,但具体是否在线取决于操作类型和字段。低峰期执行、先备份、评估磁盘空间、观察主从延迟,是上线前必做的四件事。如果表非常大,可以考虑 gh-ost、gh-ost 类工具配合业务空窗期执行。

8.7 联合索引字段顺序如何设计?

没有绝对公式,但有三个通用原则:等值查询列优先、区分度高的列靠前、考虑排序与分组字段。等值条件能稳定命中最左前缀,区分度高的字段能更快缩小扫描范围。当查询既有过滤又有排序时,可以让索引同时覆盖过滤列和排序列,避免Using filesort。真正草率的设计是把所有查询条件随机拼进联合索引,看起来很全,实际哪条查询都无法高效命中。

9. 常见问题排查对照表

问题现象可能原因排查方式解决方案
建了索引但 EXPLAIN 显示全表扫描索引列发生隐式转换或函数运算查看 EXPLAIN key 字段是否为 NULL改写 SQL,保持字段类型一致
SQL 查询时间波动大统计信息过期、缓冲池冷启动、索引选择错误执行 ANALYZE TABLE,对比多次执行时间更新统计信息,必要时用 FORCE INDEX 验证是否索引选择问题
慢查询日志没有记录long_query_time 阈值过高或慢日志未开启SHOW VARIABLES LIKE 'slow_query_log'动态开启并设置合理阈值
分页很深时响应变慢大偏移导致扫描大量无用行查看 Rows_examined改为基于 id 范围或游标分页
联合索引未被使用查询条件不满足最左前缀原则检查查询条件的字段顺序调整联合索引顺序或拆分索引
覆盖索引没有生效查询列超出索引字段查看 Extra 是否出现 Using index确认查询列是否全部在索引中
排序字段导致文件排序ORDER BY 字段不在索引序列内查看 Extra 是否出现 Using filesort让排序列加入联合索引并满足前缀顺序
写入性能下降明显冗余索引过多查看 SHOW INDEX 重复索引删除重复或长期未使用的索引

10. 索引调优最佳实践

第一,把 EXPLAIN 作为索引变更的验收标准。任何一次加索引、改 SQL、调整联合索引字段顺序,都要用优化前后的 EXPLAIN 和执行时间对比来验证效果。

第二,统一索引命名规范。主键索引一般不额外命名,普通索引使用idx_字段名,联合索引用idx_字段1_字段2,唯一索引使用uk_字段名。规范命名能让你半年后回看表结构时不用猜。

第三,验证索引区分度。创建索引前先算一下字段的区分度:

SELECT COUNT(DISTINCT status) / COUNT(*) AS selectivity FROM orders;

选择性接近 1 说明字段值几乎全不同,适合建索引;选择性接近 0 说明大量重复,建索引的意义很小,全表扫描反而更快。例如订单状态字段通常只有几个枚举值,单独建索引的效果有限,更适合放在联合索引中。

第四,联合索引字段顺序按“等值查询列优先、区分度高优先、范围列最后”设计,同时把 ORDER BY、GROUP BY 字段纳入考虑,避免额外排序。

第五,控制单表索引数量。常规业务表建议保持 5 个以内索引,写入频繁的表更要克制。索引不是越多越好,每一次写入都要同步维护所有索引节点。

第六,测试环境验证后再上生产。先在测试库压一份接近生产数据量的数据,用 EXPLAIN 和慢查询日志对比前后差异,尽量选择业务低峰期执行 DDL。涉及线上表结构调整时要有备份和回滚方案。

11. 总结与下一步

这篇文章从索引存储结构讲到 EXPLAIN 分析,再到慢查询定位和面试答案,核心是帮你建立一条完整的索引调优链路:先看typekeyrowsExtra判断是否走索引,再根据Using filesortUsing temporary定位额外开销,最后用慢查询日志找出真正需要优先优化的 SQL。

最先应该做的事,是把文中订单表建出来,亲手执行几遍带索引和不带索引的 EXPLAIN,重点观察typeALLref的变化,以及ExtraUsing index conditionUsing index的区别。最容易踩的坑是隐式类型转换和最左前缀失效,这两类场景在面试和线上排查中都特别高频,建议用不同字段类型反复验证。

后续可以继续拓展的方向:阅读 MySQL 官方文档中 Optimizer 章节,学习OPTIMIZER_TRACE的详细输出;实践 online DDL 在真实业务表上的操作流程;再进阶一点,可以研究 InnoDB Buffer Pool 命中率、redo log 刷盘策略对整体性能的影响。索引调优是一个需要持续用数据验证的过程,把 EXPLAIN 和慢查询日志用熟,后面的路会顺很多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 6:14:24

jcode工具实战:代码生成与格式化提升开发效率

1jehuang / jcode&#xff1a;一个实用的代码生成与格式化工具实战指南 在日常开发中&#xff0c;我们经常需要处理代码格式化、模板生成等重复性工作。手动操作不仅效率低下&#xff0c;还容易出错。今天要介绍的 jcode 工具&#xff0c;正是为了解决这类问题而生。本文将带你…

作者头像 李华
网站建设 2026/9/8 6:14:19

黑盒测试方法详解:等价类、边界值、场景法与错误推测实战

干测试这行&#xff0c;如果被问到最基础的问题&#xff0c;十有八九绕不开黑盒测试。不少刚入行的同学觉得黑盒测试就是"点点点"&#xff0c;没什么技术含量&#xff0c;等真正做过几个项目、被线上问题打脸过几次&#xff0c;才会明白这套东西远比想象中深。黑盒测…

作者头像 李华
网站建设 2026/9/8 6:13:31

MySQL索引调优与面试核心:B+Tree回表及执行计划详解

先回答一个很多人在准备数据库面试时都会问的问题&#xff1a;MySQL 索引调优到底在面什么&#xff1f;如果只看网上流传的“八股文”&#xff0c;你会发现索引相关的问题翻来覆去就是 BTree、聚簇索引、回表、最左前缀。这些概念确实重要&#xff0c;但真正到了面试现场&#…

作者头像 李华
网站建设 2026/9/8 6:12:17

SQL GROUP BY 与 HAVING 用法详解:从分组统计到性能优化

SQL 里的 GROUP BY 和 HAVING&#xff0c;是数据库管理系统日常开发与数据分析岗位面试里最高频的两个子句&#xff0c;也是从“会查表”走向“会统计”的分水岭。很多人能背出语法&#xff0c;但一遇到“用 WHERE 还是 HAVING”“为什么 HAVING 不能单独使用”“GROUP BY 之后…

作者头像 李华
网站建设 2026/9/8 6:10:06

基于MBLS与Copula的光伏功率时空概率预测及Matlab实现

跑过光伏功率预测项目的人应该都有这种感觉&#xff1a;点预测做得再准&#xff0c;遇到连续阴雨天、突发阵性云层遮挡时&#xff0c;结果照样被打得七零八落。光伏功率的波动性和随机性不是靠堆模型就能彻底压住的&#xff0c;真正在电力调度和现货交易里能派上用场的&#xf…

作者头像 李华
网站建设 2026/9/8 6:09:59

6个前端组件搞定精美表单:搜索框、提交按钮与校验反馈实战

简介&#xff1a;这是面向前端初学者与进阶开发者的6个精美表单提交与搜索框设计资源&#xff0c;核心覆盖文本框、下拉菜单、复选框、单选按钮等常见输入元素&#xff0c;以及提交、清除按钮的样式与交互实现&#xff0c;用于解决表单布局单调、搜索框反馈不足等典型UI痛点。压…

作者头像 李华