什么是数据库索引?
数据库索引(Index)就像书籍的索引目录。查找特定词汇时,不需要从头翻阅每一页,只需查看索引页。同样,数据库索引是一种数据结构,能在不扫描所有行的情况下快速找到目标数据行。
没有索引时,数据库必须执行**全表扫描(Full Table Scan)**来查找满足条件的数据。如果有100万行,最差情况下需要100万次比较。创建合适的索引可以将其减少到几十到几百次。
B-Tree索引的原理
大多数关系型数据库(PostgreSQL、MySQL、Oracle)使用**B-Tree(平衡树)**作为默认索引结构。
B-Tree特性:
- 所有叶节点位于同一深度(保持平衡)
- 每个节点包含有序键值
- 基于键值进行二分查找
- O(log n) 的时间复杂度
即使有1000万条数据,B-Tree高度也仅约24层,意味着最多24次比较即可找到目标数据。
索引创建基本语法
-- 单列索引
CREATE INDEX idx_users_email ON users(email);
-- 唯一索引
CREATE UNIQUE INDEX idx_users_email_unique ON users(email);
-- 复合索引
CREATE INDEX idx_orders_user_date ON orders(user_id, created_at);
-- 删除索引
DROP INDEX idx_users_email;
-- 查看索引列表(PostgreSQL)
SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'users';
复合索引与列顺序
复合索引将多列合并为一个索引。列的顺序对性能有决定性影响,这称为最左前缀规则。
CREATE INDEX idx_orders_composite ON orders(user_id, created_at, status);
-- 可以使用索引(按从左到右顺序使用条件)
SELECT * FROM orders WHERE user_id = 1;
SELECT * FROM orders WHERE user_id = 1 AND created_at > '2026-01-01';
-- 无法使用索引(缺少第一列条件)
SELECT * FROM orders WHERE created_at > '2026-01-01';
基数(Cardinality)与选择性
基数指列中唯一值的数量,是决定索引效果的关键因素。
| 列 | 值示例 | 基数 | 索引效果 |
|---|---|---|---|
| user_id | UUID | 非常高 | 优秀 |
| 邮箱地址 | 高 | 良好 | |
| status | paid/pending/failed | 低(3个值) | 有限 |
| gender | M/F | 极低 | 几乎无效 |
覆盖索引
当查询所需的所有列都包含在索引中时,数据库无需访问主表即可直接返回结果。
CREATE INDEX idx_users_covering ON users(id, name, email);
-- 此查询仅通过索引即可处理
SELECT id, name, email FROM users WHERE id = 123;
-- PostgreSQL的INCLUDE子句
CREATE INDEX idx_orders_user ON orders(user_id) INCLUDE (total_amount, status);
使用EXPLAIN验证索引效果
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 1;
-- Index Scan → 使用了索引
-- Seq Scan → 全表扫描(未使用索引)
索引设计原则
- 优先为WHERE子句的列创建索引
- JOIN条件(外键)必须创建索引
- 经常用于ORDER BY的列
- 复合索引中将基数高的列放在前面
- 索引越多,INSERT/UPDATE/DELETE速度越慢
总结
索引是数据库性能优化的核心工具,但并非越多越好。需要分析实际查询的执行计划,在关键位置战略性地创建索引。使用Erdmates设计表结构的同时,制定最优的索引策略。