PostgreSQL 常见索引类型有哪些?B-tree、Hash、GIN、GiST、BRIN 怎么选?
简化版
PostgreSQL 支持多种索引类型。B-tree 是默认索引,适合等值、范围、排序;Hash 适合等值查询;GIN 适合数组、JSONB、全文检索这类一个值包含多个元素的场景;GiST 是通用搜索树,常用于几何、范围、全文等扩展场景;BRIN 适合超大表且数据和物理存储顺序强相关的场景,比如按时间递增写入的日志表。
详细版
常见索引选择可以这样记:
| 索引类型 | 适合场景 | 注意点 |
|---|---|---|
| B-tree | 等值、范围、排序、唯一约束 | 默认选择,最常用 |
| Hash | 单列等值查询 | 不支持范围和排序 |
| GIN | JSONB、数组、全文检索 | 写入维护成本较高 |
| GiST | 几何、范围、相似搜索、全文检索 | 更偏通用框架 |
| BRIN | 大表、顺序相关数据 | 索引很小,但过滤精度较粗 |
面试回答不能只背名字,还要说明“查询条件是什么、数据分布如何、写入频率如何、是否需要排序、是否需要组合索引”。
完整版教学
一、B-tree 是默认且最常用的索引
PostgreSQL 默认创建的是 B-tree 索引:
CREATE INDEX idx_user_email ON users(email);
它适合:
=<、>、BETWEENORDER BYMIN、MAX- 唯一约束和主键
绝大多数普通业务字段,比如手机号、邮箱、订单号、创建时间,都优先考虑 B-tree。
二、Hash 索引主要服务等值查询
Hash 索引适合等值比较,例如:
SELECT * FROM users WHERE email = 'a@example.com';
但它不适合范围查询和排序。如果业务既有等值查询,又有排序或范围条件,B-tree 通常更通用。
实际项目中 Hash 索引使用频率没有 B-tree 高,面试时知道它的边界即可。
三、GIN 适合一个字段包含多个可检索元素
GIN 可以理解为倒排索引,适合“一个字段里有多个元素,需要查其中某个元素”的场景:
- 数组字段;
- JSONB 字段;
- 全文检索;
- 标签集合。
例如 JSONB 包含查询:
CREATE INDEX idx_doc_data_gin ON documents USING gin(data);
SELECT * FROM documents WHERE data @> '{"status":"paid"}';
GIN 的读取能力强,但维护成本往往比普通 B-tree 更高,写入频繁的大表要评估成本。
四、GiST 更像索引框架
GiST 是 Generalized Search Tree,适合一些 B-tree 不好表达的搜索问题,比如:
- 几何数据;
- 范围类型;
- 最近邻搜索;
- 特定扩展提供的相似度搜索;
- 全文检索。
GiST 不只是某一种单一数据结构,更像一套可扩展索引框架。回答时可以说:如果 B-tree 是常规排序树,GIN 是倒排思路,GiST 更偏通用搜索树框架。
五、BRIN 适合大表和顺序相关数据
BRIN 会记录一段数据块的摘要信息,而不是为每一行维护精确索引条目,所以索引非常小。
它适合:
- 日志表;
- 监控数据;
- 订单流水;
- 按时间递增写入的大表。
例如表按 created_at 递增插入,查询最近一天数据:
CREATE INDEX idx_log_created_at_brin ON logs USING brin(created_at);
BRIN 的优点是小而便宜,缺点是过滤不如 B-tree 精确。如果数据物理顺序和查询字段没有相关性,效果可能很差。
六、常见误区与追问
| 查询模式 | 优先考虑 | 不适合的选择 |
|---|---|---|
where id = ? order by created_at | B-tree 组合索引 | Hash 不能服务排序 |
jsonb @> ...、数组包含 | GIN | 普通 B-tree 不理解内部元素 |
| 超大时间序列表范围查 | BRIN 或 B-tree | 无相关性字段不适合 BRIN |
记忆钩子:索引类型不是按名字选,而是按“操作符 + 数据分布 + 写入成本”选。普通字段先想 B-tree,包含类查询再想 GIN,超大顺序表再想 BRIN。
举个数字例子:日志表 10 亿行按 created_at 递增写入,如果查最近 1 天且物理顺序和时间强相关,BRIN 只维护块范围摘要,索引可能比 B-tree 小很多;但如果 created_at 被乱序批量导入,某个时间范围散落在大量数据块里,BRIN 的过滤精度会下降,实际还要读很多无关块。
- 误区:PostgreSQL 有 Hash 索引,所以等值查询都该用 Hash。 B-tree 同样支持等值,并且还能支持范围、排序和唯一约束,实际更通用。
- 误区:GIN 适合所有 JSONB 查询。 GIN 适合包含、键存在等操作;固定路径等值过滤可能表达式索引更精准。
- 误区:BRIN 是 B-tree 的低成本替代品。 BRIN 依赖数据物理顺序和字段相关性,过滤粒度更粗,不适合随机分布字段。
- 追问:GiST 和 GIN 怎么区分? GIN 更像倒排索引,适合一个值包含多个可检索元素;GiST 是通用搜索树框架,常用于范围、几何、相似度等扩展场景。
- 追问:索引类型选错会怎样? 查询可能用不上索引,或者写入维护成本很高但收益很低,还会增加磁盘和缓存压力。
- 追问:为什么还要关注操作符? PostgreSQL 索引能否被使用和操作符类有关,同一个字段用不同操作符,可能需要不同索引或表达式。
七、加强记忆
PostgreSQL 索引类型可以按场景记:普通查询先想 B-tree,纯等值可以知道 Hash,数组和 JSONB 想 GIN,几何和范围扩展想 GiST,超大顺序表想 BRIN。索引不是越多越好,要同时考虑查询收益、写入成本、数据分布和维护代价。