2-3.pg核心功能之索引特色

PostgreSQL 索引特色功能

函数索引

与 Oracle 数据库类似,PostgreSQL 也支持函数索引。实际上,PostgreSQL 索引的键除了可以是一个函数外,还可以是从一个或多个字段计算出来的标量表达式(MySQL 8.0 也支持部分函数索引)。

问题场景

经常需要对一个表的字段做大小写无关比较时,常用的方法是使用 lower 函数:

sql 复制代码
SELECT * FROM mytest WHERE lower(note) = 'hello world';

但因为使用了函数,无法利用到 note 字段上的普通索引。

示例

sql 复制代码
CREATE TABLE students (
    id SERIAL PRIMARY KEY,
    name VARCHAR(50),
    age INTEGER
);

INSERT INTO students VALUES (2, 'hello world', 19);
INSERT INTO students VALUES (3, 'hello world11', 13);
INSERT INTO students VALUES (4, 'hello awardld11', 20);

-- 创建函数索引
CREATE INDEX mytest_lower_name_idx ON students (lower(name));

-- 查看执行计划
EXPLAIN SELECT * FROM students WHERE lower(name) = 'hello world';

部分索引

部分索引只对一个表中的部分行进行索引,由条件表达式(谓词)筛选出这部分行。

案例1:IP 访问系统

  • 内网 IP 固定、访问量大,外网 IP 多样。
  • 需求:经常查询外网访问记录。
建表与索引
sql 复制代码
CREATE TABLE access_log (
    url VARCHAR,
    client_ip inet
);

-- 部分索引:排除内网 IP
CREATE INDEX access_log_client_ip_ix ON access_log (client_ip)
WHERE NOT (client_ip > inet '192.168.100.0' AND client_ip < inet '192.168.100.255');
查询示例
sql 复制代码
-- 外网 IP 查询,可以使用部分索引
SELECT * FROM access_log WHERE client_ip = inet '114.113.220.27';

-- 内网 IP 查询,不会走部分索引
SELECT * FROM access_log WHERE client_ip = inet '192.168.100.55';

案例2:排除不感兴趣的数值

假设订单表中未付款订单占少数且频繁查询,可以创建部分索引优化。

sql 复制代码
CREATE TABLE orders (
    order_nr INT,
    amount DECIMAL(12,2),
    billed BOOLEAN
);

-- 创建部分索引
CREATE INDEX orders_unbilled_index ON orders (order_nr) WHERE billed IS NOT TRUE;
插入数据
sql 复制代码
INSERT INTO orders 
SELECT t.seq, t.seq + 2.44, TRUE 
FROM generate_series(1, 500000) AS t(seq);

INSERT INTO orders 
SELECT t.seq, t.seq + 2.44, FALSE 
FROM generate_series(500001, 1000000) AS t(seq);
查询示例
sql 复制代码
-- 使用索引
EXPLAIN SELECT * FROM orders WHERE billed IS NOT TRUE AND order_nr < 10000;

-- 不使用索引
EXPLAIN SELECT * FROM orders WHERE billed IS TRUE AND order_nr < 10000;

-- 也可用于不涉及索引键的查询
EXPLAIN SELECT * FROM orders WHERE billed IS NOT TRUE AND amount > 40105960.00;
注意事项

谓词必须与查询条件完全匹配,系统无法识别数学上等价但形式不同的谓词。

sql 复制代码
-- 以下查询不会使用索引
EXPLAIN SELECT * FROM orders WHERE (NOT billed) AND order_nr < 10000;

案例3:部分唯一索引

sql 复制代码
CREATE TABLE tests (
    subject TEXT,
    target TEXT,
    success BOOLEAN
);

CREATE UNIQUE INDEX tests_success_constraint ON tests (subject, target) WHERE success;

GiST 索引

GiST 是 Generalized Search Trees 的缩写,是一种平衡树结构的访问方法,是用户建立自定义索引的基础模板。

特点

  • 底层为平衡树结构。
  • 支持 @>&& 等复杂运算符,而 BTree 只支持 <=>
  • 用户只需实现一组回调函数即可自定义索引。

支持 GiST 的操作函数

  • consistent:判断索引项是否与查询相容
  • union:组合多个索引项
  • compress / decompress:压缩与解压缩索引项
  • penalty:计算插入代价
  • picksplit:决定索引分裂策略
  • same:判断两个索引项是否相等
  • distance:计算索引项与查询值之间的距离
  • fetch:将压缩索引项转换为原始数据项

案例:Point 类型 GiST 索引

sql 复制代码
CREATE TABLE pts (id INT, p POINT);
INSERT INTO pts 
SELECT t.d, point(ceil(random() * 1000), ceil(random() * 1000))
FROM generate_series(1, 1000000) AS t(d);

-- 无索引查询
EXPLAIN ANALYZE SELECT * FROM pts WHERE circle '((100,100) 100)' @> p;

-- 创建 GiST 索引
CREATE INDEX ON pts USING gist(p);

-- 再次查看执行计划
EXPLAIN ANALYZE SELECT * FROM pts WHERE circle '((100,100) 100)' @> p;

案例:inet 类型 GiST 索引

sql 复制代码
CREATE TABLE vector (id INT, ip INET);
INSERT INTO vector 
SELECT t.d, inet(ceil(random() * 255) || '.' || ceil(random() * 255) || '.' || ceil(random() * 255) || '.' || ceil(random() * 255))
FROM generate_series(1, 1000000) AS t(d);

-- BTree 索引
CREATE INDEX vector_btree_inx ON vector USING btree(ip);
EXPLAIN ANALYZE SELECT * FROM vector WHERE ip = '77.80.250.123'::inet;

-- GiST 索引
CREATE INDEX vector_gist_inx ON vector USING gist(ip inet_ops);
EXPLAIN ANALYZE SELECT * FROM vector WHERE ip = '77.80.250.123'::inet;

-- 子网查询(BTree 不支持 << 操作符)
EXPLAIN ANALYZE SELECT * FROM vector WHERE ip << '192.0.2.0/24';

总结:GiST 查询效率与 BTree 相当,但支持更多复杂运算符。


SP-GiST 索引

SP-GiST 表示空间分区 GiST,适用于可递归分割为非相交区域的结构(如四叉树、k-d 树、基数树)。

创建方法

sql 复制代码
CREATE TABLE test01 (p POINT);
CREATE INDEX ON test01 USING spgist (p);

GIN 索引

GIN 主要用于加速全文搜索。

案例:全文搜索

sql 复制代码
CREATE TABLE ts (doc TEXT, doc_tsv TSVECTOR);

INSERT INTO ts(doc) VALUES
('Can a sheet slitter slit sheets?'),
('How many sheets could a sheet slitter slit?'),
('I slit a sheet, a sheet I slit.'),
('Upon a slitted sheet I slit.'),
('Whoever slit the sheets is a good sheet slitter.'),
('I am a sheet slitter.'),
('I slit sheets.'),
('I am the sleekest sheet slitter that ever slit sheets.'),
('She slits the sheet she sits on.');

-- 更新 TSVECTOR 列
UPDATE ts SET doc_tsv = to_tsvector(doc);

-- 创建 GIN 索引
CREATE INDEX ON ts USING gin(doc_tsv);

-- 禁用全表扫描
SET enable_seqscan TO off;

-- 全文搜索
EXPLAIN ANALYZE SELECT doc FROM ts WHERE doc_tsv @@ to_tsquery('many & slitter');

GIN 索引优缺点

优点

  • 适合模糊查询、正则查询、全文搜索。

缺点

  • 插入/更新效率低。
  • 建议批量插入时先删除索引,插入后再重建。
  • 增大 maintenance_work_mem 可加速索引创建。

BRIN 索引

BRIN 是 Block Range Index 的缩写,将相邻磁盘块组合,计算每组的取值范围,适合数值线性增长、删除较少的列。

特点

  • 按数据块分组(默认每组 128 页)。
  • 查找时先排除范围外的分组,再用位图扫描。
  • 空间占用小,性能低于 BTree。

示例

sql 复制代码
CREATE TABLE test01 (id INT, info TEXT);
CREATE INDEX idx_test01_id ON test01 USING brin(id);

适用场景

  • 数据按顺序插入
  • 列值线性增长
  • 删除操作较少

总结

索引类型 适用场景 特点
函数索引 函数/表达式查询 支持计算列索引
部分索引 部分数据频繁查询 减少索引大小
GiST 空间、范围、复杂操作符 支持自定义索引
SP-GiST 空间分区结构 递归分割存储
GIN 全文搜索、数组、JSON 反向索引
BRIN 大表、顺序插入、范围查询 按块索引,空间小

相关推荐
知识分享小能手4 小时前
PostgreSQL 入门学习教程,从入门到精通,PostgreSQL 16 内部结构深度解析 —语法、实现与实战案例(20)
数据库·学习·postgresql
IvorySQL4 小时前
官宣!全球 PostgreSQL 大神再度集结,HOW 2026 正式定档
数据库·postgresql·开源
Aaron_Wjf4 小时前
关于PG兼容性的一点转换
数据库·postgresql
Mr.徐大人ゞ9 小时前
PostgreSQL 常用查询命令汇总
postgresql
次旅行的库10 小时前
【问渠哪得清如许-数据分析】学习笔记-上
数据库·笔记·sql·学习·postgresql·数据分析
MrMua10 小时前
Ubuntu24.04 安装 PostgreSQL18,配置远程连接,安装常用插件,以及性能调优
ubuntu·postgresql·远程连接
Allen_LVyingbo10 小时前
PostgreSQL动态分区裁剪技术:查询性能优化解析(2026年版)
数据库·算法·观察者模式·postgresql·性能优化·架构
项目工程打工马11 小时前
Ubuntu 上 Nginx 安装详细指南(新手零踩坑版)
nginx·ubuntu·postgresql
IvorySQL1 天前
PostgreSQL 技术日报 (3月14日)|AI 落地 PostgreSQL 拒绝 PPT 空谈
数据库·postgresql·开源