PostgreSQL 支持哪些类型的索引?
PostgreSQL 拥有非常强大的索引系统,除了传统的 B-Tree 索引外,它还专门为不同的数据类型和查询场景(如全文检索、空间地理数据、JSON文档、海量时序数据)提供了多种高级索引类型。
PostgreSQL 原生支持以下 6 种基本索引类型:
1. B-Tree 索引 (B树索引)
这是 PostgreSQL 的 默认索引类型。当你执行 CREATE INDEX 而不指定类型时,默认创建的就是 B-Tree 索引。
- 适用场景:绝大多数常规查询。适合处理可以被排序的数据。
- 支持的操作符:
<、<=、=、>=、>。 - 特性:
- 支持范围查询(Range Query)和等值查询(Equality Query)。
- 支持
ORDER BY排序。 - 支持模式匹配查询(如
LIKE 'foo%',但不支持通配符开头的LIKE '%foo')。
2. Hash 索引 (哈希索引)
基于哈希表实现的索引。
- 适用场景:仅限于等值查询。
- 支持的操作符:
=。 - 特性:
- 在 PostgreSQL 10 之前,Hash 索引不记录在 WAL(预写式日志)中,因此崩溃后不安全,且不支持流复制。从 PG 10 开始,Hash 索引已被彻底修复,完全可靠。
- 对于极长的字符串进行等值比较时,Hash 索引的体积通常比 B-Tree 小,且速度可能略快;但在大多数日常场景下,B-Tree 依然是首选。
3. GiST 索引 (Generalized Search Tree - 通用搜索树)
GiST 不是一种单一的树,而是一个框架,允许开发者实现自定义的索引结构(如 R-Tree)。
- 适用场景:多维数据、空间/地理数据(如 PostGIS)、范围类型(Range types)、全文检索。
- 支持的操作符:根据数据类型不同而不同,常见包括
<<(在左侧)、&<(重叠)、~(包含)、@>(包含) 等。 - 特性:
- 非常擅长处理“相交”、“包含”等几何查询。
- 支持 K-NN(K-Nearest Neighbor,K近邻)搜索,例如“查找距离我当前坐标最近的 5 家餐厅”。
4. SP-GiST 索引 (Space-Partitioned GiST - 空间分区 GiST)
专为内部具有不对称性或重叠的数据结构设计,支持非平衡数据结构(如基数树 Radix Tree、四叉树 Quad-tree)。
- 适用场景:具有天然聚类特征但分布不均的数据。如:
- 长字符串但有共同前缀(如 URL、电话号码)。
- 多维空间中的点集合(如二维平面上的点分布)。
- 特性:在处理大量拥有相同前缀的数据时,无论在存储空间还是查询效率上都优于常规 B-Tree。
5. GIN 索引 (Generalized Inverted Index - 通用倒排索引)
类似于搜索引擎底层使用的倒排索引,专门用于处理包含多个元素的值。
- 适用场景:数组 (Arrays)、JSONB 文档、全文检索 (Full-text search)。
- 支持的操作符:
@>(包含)、<@(被包含)、&&(重叠)。 - 特性:
- 它是 PostgreSQL 处理 JSONB 数据的最强武器。如果你要查询 JSON 内部的某个键值对,GIN 是首选。
- 缺点:插入和更新非常慢(因为插入一行数据可能会导致索引中增加成百上千个条目),但查询极快。适合读多写少的场景。
6. BRIN 索引 (Block Range Index - 块范围索引)
专为海量数据表(如时序数据、物联网数据)设计。
- 适用场景:数据在物理磁盘上的存储顺序与数据本身的逻辑顺序(如时间戳)高度一致的超大表。
- 原理:它不为表中的每一行记录索引,而是将表划分为多个“块”(Block),并记录每个块中数据的最大值和最小值。
- 特性:
- 极度节省空间:索引体积可能只有 B-Tree 的千分之一甚至万分之一。
- 创建速度极快。
- 缺点:如果数据是随机插入、物理分布杂乱无章的,BRIN 索引将毫无用处。
💡 PostgreSQL 的高级索引特性
除了上述基本的索引类型,PostgreSQL 还支持以下非常实用的索引构建方式:
- 多列索引 (Multicolumn Indexes):
支持在一个索引中包含多个列。最多支持 32 列。 - 唯一索引 (Unique Indexes):
保证列中的数据唯一。CREATE UNIQUE INDEX ...(目前只有 B-Tree 原生支持唯一索引)。 - 表达式索引 (Expression Indexes / Function-Based Indexes):
不针对列本身,而是针对计算结果建索引。
例如:CREATE INDEX ON users (LOWER(email));,这样查询WHERE LOWER(email) = 'abc@test.com'时就能走索引。 - 部分索引 (Partial Indexes):
只为表中满足特定条件的行创建索引,极大地节省磁盘空间和提高效率。
例如:CREATE INDEX ON orders (order_id) WHERE status = 'unpaid';(只对未支付的订单建索引)。 - 覆盖索引 (Covering Indexes -
INCLUDE子句):
允许在索引的叶子节点中附加非索引列的数据,从而实现仅索引扫描 (Index-Only Scan),完全不需要回表查数据。
例如:CREATE INDEX ON users (id) INCLUDE (username);
📊 总结:如何选择合适的索引?
| 数据类型 / 查询场景 | 推荐索引类型 | 举例 |
|---|---|---|
| 常规的主键、外键、数字、字符串 | B-Tree | WHERE id = 123 或 WHERE age > 18 |
| 单纯的等值匹配,且字符串很长 | Hash 或 B-Tree | WHERE md5_hash = '...' |
| 地理位置、几何图形、范围重叠 | GiST | “这个多边形是否包含那个点?” |
| 全文检索、数组包含、JSONB 键值查询 | GIN | WHERE data @> '{"status": "active"}' |
| 超大数据量且按时间追加(日志/时序数据) | BRIN | WHERE created_at > '2023-01-01' |
| 带有公共前缀的文本(如 URL)、大量坐标点 | SP-GiST | 路由表匹配、大量二维坐标分析 |