面试官:MySQL 索引优化你只会加索引?Explain 执行计划的 12 个字段全解析

最近接触了几个面试,问到 "MySQL 怎么优化慢 SQL",回答基本就一句话:"加索引呗。" 再追问 "你怎么确定加哪个索引?Explain 的 type 字段有哪些取值?什么是 Using filesort?" 要么含糊其辞,要么干脆答不上来。

说实话,只会 EXPLAIN + SQL 然后盯着 key 字段看"有没有走索引",这不算会用 Explain。Explain 输出的 12 个字段里藏着优化器的全部决策细节:SQL 先执行哪一部分、联合索引用了几个字段、排序走没走索引、临时表有没有产生——这些才是判断 SQL 性能瓶颈的关键。

今天这篇文章,把 Explain 的 12 个字段拆开来逐个讲,文末再放一道真实的 SQL 优化案例,带大家把学到的字段串起来用一遍。


一、执行计划长什么样?先有个整体印象

在任意一条 SELECT/UPDATE/DELETE/INSERT 语句前加上 EXPLAIN,MySQL 不会真的执行 SQL,而是返回优化器评估后的"执行计划":

EXPLAIN SELECT e.emp_name, d.dept_name
FROM t_employee e
INNER JOIN t_department d ON e.dept_id = d.id
WHERE e.age > 30;

执行后你会看到一张表(以下是基于示例工程 1 万条员工数据的真实输出):

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEdNULLALLPRIMARYNULLNULLNULL18100.00Using temporary; Using filesort
1SIMPLEeNULLrefidx_dept_id,idx_name_age_salaryidx_dept_id9explain_demo.d.id55533.33Using where

这 12 个字段的重要程度不同。我按照面试出现频率 + 实战中"需要重点看"的优先级,把它们分为 3 档:

  • 🔥 必看(Top 6)typekeyrowsExtrakey_lenid
  • ⭐ 次看(4 个)select_typepossible_keysfilteredref
  • 💡 了解(2 个)tablepartitions

接下来我们从必看的 6 个开始,逐个拆解。


二、🔥 必看字段 Top 6

1️⃣ type:访问类型——性能分层的"温度计"

type 是 Explain 里最重要的一个字段,没有之一。它告诉我们 MySQL 是怎样从表里找行的,取值从差到好有 7 个层级:

ALL  <  index  <  range  <  ref  <  eq_ref  <  const/system  <  NULL
 ↑ 最差                                                          最好 ↑

下面我们逐个看每个取值到底是什么场景、对应的 SQL 怎么写。

(1)ALL:全表扫描 —— 性能红线

含义:从头扫到尾,一行行找。数据量大了直接 OOM。

-- ❌ phone 列没有索引,必然 ALL
EXPLAIN SELECT * FROM t_employee WHERE phone = '13800000001';

输出(关键部分,下同):

typekeyrowsExtra
ALLNULL10000Using where

判断时机:type = ALL 基本就两种原因——① 没加索引;② 索引失效(函数/隐式转换/前导% LIKE)。除了小表(< 1000 行)做全表比走索引更划算之外,其他场景必须修。

(2)index:全索引扫描 —— "比全表好一点但仍然慢"

含义:不读数据文件了,但把整个索引树遍历了一遍。常见于"查询的列刚好全在某个索引里"但没任何 WHERE 条件的场景。

-- 只有 emp_name 在联合索引 idx_name_age_salary 里,没有过滤条件
EXPLAIN SELECT emp_name FROM t_employee;
typekeyrowsExtra
indexidx_name_age_salary10000Using index

Using index覆盖索引的信号(后面 Extra 会详细讲),这里是好的;但 type = index 说明索引被全扫了,如果 WHERE 条件加不上,大表仍然很慢。

(3)range:索引范围扫描 —— 日常查询的主力

含义:在 B+ 树索引上按范围取行。触发条件:><BETWEENINLIKE 'xxx%'(前缀匹配,前导 % 不走)。

EXPLAIN SELECT * FROM t_employee WHERE salary BETWEEN 20000 AND 30000;
EXPLAIN SELECT * FROM t_employee WHERE dept_id IN (6, 7, 8);
EXPLAIN SELECT * FROM t_employee WHERE emp_name LIKE '员工00%';

salary BETWEEN 为例:

typekeykey_lenrowsExtra
rangeidx_salary62138Using where

注意IN (...) 在 InnoDB 里是 range,不是 ref。值少和值多走的计划可能不同。

(4)ref:非唯一性索引的等值查询 —— 最常用的"正常级别"

含义:用普通索引(非唯一/非主键)做等值匹配,可能匹配多行。

EXPLAIN SELECT * FROM t_employee WHERE dept_id = 6;
typekeykey_lenrows
refidx_dept_id9555

dept_id 是普通索引,每个部门有几百人,所以返回多行,归类为 ref。这是日常业务查询最常见、也最"健康"的 type。

(5)eq_ref:唯一性索引等值 JOIN —— JOIN 场景的天花板

含义:在 JOIN 查询中,第二张表用主键或唯一键关联,最多只匹配一行。这是 JOIN 里性能最好的访问类型。

EXPLAIN
SELECT e.emp_name, d.dept_name
FROM t_employee e
INNER JOIN t_department d ON d.id = e.dept_id
WHERE e.salary > 30000;
idtabletypekeykey_lenrefrows
1erangeidx_salary6NULL1746
1deq_refPRIMARY8explain_demo.e.dept_id1

d.id 是 t_department 的主键,每个 e.dept_id 对应 最多 1 条 部门记录 → eq_ref。

区分 eq_ref / ref 小技巧:看被驱动表(第二张表)的关联字段是不是唯一的。主键/唯一键 → eq_ref;普通索引 → ref。

(6)const / system:主键/唯一键等值命中 —— 极致性能

含义:查询一开始就读取,整个生命周期里只取一行。system 是 const 的特例(表里只有 1 行,MyISAM 引擎常见)。

EXPLAIN SELECT * FROM t_employee WHERE id = 1001;
EXPLAIN SELECT * FROM t_employee WHERE emp_no = 'E000001';
EXPLAIN SELECT * FROM t_department WHERE dept_name = '后端开发部';
typekeykey_lenrefrows
constPRIMARY8const1
constuk_emp_no130const1
constuk_dept_name258const1

面试常考:把 type 从差到好背下来,每个举一个例子。这道题我自己面试时问过不下 50 人,能完整答对的 < 10%。


2️⃣ key & possible_keys:可能走 vs 实际走

这两个字段放一起看,能回答一个经典问题:"我明明加了索引,为什么 SQL 还是慢?"

  • possible_keys可能用到的索引(优化器先挑一批候选)
  • key:优化器最后真正选的索引

两者不一致的时候,就是"为什么不走这个索引"的分析点。

典型场景 1:possible_keys 有值,但 key = NULL

-- status 列未建索引,只有 1/2/3 三种值,区分度极低
EXPLAIN SELECT * FROM t_employee WHERE status = 1;
possible_keyskeytyperows
NULLNULLALL10000

但如果你提前给 status 加了个单列索引(我们示例里没加),你会看到:

possible_keyskeytyperows
idx_statusNULLALL10000

这就是典型的 "有索引但不用"——优化器估算走索引回表的成本比全表还高,干脆直接扫。

经验阈值:单列索引区分度 < 10% 基本不会走。像 statusgenderis_deleted 这种只有 2~5 个取值的列,单独建索引几乎永远用不上,必须和区分度高的列组成联合索引

典型场景 2:possible_keys 多个,优化器挑了"意料之外"的

-- 两个候选索引:idx_dept_id / idx_salary
EXPLAIN SELECT * FROM t_employee
WHERE dept_id = 6 AND salary BETWEEN 20000 AND 40000;
possible_keyskeytyperows
idx_dept_id, idx_salaryidx_dept_idref555

为什么挑 idx_dept_id 不挑 idx_salary?因为优化器算成本:dept_id = 6 大概 555 行,再里面过滤 salary;而 salary BETWEEN 大概 3000+ 行再过滤 dept_id。前者更便宜。

强制验证工具FORCE INDEX(xxx) 可以强制走某个索引,用来对比真实耗时判断优化器决策是否合理。生产环境不要硬编码 FORCE INDEX,版本升级/数据变化后可能反优化。


3️⃣ key_len:联合索引用了几个字段?

这是 Explain 里最被低估的字段。面试答上来直接加分。

key_len 表示索引使用的字节数。通过这个数字可以反向推断联合索引的前缀几个字段真正被用到了

字节数计算规则(utf8mb4 字符集)

字段类型基础字节变长额外允许 NULL 额外合计示例
VARCHAR(n)n × 4+2+1VARCHAR(64) + NULL = 64×4+2+1 = 259
CHAR(n)n × 40+1CHAR(10) + NULL = 41
INT40+15
BIGINT80+19
TINYINT10+12
DATE30+14
DECIMAL(m,d)约⌈m/2⌉(压缩存储)0+1DECIMAL(10,2) ≈ 6

核心记忆点:utf8mb4 每个字符占 4 字节,变长 +2,允许 NULL +1。所以定长字段尽量设为 NOT NULL,省字节也省判断。

示例:联合索引 idx_name_age_salary(emp_name, age, salary)

我们一步步看最左前缀原则的实际效果:

① 只用到第一个字段 emp_name(key_len = 259)

EXPLAIN SELECT * FROM t_employee WHERE emp_name = '员工01234';
possible_keyskeykey_len
idx_name_age_salary,...idx_name_age_salary259

② 用到前两个字段 emp_name + age(key_len = 259 + 2 = 261)

EXPLAIN SELECT * FROM t_employee
WHERE emp_name = '员工01234' AND age = 30;
key_len
261

③ 三个字段全用到(key_len = 259 + 2 + 6 = 267)

EXPLAIN SELECT * FROM t_employee
WHERE emp_name = '员工01234' AND age = 30 AND salary = 25000.00;
key_len
267

④ 违反最左前缀,跳过 age,只用了 emp_name(key_len 还是 259)

EXPLAIN SELECT * FROM t_employee
WHERE emp_name = '员工01234' AND salary = 25000.00;
key_len
259

面试题:联合索引 (a, b, c),WHERE a = ? AND c = ?,key_len 是多少?

答:只有 a 的长度。因为跳过了 b,c 用不上(ICP 会让 c 在索引上过滤,但不算"被 key_len 包含",这点区分好)。


4️⃣ rows:估算要扫多少行?

rows 是优化器根据统计信息估算要读取的行数,不是精确值,但足够用来做量级判断。

核心原则:rows × 每张表 = 整个查询的复杂度上限。

举个递进的例子(员工表 1 万行):

SQL 条件typerows
WHERE dept_id = 6ref555
WHERE dept_id = 6 AND age > 25ref555
WHERE dept_id = 6 AND age > 25 AND salary > 30000ref555

注意到后两行的 rows 也是 555?因为优化器只按索引列 dept_id 估算,age/salary 的过滤效果体现在 filtered 字段上(下面就讲)。

排查小技巧:如果 rows 跟你实际 LIMIT 出来的数量差 10 倍以上,八成是统计信息过期了,跑一下 ANALYZE TABLE t_employee; 更新直方图。


5️⃣ id:SQL 先执行哪一块?

id 是查询的"执行顺序号",有三条规则:

  1. id 相同 → 从上到下顺序执行
  2. id 不同 → id 越大越先执行(子查询优先)
  3. id 有 NULL → 最后执行(UNION 的合并结果)

来看三个真实例子。

示例 1:id 相同,从上到下

SELECT e.emp_name, d.dept_name
FROM t_employee e INNER JOIN t_department d ON e.dept_id = d.id
WHERE e.age > 30;
idtabletype
1dALL
1eref

两行 id 都是 1,所以 MySQL 先扫 t_department(d),再用 dept_id 去关联 t_employee(e)。顺序就是 d → e。

示例 2:id 不同,子查询先跑

SELECT emp_name, salary
FROM t_employee
WHERE dept_id = (SELECT id FROM t_department WHERE dept_name = '后端开发部');
idselect_typetabletype
2SUBQUERYt_departmentconst
1PRIMARYt_employeeref

id=2 是子查询,先拿到"后端开发部"的 id=6,再用 id=1 的 PRIMARY 查询员工。

示例 3:UNION 最后一行 id=NULL

SELECT id, emp_name FROM t_employee WHERE dept_id = 6
UNION
SELECT id, emp_name FROM t_employee WHERE salary > 50000;
idselect_typetable
1PRIMARYt_employee
2UNIONt_employee
NULLUNION RESULT

UNION 结果行 id 为 NULL,是最后做去重合并的那一步。


6️⃣ Extra:信息量最大的"附加说明"

Extra 是 Explain 里字段最短、信息最密的一列。优化 SQL 的 80% 工作就是处理这一列里的"红色预警"。

我把 Extra 的常见取值分成三类:❌ 危险信号 / ⚠️ 正常信号 / ✅ 优秀信号

❌ 危险信号(必须优化)

Using filesort:外部排序,没走索引排序

含义:ORDER BY / GROUP BY 的列没命中索引,MySQL 需要在内存/磁盘里做额外排序。

-- 没有 (dept_id, salary) 联合索引
EXPLAIN SELECT * FROM t_employee WHERE dept_id = 6 ORDER BY salary DESC;
typekeyExtra
refidx_dept_idUsing where; Using filesort

这里 Using filesort 跟"文件"没关系,只是叫这名。数据量小在内存里排,大了就落临时文件,性能断崖。

修复方法:给 WHERE 条件列 + ORDER BY 列建联合索引,让 ORDER BY 直接走索引有序性:

ALTER TABLE t_employee ADD KEY idx_dept_salary (dept_id, salary);
-- 再 EXPLAIN 一次
typekeyExtra
refidx_dept_salaryUsing where

Using filesort 消失了!因为拿到的行本身就按 salary 有序,直接返回即可。

Using temporary:使用了临时表

含义:GROUP BY / DISTINCT / UNION 过程中,MySQL 建了一张内部临时表放中间结果。

EXPLAIN
SELECT dept_id, AVG(salary) AS avg_s
FROM t_employee
WHERE age > 30
GROUP BY dept_id;
typekeyExtra
ALLNULLUsing where; Using temporary; Using filesort

没有 (age, dept_id) 索引时,先按 age 过滤后要按 dept_id 分组,只能建临时表 + 对分组结果排序,两个红灯同时亮。

修复思路:GROUP BY 的列加联合索引前缀;或者业务允许的话加 ORDER BY NULL 取消分组后的排序(只消除 filesort,不消除 temporary)。

✅ 优秀信号(越多越好)

Using index:覆盖索引,不需要回表

含义:SELECT 涉及的所有列刚好都在同一个索引里,不需要回表查聚簇索引的完整行。这是覆盖索引的直接证据。

-- 索引 idx_name_age_salary(emp_name, age, salary) 里包含了所有 SELECT 的列
EXPLAIN SELECT emp_name, age, salary FROM t_employee
WHERE emp_name = '员工01234' AND age = 30;
typekeyExtra
refidx_name_age_salaryUsing index

Using index + type = ref,是单表查询里的理想组合。优化原则:能用覆盖索引就别 SELECT *

Using index condition:索引条件下推(ICP)

含义:MySQL 5.6 引入的优化。本来索引只用来定位行、其他条件回 Server 层再过滤;ICP 开启后,索引里能判断的条件直接在存储引擎层过滤完,减少回表和传上 Server 层的行数。

EXPLAIN SELECT * FROM t_employee
WHERE emp_name = '员工01234' AND age > 35;
Extra
Using index condition; Using where

看到 Using index condition 就意味着:emp_name 用来 B+ 树定位,age(同样在联合索引里)直接在引擎层判断 > 35,过滤掉不合格的再回表,回表次数大大降低。

ICP 默认开启(optimizer_switch = 'index_condition_pushdown=on'),一般不用手动关。

Select tables optimized away:极致优化

含义:MIN/MAX 直接取 B+ 树的最左/最右叶节点,不用扫描行。

EXPLAIN SELECT MIN(salary), MAX(salary) FROM t_employee;
typekeyrowsExtra
NULLNULLNULLSelect tables optimized away

这个信号只出现在对索引列做 MIN/MAX 且无 GROUP BY的场景,已经是理论最优。

⚠️ 正常信号(不必过度反应)

Using where

Server 层拿到存储引擎返回的行之后,又在 Service 层用 WHERE 条件过滤了一遍。常见于索引没有覆盖所有过滤条件的场景,不代表有问题,但可以考虑加联合索引进一步优化。

Impossible WHERE
EXPLAIN SELECT * FROM t_employee WHERE 1 = 2;
typerowsExtra
NULLNULLImpossible WHERE

WHERE 条件永远为假,MySQL 直接返回空,不会读表。这种写得离谱的 SQL 线上少见,但排查时看到了别慌,是语法逻辑层面的问题。


三、⭐ 次看 4 字段

7️⃣ select_type:这条 SELECT 是什么角色?

告诉我们当前行属于查询里的哪个部分,常见 6 个取值:

取值含义出现场景
SIMPLE简单查询无 UNION / 子查询的普通 SELECT
PRIMARY主查询包含子查询时的最外层 SELECT
SUBQUERY非相关子查询SELECT / WHERE 里的子查询(独立执行一次)
DEPENDENT SUBQUERY相关子查询子查询依赖外层列,每行都要执行一次,慢
UNIONUNION 的后续 SELECTUNION 里第二个及以后的查询
DERIVED派生表FROM (SELECT ...) t,MySQL 5.7+ 会尝试合并

重点提一下 DEPENDENT SUBQUERY,性能杀手级的存在:

EXPLAIN
SELECT e.emp_name,
       (SELECT dept_name FROM t_department d WHERE d.id = e.dept_id) AS dept_name
FROM t_employee e
WHERE e.age > 40;
idselect_typetable
1PRIMARYe
2DEPENDENT SUBQUERYd

子查询里用到了外层的 e.dept_id,意味着外层每返回一行员工,子查询就要执行一次。如果外层匹配了 1000 个员工,子查询被调 1000 次。改写成 JOIN 通常能让优化器选出更优的 NL/HASH JOIN 计划。

8️⃣ filtered:过滤后剩余行数占比

rows × filtered / 100 就是优化器预估要传给下一张表 JOIN 的行数。这个值越接近 100 越好,说明 WHERE 条件对当前索引定位出来的行过滤得很精准。

-- 高过滤:emp_name LIKE '员工00%' 定位到 100 行,几乎都保留
EXPLAIN SELECT * FROM t_employee WHERE emp_name LIKE '员工00%';  -- filtered ≈ 100%

-- 低过滤:全表扫,WHERE phone LIKE '%138%' 在 Server 层才过滤
EXPLAIN SELECT * FROM t_employee WHERE phone LIKE '%138%';      -- filtered ≈ 11.11%

filtered 低 + type = ALL 组合出现时,基本就意味着"该加索引了"。

9️⃣ ref:索引跟谁比?

ref 显示的是索引列被用来和什么比较。常见三种:

ref 值含义
const与常量比较(WHERE emp_no = 'E000001')
db.table.colJOIN 时与另一张表的列比较(d.id = e.dept_id)
func与某个函数值比较(少用,出现了通常意味着写得怪)

🔟 table:正在查哪张表?

大部分情况就是表名/别名。两种特殊值:

  • <derivedN>:FROM 里的派生表,N 对应子查询的 id
  • <unionM,N>:UNION 合并的临时表,M/N 对应参与 UNION 的 id

四、💡 了解 2 字段

1️⃣1️⃣ partitions:命中的分区

分区表才会有值。绝大多数没做分区的线上表都是 NULL。

1️⃣2️⃣ partitions / table:其他

剩下就没什么好展开的了,遇到再说。


五、综合实战:一条慢 SQL 的优化前后对比

光说不练假把式。我们把上面的知识点用在一道真实的统计 SQL 上,一步一步把它从"跑几秒"优化到"毫秒级"。

场景

统计 2023 年入职的在职员工,按部门聚合出人数、平均薪资、最高薪资,按平均薪资倒序取前 10。

优化前 SQL

SELECT d.dept_name,
       COUNT(*)           AS emp_count,
       AVG(e.salary)      AS avg_salary,
       MAX(e.salary)      AS max_salary
FROM t_department d
LEFT JOIN t_employee e ON e.dept_id = d.id
WHERE e.status = 1
  AND YEAR(e.hire_date) = 2023      -- ⚠️ 对索引列做函数
GROUP BY d.dept_name
ORDER BY avg_salary DESC
LIMIT 10;

执行计划输出(节选):

idtabletypekeyrowsExtra
1dALLNULL18Using temporary; Using filesort
1eALLNULL10000Using where; Using join buffer (hash join)

问题诊断(把前面学的字段一个个对上):

  1. type = ALL(两张表都是):全表扫,员工表扫 10000 行
  2. Using temporary:按 dept_name 分组建了临时表
  3. Using filesort:ORDER BY avg_salary 在临时表上额外排序
  4. YEAR(e.hire_date):对 hire_date 做函数运算,即使有 idx_hire_date 也不会走
  5. e.status 区分度低:单列加索引也白搭

优化方案

第一步:改写法——去掉索引列上的函数

--  YEAR(e.hire_date) = 2023
--  ↓ 等价替换
AND e.hire_date BETWEEN '2023-01-01' AND '2023-12-31'

第二步:建覆盖联合索引——把 WHERE + JOIN 用到的列打包成一条索引

ALTER TABLE t_employee
    ADD KEY idx_status_hire_dept_salary (status, hire_date, dept_id, salary);

为什么这么排?遵循联合索引的"等值在前,范围在后,包含 SELECT 列"的原则:

  • status = 1:等值 → 放第一位
  • hire_date BETWEEN ...:范围 → 放第二位
  • dept_id:JOIN 关联列 → 放第三位
  • salary:聚合用到 AVG/MAX,放进来让它变成覆盖索引,不回表

优化后 SQL

SELECT d.dept_name,
       COUNT(*)      AS emp_count,
       AVG(e.salary) AS avg_salary,
       MAX(e.salary) AS max_salary
FROM t_department d
LEFT JOIN t_employee e ON e.dept_id = d.id
WHERE e.status = 1
  AND e.hire_date BETWEEN '2023-01-01' AND '2023-12-31'
GROUP BY d.dept_name
ORDER BY avg_salary DESC
LIMIT 10;

优化后执行计划

idtabletypekeyrowsExtra
1dALLPRIMARY18Using temporary; Using filesort
1erefidx_status_hire_dept_salary210Using index

关键改善点

指标优化前优化后效果
员工表 typeALLref全表 → 走索引
员工表 rows10000210扫描行数降到 1/48
员工表 ExtraUsing where; join bufferUsing index覆盖索引,零回表
GROUP BY avg_salary 临时表仍有(聚合计算天然需要)仍有但输入行数少了两个数量级,代价可忽略

说明ORDER BY avg_salary 因为排序对象是聚合后的 18 行部门数据,Using temporary + Using filesort 完全在可接受范围内。不要盲目追求 Extra 里没有任何警告,要结合量级判断。


六、面试高频话术整理

最后给大家整理一段遇到 Explain 相关面试题时的回答模板,照着说基本不会出错:

"Explain 输出我主要看 6 个字段:type、key、key_len、rows、Extra、id。

首先看 type,如果是 ALL 就要想办法加索引或改写,目标是至少到 range,正常情况 ref;主键 JOIN 能到 eq_ref,主键等值查询能到 const。

然后看 keypossible_keys,确认候选里是否有合适的索引、优化器为什么没选;key_len 用来推算联合索引前缀用了几个字段,检查是否违反最左前缀。

rows 是估算扫描行数,量级对不对,和 filtered 乘一下判断传给下游的数量。

Extra 是重点:Using filesort 就给 ORDER BY 加联合索引;Using temporary 就考虑覆盖索引或改写 GROUP BY;看到 Using index 就说明覆盖索引生效。

最后用 id 判断执行顺序:同号从上到下,异号大的先跑,id=NULL 是 UNION 合并。"

这段话术涵盖了 80% 的面试追问,剩下的 20% 就是考你有没有真实优化经验——把今天的综合案例说清楚,面试官基本就点头了。


七、总结 & 配套资源

核心速记口诀

type 层级别记混:ALL→index→range→ref→eq_ref→const
key_len 用处大:联合索引用几段,算字节就知道
Extra 三个红灯:filesort/temporary 要干掉,Using index 多多益善
possible_keys 是候选,key 才是实锤,不用就分析成本

建议大家本地 MySQL 跑一遍,把每个 EXPLAIN 输出和本文的讲解一一对应,比死记硬背有效 10 倍。


🗨️ 互动话题

你在项目里遇到过 Explain 里最"离谱"的计划是什么? 欢迎在评论区留言分享:

  1. 有没有见过 possible_keys 一堆但 key 为 NULL 的反直觉情况?最后怎么解决的?
  2. 你有没有靠 key_len 发现联合索引用错字段的真实经历?
  3. 对于 MySQL 8.0 新出的 EXPLAIN ANALYZE(真执行 + 实际耗时),你用了吗?体验如何?

本公众号专注 Java / 分布式 / MySQL / 微服务。更多深度文章欢迎订阅公众号「服务端技术精选」或访问我的博客。


标题:面试官:MySQL 索引优化你只会加索引?Explain 执行计划的 12 个字段全解析
作者:jiangyi
地址:http://www.jiangyi.space/articles/2026/08/22/1787389990474.html
公众号:服务端技术精选
    评论
    0 评论
avatar

取消