49 lines
2.0 KiB
Markdown
49 lines
2.0 KiB
Markdown
---
|
||
title: "数据库索引优化实战"
|
||
date: 2026-05-10T16:00:00+08:00
|
||
draft: false
|
||
description: "慢查询分析、联合索引的最左前缀原则、覆盖索引与回表的性能差异。几个真实 SQL 优化案例,从几秒降到毫秒级。"
|
||
tags:
|
||
- 数据库
|
||
- SQL
|
||
- 性能
|
||
---
|
||
|
||
`EXPLAIN` 是 DBA 的眼睛。看 `type` 字段从 `ALL` 变成 `ref` 是最有成就感的事。
|
||
|
||
## 从慢查询开始
|
||
|
||
一切优化从定位问题开始。打开 MySQL 的慢查询日志(`slow_query_log`),设置 `long_query_time = 0.1`,跑一段时间后分析。PostgreSQL 用 `pg_stat_statements` 扩展,同样有效。
|
||
|
||
拿到慢查询,先 `EXPLAIN` 看执行计划。关注几个关键字段:
|
||
|
||
| 字段 | 含义 | 目标 |
|
||
|------|------|------|
|
||
| type | 访问类型 | 至少到 range,最好到 ref/const |
|
||
| key | 使用的索引 | 不为 NULL |
|
||
| rows | 扫描行数 | 越小越好 |
|
||
| Extra | 额外信息 | 避免 Using filesort / Using temporary |
|
||
|
||
## 最左前缀原则
|
||
|
||
联合索引是最容易出错的地方。假设索引是 `(a, b, c)`:
|
||
|
||
- `WHERE a = 1` ✅ 用到索引
|
||
- `WHERE a = 1 AND b = 2` ✅ 用到索引
|
||
- `WHERE b = 2` ❌ 用不到
|
||
- `WHERE a = 1 AND c = 3` ⚠️ 只用到了 a 列
|
||
|
||
排序也受这个规则约束。`ORDER BY a, b` 没问题,`ORDER BY b, c` 就需要额外排序。
|
||
|
||
## 覆盖索引
|
||
|
||
如果查询需要的所有列都在索引里,就不需要回表取数据。`Extra` 列显示 `Using index` 就是覆盖索引。对一个高频查询,覆盖索引能减少大量随机 I/O。
|
||
|
||
代价是索引变大了。不要为了覆盖索引把所有列都塞进索引——算一笔写入性能和存储空间的账。
|
||
|
||
## 真实案例
|
||
|
||
某个订单列表查询,原始 SQL 跑了 8 秒。`EXPLAIN` 一看,`type: ALL`,全表扫描 200 万行。加了 `(user_id, created_at)` 联合索引后,`type: ref`,扫描 20 行,耗时 12ms。
|
||
|
||
**索引不是越多越好**——每个索引都会拖慢写入速度并占用磁盘空间。建索引前问自己:这个查询的执行频率值得一个索引吗?
|