PostgreSQL 索引

PostgreSQL 索引(Index) 是提升查询性能的关键数据库对象,通过在列上建立快速查找结构,大幅减少全表扫描(Sequential Scan),尤其在 WHEREJOINORDER BYGROUP BY 等操作中。


一、索引核心作用

操作无索引有索引
WHERE id = 100全表扫描索引查找(O(log n))
ORDER BY created_at排序开销大直接读取有序数据
JOIN 大表嵌套循环慢高效合并

二、PostgreSQL 支持的索引类型

类型用途语法
B-tree(默认)=, >, <, >=, <=, BETWEEN, IN, IS NULLCREATE INDEX ON table(col);
Hash仅支持 =CREATE INDEX ON table USING HASH(col);
GiST几何、全文搜索、范围类型CREATE INDEX ON table USING GIST(col);
SP-GiST分区搜索(如电话号码、IP)
GIN数组、jsonb、全文搜索CREATE INDEX ON table USING GIN(col);
BRIN超大表、按物理顺序存储CREATE INDEX ON table USING BRIN(col);

三、创建索引基本语法

CREATE [UNIQUE] INDEX [CONCURRENTLY] index_name
    ON table_name [USING method]
    (column_name [ASC|DESC] [NULLS FIRST|LAST], ...);

示例:

-- 1. 单列 B-tree 索引(默认)
CREATE INDEX idx_users_email ON users(email);

-- 2. 复合索引(多列)
CREATE INDEX idx_orders_user_status ON orders(user_id, status);

-- 3. 唯一索引
CREATE UNIQUE INDEX idx_users_email_unique ON users(lower(email));

-- 4. 表达式索引
CREATE INDEX idx_users_lower_name ON users(lower(name));

-- 5. 部分索引(WHERE 条件)
CREATE INDEX idx_orders_active ON orders(user_id) WHERE status = 'active';

-- 6. GIN 索引(用于 jsonb 或数组)
CREATE INDEX idx_products_tags ON products USING GIN(tags);

-- 7. 并发创建索引(不锁表,生产推荐)
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);

四、索引使用场景与最佳实践

场景推荐索引
主键自动创建 UNIQUE B-tree
外键建议建索引(JOIN 加速)
频繁 WHERE 过滤列B-tree
LIKE 'prefix%'B-tree 支持
LIKE '%suffix'不支持 → 用 pg_trgm + GIN
ORDER BY colB-tree(可避免排序)
jsonb 字段查询GIN
全文搜索GIN + tsvector
超大表(亿级)+ 按时间顺序BRIN

五、索引分析与优化

1. 查看执行计划(推荐)

EXPLAIN ANALYZE
SELECT * FROM users WHERE email = 'alice@example.com';
  • Index Scan using idx_users_email → 使用了索引
  • Seq Scan → 没用索引,需优化

2. 查看表所有索引

-- 方式1:\d+ 在 psql 中
\d+ users

-- 方式2:查询系统表
SELECT 
    indexname, 
    indexdef 
FROM pg_indexes 
WHERE tablename = 'users';

3. 查看索引是否被使用

SELECT * FROM pg_stat_user_indexes WHERE relname = 'users';

六、索引维护

操作命令
删除索引DROP INDEX index_name;
重建索引REINDEX INDEX index_name;
并发重建REINDEX INDEX CONCURRENTLY index_name;
查看索引大小“`sql

七、常见索引陷阱

问题原因解决
索引未使用选择性低(如 gender)避免低选择性列建索引
LIKE '%abc'不支持 B-treepg_trgm 扩展 + GIN
函数包裹列WHERE lower(email) = 'a'建表达式索引
频繁更新列索引维护开销大权衡读写频率
索引膨胀大量 UPDATE/DELETE定期 REINDEXVACUUM

八、实战示例:电商订单查询优化

-- 表结构
CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    user_id INT,
    status VARCHAR(20),
    created_at TIMESTAMP,
    total_amount DECIMAL
);

-- 常见查询
SELECT * FROM orders 
WHERE user_id = 123 
  AND status = 'completed' 
  AND created_at >= '2025-01-01'
ORDER BY created_at DESC;

推荐索引:

-- 复合索引(顺序重要!)
CREATE INDEX idx_orders_query 
ON orders(user_id, status, created_at DESC);

-- 或部分索引(如果 status 很少变)
CREATE INDEX idx_orders_completed 
ON orders(user_id, created_at DESC) 
WHERE status = 'completed';

索引列顺序原则WHERE 中等号 → WHERE 中范围 → ORDER BY


九、GIN 索引:数组与 JSONB

-- 数组列
ALTER TABLE products ADD COLUMN tags TEXT[];
CREATE INDEX idx_products_tags ON products USING GIN(tags);

SELECT * FROM products WHERE tags @> ARRAY['electronics'];

-- JSONB 列
ALTER TABLE users ADD COLUMN profile JSONB;
CREATE INDEX idx_users_profile ON users USING GIN(profile);

SELECT * FROM users WHERE profile -> 'settings' ->> 'theme' = 'dark';

十、BRIN 索引:海量数据神器

适合 按插入顺序递增 的列(如 created_at),索引极小。

CREATE TABLE logs (
    id BIGSERIAL,
    created_at TIMESTAMP DEFAULT NOW(),
    message TEXT
);

CREATE INDEX idx_logs_created_at_brin 
ON logs USING BRIN(created_at);

-- 查询最近一天
SELECT * FROM logs WHERE created_at >= NOW() - INTERVAL '1 day';

BRIN 索引大小 ≈ 几 KB(即使表 100GB)


总结:索引选择表

场景推荐索引
等值查询B-tree
范围查询B-tree
排序B-tree(带方向)
全文搜索GIN + tsvector
数组包含GIN
JSONB 嵌套查询GIN
超大表 + 时间序列BRIN
几何数据GiST

常用命令速查

-- 创建索引
CREATE INDEX idx_name ON table(col);

-- 并发创建(生产)
CREATE INDEX CONCURRENTLY idx_name ON table(col);

-- 删除索引
DROP INDEX idx_name;

-- 查看索引
\d+ table_name

-- 查看索引大小
SELECT pg_size_pretty(pg_total_relation_size('idx_name'));

-- 强制使用索引(调试)
SET enable_seqscan = OFF;

需要我提供一个 完整的索引优化案例(含建表 + 数据 + 查询 + EXPLAIN 对比) 吗?

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注