Files
Hugo/content/blog/database-indexing.md
T

49 lines
2.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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。
**索引不是越多越好**——每个索引都会拖慢写入速度并占用磁盘空间。建索引前问自己:这个查询的执行频率值得一个索引吗?