利用 PostgreSQL 部分索引提高查询语句的性能

文章目录

大家好,我是只谈技术不剪发的 Tony 老师。

在 PostgreSQL 数据库中,部分索引(partial index)是指对表中满足特定条件的数据行进行索引。由于它不需要对全部数据进行索引,因此索引会更小,在特定场景下通过部分索引查找数据时性能会更好。本文就给大家介绍一下 PostgreSQL 中的部分索引功能。

如果觉得文章有用,欢迎评论📝、点赞👍、推荐🎁

PostgreSQL 在创建索引时可以通过一个 WHERE 子句指定需要索引的数据行,从而创建一个部分索引。例如,对于以下订单表 orders:

CREATE TABLE orders (
  id INT PRIMARY KEY,
  customer_id INT,
  status TEXT
);

INSERT INTO orders (id, customer_id, status)
SELECT
  i,
  (random()*10000)::INT,
  CASE (random() * 100)::int
    WHEN 0 THEN 'pending'
    WHEN 1 THEN 'shipped'
    ELSE 'completed'
  END
    FROM generate_series(1, 1000000) i;

该表中总共有 1000000 个订单,通常绝大部的订单都处于完成状态。一般情况下,我们只需要针对某个用户未完成的订单进行查询跟踪,因此可以创建一个基于用户编号和状态的部分索引:

CREATE INDEX full_idx ON orders (customer_id, status);

然后使用 EXPLAIN ANALYZE 命令查看 SELECT 语句的执行计划:

EXPLAIN ANALYZE
SELECT * 
FROM orders
WHERE customer_id = 5678
AND status != 'completed';

QUERY PLAN                                                                                                           |
---------------------------------------------------------------------------------------------------------------------|
Bitmap Heap Scan on orders  (cost=5.18..369.08 rows=1 width=17) (actual time=33.661..34.040 rows=1 loops=1)          |
  Recheck Cond: (customer_id = 5678)                                                                                 |
  Filter: (status <> 'completed'::text)                                                                              |
  Rows Removed by Filter: 109                                                                                        |
  Heap Blocks: exact=109                                                                                             |
  ->  Bitmap Index Scan on full_idx  (cost=0.00..5.17 rows=100 width=0) (actual time=33.526..33.526 rows=110 loops=1)|
        Index Cond: (customer_id = 5678)                                                                             |
Planning Time: 1.252 ms                                                                                              |
Execution Time: 34.180 ms                                                                                            |

输出结果显示利用索引 full_idx 扫描了 110 行,然后通过 status 过滤掉了 109 行,而不是直接通过索引扫描出所需的数据。

此时,我们可以查看一下索引 full_idx 占用的空间大小:

select pg_size_pretty(pg_table_size('full_idx'));

pg_size_pretty|
--------------|
30 MB         |

接下来我们再创建一个部分索引,只包含未完成的订单数据,从而减少索引的数据量:

CREATE INDEX partial_idx ON orders (customer_id)
WHERE status != 'completed';

索引 partial_idx 中只有 customer_id 字段,不需要 status 字段。然后再次执行相同的 EXPLAIN ANALYZE 语句,查看执行计划:

EXPLAIN ANALYZE
SELECT * 
FROM orders
WHERE customer_id = 5678
AND status != 'completed';

QUERY PLAN                                                                                                         |
-------------------------------------------------------------------------------------------------------------------|
Index Scan using partial_idx on orders  (cost=0.29..8.30 rows=1 width=17) (actual time=0.246..0.249 rows=1 loops=1)|
  Index Cond: (customer_id = 5678)                                                                                 |
Planning Time: 0.397 ms                                                                                            |
Execution Time: 0.295 ms                                                                                           |

输出结果显示 PostgreSQL 执行计划选择了索引 partial_idx,而不是 full_idx;因为这样性能更好,只需要扫描 1 行记录就可以得到结果。

同样可以查看一下索引 partial_idx 占用的空间大小:

select pg_size_pretty(pg_table_size('partial_idx'));

pg_size_pretty|
--------------|
352 kB        |

索引只有 352 KB,而不是 30 MB,因为绝大多数订单都处于完成状态。

另外,部分索引还可以用于实现其他的功能。例如,我们可以将索引 partial_idx 定义为唯一索引,从而实现每个用户只能存在一个未完成订单的约束。

DROP INDEX partial_idx;
TRUNCATE TABLE orders;

CREATE UNIQUE INDEX partial_idx ON orders (customer_id)
WHERE status != 'completed';

INSERT INTO orders(id, customer_id, status) VALUES (1, 1, 'pending');

INSERT INTO orders(id, customer_id, status) VALUES (2, 1, 'pending');
SQL 错误 [23505]: 错误: 重复键违反唯一约束"partial_idx"
  详细:键值"(customer_id)=(1)" 已经存在

用户必须完成一个订单之后才能继续生成新的订单。

📝更多关于 PostgreSQL 索引和优化的内容,可以参考这篇文章

PostgreSQL中,创建索引的语法如下: CREATE INDEX index_name ON table_name (column_name); 其中,index_name是你给索引起的名字,table_name是你要在哪个表上创建索引,column_name是你要在哪个列上创建索引。这个语句可以在单列上创建索引。 另外,还有一种创建组合索引的方式,即在多个列上创建索引。基本语法如下: CREATE INDEX index_name ON table_name (column1_name, column2_name); 此外,你还可以创建唯一索引,即保证被索引的列的值是唯一的。创建唯一索引的语法如下: CREATE UNIQUE INDEX index_name on table_name (column_name); 如果你想删除索引,可以使用DROP INDEX语句: DROP INDEX index_name; 以上是PostgreSQL中创建索引的语法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Postgresql如何创建索引,有什么技巧?](https://blog.csdn.net/weixin_35749440/article/details/128867438)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [PostgreSQL 创建表格](https://download.csdn.net/download/weixin_38632797/13704517)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [postgresql创建索引](https://blog.csdn.net/weixin_42234450/article/details/122307514)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不剪发的Tony老师

为 5 个 C 币而折腰。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值