1. 索引创建基础语法
PostgreSQL 默认使用B-tree索引,通过CREATE INDEX命令创建;其他索引类型需通过USING关键字显式指定,通用语法如下:
-- 默认创建 B-tree 索引 CREATE INDEX 索引名 ON 表名 (列名); -- 创建指定类型的索引 CREATE INDEX 索引名 ON 表名 USING 索引类型 (列名); -- 示例:创建 Hash 索引 CREATE INDEX idx_user_id ON users USING HASH (user_id);
2. 核心索引类型及实操样例
2.1 B-tree 索引
2.1.1 核心特性
B-tree(平衡树)是 PostgreSQL 默认索引类型,适用于可排序数据的等值、范围查询,支持排序操作,是最通用的索引类型。
2.1.2 支持的操作符
- 基础比较:<(小于)、<=(小于等于)、=(等于)、>=(大于等于)、>(大于)
- 组合条件:BETWEEN(介于两值之间)、IN(匹配列表中任一值)
- 空值判断:IS NULL(为空)、IS NOT NULL(不为空)
- 模式匹配(有限支持):
- LIKE 'foo%'(模糊匹配以foo开头的字符串)、~ '^foo'(正则匹配以foo开头的字符串)
- ILIKE(不区分大小写的模糊匹配)、~*(不区分大小写的正则匹配),仅模式以非字母开头时生效
2.1.3 实操样例
步骤1:创建测试表
-- 创建用户表 CREATE TABLE users ( id SERIAL PRIMARY KEY, username VARCHAR(50) NOT NULL, age INT, register_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 插入测试数据 INSERT INTO users (username, age) VALUES ('zhangsan', 25), ('lisi', 30), ('wangwu', 28), ('zhaoliu', 35);
步骤2:创建 B-tree 索引
-- 为 age 列创建 B-tree 索引(默认类型,可省略 USING BTREE) CREATE INDEX idx_users_age ON users (age); -- 为 register_time 列创建 B-tree 索引(支持范围/排序) CREATE INDEX idx_users_register_time ON users (register_time);
步骤3:索引生效的查询场景
-- 等值查询(命中 idx_users_age) SELECT * FROM users WHERE age = 30; -- 范围查询(命中 idx_users_age) SELECT * FROM users WHERE age BETWEEN 25 AND 30; -- 排序查询(命中 idx_users_register_time) SELECT * FROM users ORDER BY register_time DESC LIMIT 2; -- 模式匹配(前缀匹配,命中 idx_users_username) CREATE INDEX idx_users_username ON users (username); SELECT * FROM users WHERE username LIKE 'zhang%';
2.2 Hash 索引
2.2.1 核心特性
存储索引列的 32 位哈希值,仅支持简单等值比较,功能单一但等值查询效率高,适用场景有限。
2.2.2 支持的操作符
仅等值判断:=
2.2.3 实操样例
步骤1:创建 Hash 索引
-- 为 username 列创建 Hash 索引(仅支持等值) CREATE INDEX idx_users_username_hash ON users USING HASH (username);
步骤2:索引生效的查询场景
-- 纯等值查询(命中 Hash 索引) SELECT * FROM users WHERE username = 'lisi'; -- 注意:以下场景 Hash 索引不生效,会走全表扫描 SELECT * FROM users WHERE username LIKE 'li%'; -- 范围/模糊查询不支持 SELECT * FROM users WHERE age = 28; -- 未创建 Hash 索引的列
2.3 GiST 索引
2.3.1 核心特性
GiST(Generalized Search Tree)并非单一索引,而是通用索引框架,可实现多种索引策略,支持复杂数据类型(如空间几何、全文检索)和“最近邻”搜索。
2.3.2 支持的操作符(以二维几何类型为例)
空间关系:<<(左侧)、&<(重叠左侧)、&>(重叠右侧)、>>(右侧)、<<|(下侧)、&<|(重叠下侧)、|&>(重叠上侧)、|>>(上侧)、@>(包含)、<@(被包含)、~=(相等)、&&(相交);最近邻搜索:<->(距离运算符,用于按距离排序)
2.3.3 实操样例(空间数据场景)
步骤1:启用 PostGIS 扩展(需先安装)
CREATE EXTENSION IF NOT EXISTS postgis;
步骤2:创建空间数据表并插入数据
-- 创建地点表(包含地理坐标) CREATE TABLE places ( id SERIAL PRIMARY KEY, name VARCHAR(100), location GEOGRAPHY(POINT) -- 地理坐标类型 ); -- 插入测试地点(纬度、经度) INSERT INTO places (name, location) VALUES ('公园', ST_SetSRID(ST_MakePoint(116.40, 39.90), 4326)), ('商场', ST_SetSRID(ST_MakePoint(116.41, 39.91), 4326)), ('学校', ST_SetSRID(ST_MakePoint(116.39, 39.89), 4326));
步骤3:创建 GiST 索引
-- 为地理坐标列创建 GiST 索引 CREATE INDEX idx_places_location ON places USING GIST (location);
步骤4:索引生效的查询场景
-- 1. 空间包含查询(查找指定区域内的地点) SELECT * FROM places WHERE ST_DWithin(location, ST_SetSRID(ST_MakePoint(116.40, 39.90), 4326), 1000); -- 1000 米范围内 -- 2. 最近邻搜索(查找离指定坐标最近的 2 个地点) SELECT * FROM places ORDER BY location <-> ST_SetSRID(ST_MakePoint(116.40, 39.90), 4326) LIMIT 2;
2.4 SP-GiST 索引
2.4.1 核心特性
SP-GiST(Space-Partitioned Generalized Search Tree)是另一种通用索引框架,支持非平衡磁盘数据结构(四叉树、k-d 树、基数树等),适配空间分区类查询,轻量化高效。
2.4.2 支持的操作符(以二维点类型为例)
空间关系:<<(左侧)、>>(右侧)、~=(相等)、<@(被包含)、<<|(下侧)、|>>(上侧);最近邻搜索:支持(依赖具体操作符类,常用<->距离运算符)
2.4.3 实操样例(二维点数据)
步骤1:创建二维点表并插入数据
-- 创建二维点表 CREATE TABLE points ( id SERIAL PRIMARY KEY, pos POINT -- 二维点类型 ); -- 插入测试数据 INSERT INTO points (pos) VALUES ('(1,2)'), ('(3,4)'), ('(5,6)'), ('(7,8)');
步骤2:创建 SP-GiST 索引
-- 为 pos 列创建 SP-GiST 索引 CREATE INDEX idx_points_pos ON points USING SP-GiST (pos);
步骤3:索引生效的查询场景
-- 1. 空间范围查询(查找 x 轴小于 5 的点) SELECT * FROM points WHERE pos << '(5,5)'; -- << 表示左侧(x 更小) -- 2. 最近邻搜索(查找离 (4,4) 最近的点) SELECT * FROM points ORDER BY pos <-> '(4,4)' LIMIT 1;
2.5 GIN 索引
2.5.1 核心特性
GIN(Generalized Inverted Index,倒排索引)专为多值数据类型设计,为每个组成值建立独立索引项,高效支持“包含/存在”类查询,是数组、JSONB、全文检索的首选索引。
2.5.2 支持的操作符(以数组类型为例)
包含/存在:<@(被包含,如数组A <@ 数组B表示A是B的子集)、@>(包含,如数组A @> 数组B表示B是A的子集)、=(相等)、&&(相交,两数组有共同元素)
2.5.3 实操样例(数组/JSONB 场景)
场景1:数组查询
-- 1. 创建带数组列的表 CREATE TABLE articles ( id SERIAL PRIMARY KEY, title VARCHAR(100), tags TEXT[] -- 标签数组 ); -- 2. 插入测试数据 INSERT INTO articles (title, tags) VALUES ('PostgreSQL 索引', ARRAY['postgres', '数据库', '索引']), ('Python 教程', ARRAY['python', '编程', '教程']), ('GIS 空间分析', ARRAY['gis', 'postgres', '空间数据']); -- 3. 创建 GIN 索引 CREATE INDEX idx_articles_tags ON articles USING GIN (tags); -- 4. 索引生效的查询(包含指定标签) SELECT * FROM articles WHERE tags @> ARRAY['postgres']; -- 包含 postgres 标签 SELECT * FROM articles WHERE tags && ARRAY['数据库', '编程']; -- 交集(包含任一)
场景2:JSONB 查询
-- 1. 创建带 JSONB 列的表 CREATE TABLE products ( id SERIAL PRIMARY KEY, info JSONB -- 产品信息(JSONB 类型) ); -- 2. 插入测试数据 INSERT INTO products (info) VALUES ('{"name": "手机", "price": 2999, "tags": ["数码", "通讯"]}'), ('{"name": "电脑", "price": 5999, "tags": ["数码", "办公"]}'); -- 3. 创建 GIN 索引 CREATE INDEX idx_products_info ON products USING GIN (info); -- 4. 索引生效的查询(JSONB 包含键/值) SELECT * FROM products WHERE info @> '{"tags": ["数码"]}'; -- 包含数码标签 SELECT * FROM products WHERE info ->> 'name' = '手机'; -- 等值查询(需结合操作符)
2.6 BRIN 索引
2.6.1 核心特性
BRIN(Block Range Index,块范围索引)存储表物理块范围的值摘要信息(最小值/最大值),占用空间极小,适配大数据量且物理顺序与逻辑顺序高度相关的场景(如时序日志表)。
2.6.2 支持的操作符(线性排序类型)
基础比较:<(小于)、<=(小于等于)、=(等于)、>=(大于等于)、>(大于)
2.6.3 实操样例(时序日志表)
步骤1:创建日志表并插入有序数据
-- 创建访问日志表(按时间有序插入) CREATE TABLE access_logs ( id SERIAL, user_id INT, access_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, ip VARCHAR(20) ); -- 插入 10 万条测试数据(模拟时间有序的日志) INSERT INTO access_logs (user_id, ip) SELECT floor(random() * 1000)::INT, '192.168.' || floor(random() * 255)::INT || '.' || floor(random() * 255)::INT FROM generate_series(1, 100000);
步骤2:创建 BRIN 索引
-- 为 access_time 列创建 BRIN 索引(物理有序,效率高) CREATE INDEX idx_access_logs_time ON access_logs USING BRIN (access_time);
步骤3:索引生效的查询场景
-- 范围查询(命中 BRIN 索引,快速定位块范围) SELECT COUNT(*) FROM access_logs WHERE access_time BETWEEN '2026-01-01 00:00:00' AND '2026-01-01 12:00:00'; -- 注意:若数据物理无序,BRIN 索引会失效,优先选 B-tree
2.7 扩展索引:Bloom
2.7.1 核心特性
Bloom 是 PostgreSQL 扩展提供的索引类型,基于布隆过滤器实现,适用于多列等值查询,可大幅减少多列组合索引的存储空间,但存在假阳性(需回表验证)。
2.7.2 实操样例
步骤1:启用 bloom 扩展
CREATE EXTENSION IF NOT EXISTS bloom;
步骤2:创建 Bloom 索引
-- 为 user_id 和 ip 多列创建 Bloom 索引 CREATE INDEX idx_access_logs_bloom ON access_logs USING bloom (user_id, ip) WITH (length=80, col1=2, col2=4); -- length:索引长度;colN:各列的位数
步骤3:索引生效的查询场景
-- 多列等值查询(命中 Bloom 索引) SELECT * FROM access_logs WHERE user_id = 100 AND ip = '192.168.10.20';
2.7.3 Bloom 索引 vs 传统多列组合索引(B-tree)
在多列等值查询场景中,Bloom 索引和 B-tree 组合索引是两种常见方案,但设计理念、性能、适用场景差异显著,以下是详细对比:
1. 核心原理差异
| 维度 | Bloom 索引 | B-tree 组合索引 |
|---|---|---|
| 存储结构 | 基于布隆过滤器,存储列的哈希值位图,不存储原始数据 | 基于平衡树,按列的组合顺序存储原始值(如 (col1, col2)) |
| 存储空间 | 极小(MB 级),仅存储哈希位图,与表行数无关 | 较大(GB 级),需存储所有列的原始值,随行数线性增长 |
| 索引精度 | 存在假阳性(可能匹配到不存在的数据,需回表验证) | 精准匹配,无假阳性 |
2. 支持的查询场景
| 查询类型 | Bloom 索引 | B-tree 组合索引 |
|---|---|---|
| 多列全等值查询 | 支持(如col1 = ? AND col2 = ?) | 支持(且精准) |
| 前缀列查询 | 不支持(如仅查col1 = ?无法使用) | 支持(组合索引的核心优势,如 (col1, col2) 支持仅查 col1) |
| 范围查询 | 不支持(仅等值) | 支持(如col1 = ? AND col2 > ?) |
| 排序/分组 | 不支持 | 支持(按组合列排序) |
3. 实操对比样例
access_logs前提:复用前文 表(10 万条数据)
-- 场景1:创建 B-tree 组合索引(col1=user_id, col2=ip) CREATE INDEX idx_access_logs_btree ON access_logs (user_id, ip); -- 场景2:创建 Bloom 索引 CREATE INDEX idx_access_logs_bloom ON access_logs USING bloom (user_id, ip) WITH (length=80, col1=2, col2=4); -- 对比1:多列全等值查询 -- Bloom 索引生效(需回表验证假阳性) EXPLAIN ANALYZE SELECT * FROM access_logs WHERE user_id = 100 AND ip = '192.168.10.20'; -- B-tree 组合索引生效(精准匹配,无需回表) EXPLAIN ANALYZE SELECT * FROM access_logs WHERE user_id = 100 AND ip = '192.168.10.20'; -- 对比2:仅前缀列查询(Bloom 失效,B-tree 生效) -- Bloom 索引不生效(仅查 user_id),走全表扫描 EXPLAIN ANALYZE SELECT * FROM access_logs WHERE user_id = 100; -- B-tree 组合索引生效(前缀列匹配) EXPLAIN ANALYZE SELECT * FROM access_logs WHERE user_id = 100; -- 对比3:范围查询(Bloom 失效,B-tree 生效) -- Bloom 索引不生效(ip 用范围) EXPLAIN ANALYZE SELECT * FROM access_logs WHERE user_id = 100 AND ip LIKE '192.168.10.%'; -- B-tree 组合索引生效(前缀列等值 + 后缀列范围) EXPLAIN ANALYZE SELECT * FROM access_logs WHERE user_id = 100 AND ip LIKE '192.168.10.%';
4. 适用场景选择
| 选 Bloom 索引 | 选 B-tree 组合索引 |
|---|---|
| 多列全等值查询为主 | 需要支持前缀列查询、范围查询、排序 |
| 表数据量极大(千万/亿级),追求极小存储成本 | 数据量中等,追求查询精准度和多功能性 |
| 可接受少量假阳性(回表验证的性能损耗) | 对查询精度要求高,不接受假阳性 |
| 多列无明显“前缀优先级”(无单列表查询) | 有明确的前缀列查询需求(如仅查 user_id) |
3. 索引类型选择速查表
| 索引类型 | 核心优势 | 典型适用场景 | 核心样例语句 | 备注 |
|---|---|---|---|---|
| B-tree | 通用、支持排序/范围 | 常规等值/范围查询 | CREATE INDEX idx ON tbl (col); | 默认选择,覆盖 80%+ 场景 |
| Hash | 等值查询效率高 | 纯等值查询 | CREATE INDEX idx ON tbl USING HASH (col); | 功能单一,适用场景有限 |
| GiST | 复杂数据、最近邻 | 空间数据、全文检索 | CREATE INDEX idx ON tbl USING GIST (geo_col); | 需配合扩展(如 PostGIS) |
| SP-GiST | 空间分区、轻量化 | 二维点/多维数据 | CREATE INDEX idx ON tbl USING SP-GiST (pos_col); | 非平衡结构,适配分区查询 |
| GIN | 多值数据、倒排索引 | 数组、JSONB、全文检索 | CREATE INDEX idx ON tbl USING GIN (array_col); | 多值数据首选索引 |
| BRIN | 低存储、大数据量有序表 | 日志表、时序数据 | CREATE INDEX idx ON tbl USING BRIN (time_col); | 仅适用于数据物理有序场景 |
| Bloom | 多列等值、低存储 | 多列组合等值查询(无前缀/范围需求) | CREATE INDEX idx ON tbl USING bloom (col1, col2); | 对比 B-tree 组合索引:存储小但功能单一,有假阳性 |
4. 总结
- 优先选 B-tree:无特殊需求时,B-tree 可覆盖 80% 以上的常规查询场景,是默认且最通用的选择。
- 专用场景选专用索引:处理空间数据用 GiST/SP-GiST,多值数据(数组/JSONB)用 GIN,大数据量有序表用 BRIN。
- 多列查询选对索引:需支持前缀列、范围查询 → 选 B-tree 组合索引;仅多列全等值查询、追求低存储 → 选 Bloom 索引(接受假阳性)。
- 样例可直接复用:文档中所有 SQL 样例均基于 PostgreSQL 14+ 编写,启用扩展(如 postgis、bloom)后可直接执行,便于快速验证索引效果。