跳至主要内容

MySQL EXPLAIN 怎么看

MySQL EXPLAIN 怎么看

EXPLAIN 输出主要看这几个关键字段,我按重要性和常见坑点来讲。

核心字段速览

字段 含义
id 查询的执行顺序标识,id 越大越先执行(子查询、派生表会有不同 id)
select_type 查询类型:SIMPLE、PRIMARY、SUBQUERY、DERIVED、UNION 等
table 当前这一行对应的表
type 最重要,访问类型,反映查询效率
possible_keys 可能用到的索引
key 实际用到的索引
key_len 索引使用的字节数,可以判断联合索引用了几列
rows 预估扫描行数(估算值,非精确)
filtered 按条件过滤后剩余行的百分比
Extra 第二重要,很多关键信息都在这里

type 字段:从好到差的排序

system > const > eq_ref > ref > range > index > ALL
  • system/const:主键或唯一索引等值查询,最快,基本只在单表单行时出现
  • eq_ref:多表连接时,被驱动表用主键/唯一索引做等值匹配(比如 a.id = b.a_id 且 b.a_id 是唯一索引)
  • ref:普通索引的等值查询,可能返回多行
  • range:索引范围扫描,比如 ><BETWEENIN
  • index:索引全扫描(遍历整棵索引树),比 ALL 好但依然要扫全部索引
  • ALL:全表扫描,一般需要优化

经验标准:一般要求至少达到 range 级别,最好是 ref 或以上,看到大表出现 ALL 基本就要警觉了。

Extra 字段:常见情况解读

1. Using filesort(需要额外排序)

出现在 ORDER BY 无法利用索引顺序时,MySQL 需要在内存或磁盘中额外排序。

常见诱因: - ORDER BY 的列没有索引 - 联合索引使用顺序和 ORDER BY 不一致 - WHERE 条件用了索引,但排序列不在索引里,且中间断了顺序性

优化思路:建立能同时覆盖 WHERE 和 ORDER BY 的联合索引,让索引顺序和排序顺序一致。

2. Using temporary(使用临时表)

常见于 GROUP BYDISTINCT、某些子查询、UNION 场景。临时表开销较大,配合 Using filesort 出现时通常是性能瓶颈信号。

优化思路:GROUP BY 的列尽量走索引;避免不必要的 DISTINCT。

3. Using index(覆盖索引,好事)

表示查询所需的所有列都能从索引中直接获取,不需要回表查聚簇索引数据,效率很高。

这个和 Using where 不冲突,可能同时出现:"Using where; Using index" 表示用索引过滤又直接用索引取数据。

4. Using where

表示在存储引擎返回数据后,MySQL Server 层还需要根据 WHERE 条件再过滤一次(说明索引没有完全覆盖过滤条件)。单独出现不算坏事,但结合 type=ALL 就意味着全表扫描后再过滤,效率差。

5. Using index condition(索引下推 ICP)

5.6+ 版本引入,将部分 WHERE 条件下推到存储引擎层用索引过滤,减少回表次数,属于优化效果,是好事。

6. Using join buffer

两表连接时,被驱动表没有可用索引,MySQL 用连接缓冲区(Block Nested Loop)来减少扫描次数。出现这个通常意味着被驱动表缺少合适的索引,需要加索引。

7. Impossible WHERE / const row not found

说明 WHERE 条件本身就不可能成立(比如逻辑矛盾),或者表是空表,MySQL 直接判断不需要真正查询。

联合索引相关的常见坑

最左前缀失效

联合索引 (a, b, c): - WHERE a=1 AND b=2 → 能用索引 - WHERE b=2 AND c=3(跳过 a)→ 用不上这个索引 - WHERE a=1 AND c=3(跳过 b)→ 只能用到 a 这一列

key_len 判断实际用了几列

比如 key_len=5,如果第一列是 int(4字节)+1字节null标记,说明只用了联合索引的第一列,第二列没用上——这时候要回头看 WHERE 条件写法或者数据类型是否匹配。

索引失效的典型场景

  1. 索引列上做了函数运算:WHERE YEAR(create_time)='2024' → 索引失效
  2. 隐式类型转换:字段是 varchar,但传入的是数字,MySQL 会做类型转换导致索引失效
  3. LIKE '%xxx' 前导通配符 → 索引失效(LIKE 'xxx%' 可以用)
  4. 索引列参与了运算:WHERE id + 1 = 100
  5. OR 条件中有一侧列没有索引,可能导致整体索引失效(视版本和优化器而定)
  6. 联合索引里,中间某列用了范围查询(如 >),后面的列即使在索引里也不能再用来等值过滤索引(只能用于排序覆盖等有限场景)

一个简单排查思路

  1. 先看 type,是不是 ALL/index 这种低效访问
  2. key 是不是空,如果 possible_keys 有值但 key 是 NULL,说明优化器没选它(可能被判断为不划算,或者其实是索引失效)
  3. rows,扫描行数和实际结果差异大不大
  4. Extra,重点关注 Using filesortUsing temporary
  5. key_len 验证联合索引到底用了几列

此博客中的热门博文

Elasticsearch 读写原理指南

### 1. 什么是 segment,里面装了什么? 在 Lucene(也是 Elasticsearch)里,索引被切分成若干 **segment(段)**,每个 segment 是一个完整的、只读的倒排索引单元。一个 segment 包含: * **倒排词典** —— 用 **FST(Finite‑State Transducer)** 以高度压缩的形式保存每个字段出现的所有 term 以及 term→ord 的映射。对应的磁盘文件是 `*.tim`(新版)或 `*.tis/*.tii`(旧版)。 * **倒排列表(postings)** —— 保存每个 term 出现的文档 ID、频次、位置信息等,文件名通常是 `*.doc`、`*.pos`、`*.pay`。 * **存储字段**(_source、store:true 的字段)—— 以二进制块的形式写入 `*.fdt` / `*.fdx`。 * **doc‑values、norms、向量** 等辅助结构,分别保存在 `*.dv`、`*.norm`、`*.tv` 等文件里。 * **deleted‑docs bitmap**(`*.del`),标记哪些文档已被删除或被更新。 所有这些文件在 segment **写入磁盘后即成为只读**,后续的查询只能读取,永远不会在原文件上进行增删改。 --- ### 2. 原始文档和 FST 为什么都在 segment 里? * **原始文档**:Elasticsearch 默认把完整的 JSON(_source)以及任何 `store:true` 的字段写入 segment 的 `*.fdt/*.fdx` 文件。每个 segment 保存自己的那部分文档,旧的 segment 在合并前仍然保留,直到合并后被删除。 * **FST**:每个字段的词典在每个 segment 中单独维护,采用 FST 进行前缀共享和字节压缩。这样即使同一个 term 在多个 segment 中出现,也会在每个 segment 里拥有独立的映射,查询时只需要在对应 segment 的 FST 中定位即可。 --- ### 3. 查询时到底是怎么遍历 segment 的? 1. **请求入口**      客户端的搜索请求先到达 **协调节点**,协调节点把请求 ...

LLM缓存详解

 可以把“大模型缓存”理解成: 把已经算过的结果(或中间结果)存下来,下次尽量复用 。但这里面其实分几层,不只是简单的“问题→答案”缓存。 1️⃣ 常见的几种缓存类型 (1)KV Cache(推理内部缓存) Transformer 在生成时,会把前面 token 的 Key/Value 向量 缓存下来。 本质:避免重复计算 attention 作用: 同一请求内部加速 特点: 👉 只对“同一上下文继续生成”有效 👉 不跨用户、不跨请求 这类缓存是你体感“流式输出越来越快”的原因之一。 (2)Prompt Cache(提示词缓存) 缓存的是: 相同(或高度相似)的 prompt → 对应的中间表示 / 输出 典型场景: 系统提示词(system prompt)很长 多轮对话里前文基本不变 👉 这里能省掉 前缀计算成本(prefill) (3)Embedding / 语义缓存(Semantic Cache) 这个才是你问题的关键 👇 不是按“字符串完全一致”,而是: 把问题转成向量 → 找“语义相似”的历史问题 → 直接复用答案 2️⃣ 为什么命中缓存成本低很多? 因为大模型推理成本主要在两块: (1)Prefill(吃 prompt) 复杂度 ~ O(n²) 很贵(尤其长 prompt) (2)Decode(逐 token 生成) 每个 token 都要算一遍模型 而缓存命中后: KV cache:不用重复 attention Prompt cache:不用重新 encode 语义缓存: 直接跳过模型推理 👉 相当于从: 几十~几百毫秒 + GPU算力 变成: 一次向量检索(毫秒级)+ 直接返回 所以成本差一个数量级是正常的。 3️⃣ “每个人问法不同,怎么命中缓存?” 这是核心难点,也是工程重点👇 ❌ 不能靠字符串匹配 比如: “今天天气怎么样” “今天外面热不热” 字符串完全不同 → 必须 miss ✅ 用语义相似度(Embedding) 流程一般是: 把问题转 embedding(向量) 在向量数据库里找 TopK 相似问题 如果相似度 > 阈值(比如 0.9) 直接返回缓存答案 一个简单示意 Q1: 北京天气怎么样 → embedding A Q2: 北京今天热吗 → embedding B cosine(A, B) ≈ 0.95...

事务的ACID是什么

 事务的 ACID 是数据库事务必须满足的四个基本性质,用来保证在并发和故障情况下数据的正确性与可靠性: A(Atomicity,原子性) 一个事务中的操作要么 全部成功 ,要么 全部失败回滚 ,不存在“只做了一半”的中间状态。 C(Consistency,一致性) 事务执行前后,数据库都必须处于 一致的合法状态 ,满足约束(如主键、外键、唯一性、业务规则等)。 I(Isolation,隔离性) 并发执行的多个事务之间 相互隔离 ,一个事务未提交的中间结果对其他事务不可见(具体强弱由隔离级别决定)。 D(Durability,持久性) 一旦事务提交成功,其结果会被 永久保存 ,即使系统崩溃也不会丢失(通常依赖 WAL/redo log 等机制)。 一句话记忆: 要么全做完、前后不破坏规则、互不干扰、做完不丢。