POSTGRESQL分区表模式下查询效率测试

本文通过创建大规模分区表并插入大量数据来测试PostgreSQL的性能。实验中,通过不同查询条件对比查询时间,验证了索引及分区表查询性能的相关猜想。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

测试环境: POSTGRESQL 13 CENTOS 7.6

一、创建测试表

CREATE TABLE sheet_sale_main_test(company_id integer NOT NULL,sheet_id serial,sheet_no text NOT NULL, order_sheet_id integer,order_sheet_date date,sheet_type text,money_inout_flag smallint, branch_id integer, red_flag smallint,red_sheet_id integer, red_sheet_date date,supcust_id integer,shop_id integer,total_amount float8 NOT NULL, is_retail boolean,tax_amount float4,discount_info text,
payway1_id integer,payway1_amount float8,payway2_id integer,payway2_amount float8, now_pay_amount float8 NOT NULL,
now_disc_amount float4 DEFAULT 0 NOT NULL,paid_amount float8 NOT NULL,disc_amount float4 DEFAULT 0 NOT NULL,
maker_id integer, make_time timestamp, happen_time timestamp, approver_id integer,approve_time timestamp, seller_id integer,make_brief text,approve_brief text, submit_time timestamp) partition by range(happen_time);

create table sheet_sale_main_test_20072102 partition of sheet_sale_main_test for values from (‘2020-07-01’) to (‘2021-03-01’);
create table sheet_sale_main_test_21032106 partition of sheet_sale_main_test for values from (‘2021-03-01’) to (‘2021-06-01’);
create table sheet_sale_main_test_21062201 partition of sheet_sale_main_test for values from (‘2021-06-01’) to (‘2022-01-01’);
create table sheet_sale_main_test_22012301 partition of sheet_sale_main_test for values from (‘2022-01-01’) to (‘2023-01-01’);
create table sheet_sale_main_test_23012401 partition of sheet_sale_main_test for values from (‘2023-01-01’) to (‘2024-01-01’);
create table sheet_sale_main_test_24012501 partition of sheet_sale_main_test for values from (‘2024-01-01’) to (‘2025-01-01’);
create table sheet_sale_main_test_25012601 partition of sheet_sale_main_test for values from (‘2025-01-01’) to (‘2026-01-01’);
create table sheet_sale_main_test_26012701 partition of sheet_sale_main_test for values from (‘2026-01-01’) to (‘2027-01-01’);
create table sheet_sale_main_test_27013001 partition of sheet_sale_main_test for values from (‘2027-01-01’) to (‘2030-01-01’);
create table sheet_sale_main_test_30013301 partition of sheet_sale_main_test for values from (‘2030-01-01’) to (‘2033-01-01’);

二、插入测试数据

insert into sheet_sale_main_test
(company_id,sheet_no,sheet_type,money_inout_flag,branch_id, supcust_id ,shop_id, total_amount, now_pay_amount, paid_amount, happen_time, seller_id)
SELECT 10,‘X40505050666’,‘X’, 1, (random()*100)::INT, (random()*3000)::INT, (random()*3000)::INT,(random()*1000)::INT, 0, 0, date(generate_series(‘2021-01-11’::timestamp, ‘2021-01-11’::timestamp + ‘360 days’, ‘1 second’)),(random()*1000)::INT

执行该语句一次插入从2021-1-11开始,每一秒产生一条记录,360天产生3000多万条记录,大约90秒可以完成插入。这样连续插入3年数据,大约1亿多条。

三、查询测试

select * from sheet_sale_main where sheet_id=145667
查询时间需要40秒。

select * from sheet_sale_main where happen_time>=‘2021-1-11’ and happen_time<=‘2021-1-12’ limit 20
查询需要10秒

对sheet_id,happen_time分别增加索引后,以上查询都提升到0.030秒左右。

验证猜想1:分区表查询条件必须指定分区键,否则会很慢(证明是错的)

我之前有个猜想,就是分区表的查询条件必须指定分区键,否则会扫描所有分区,会很慢。分区键是happen_time,.所以要根据sheet_id查询一条记录,为了速度也得指定happen_time。为了证明猜想,就做了以下测试:
select * from sheet_sale_main where sheet_id=145667
select * from sheet_sale_main where sheet_id=145667 and happen_time>=‘2021-1-11’ and happen_time<=‘2021-1-12’
结果发现两者耗费时间相同,也就是说,对于有索引的自增列字段,不指定分区键查询也一样快。

验证猜想2:分区表按某日期列查询最早或最后的记录会扫描所有分区,所以会很慢

对1亿条记录的表执行
select max(happen_time) from sheet_sale_main_test where supcust_id=90
select max(happen_time) from sheet_sale_main_test
执行时间都差不多,大约0.019秒

查找某客户最后一次购买记录

select * from sheet_sale_main_test where supcust_id=1000 order by happen_time desc limit 1
supcust_id并没有加索引 查询在0.037秒内完成
查询某客户
select * from sheet_sale_main_test where supcust_id=900 and total_amount-paid>900 order by happen_time limit 1
查询时间0.038秒

结论:POSTGRESQL不愧是性能怪兽,对大表查询效率非常高

### PGSQL 分区表的创建、管理和查询 #### 创建分区表PostgreSQL 中,可以通过定义父表并为其设置分区规则来创建分区表。对于范围分区,可以基于列中的值进行划分。 ```sql CREATE TABLE measurement ( city_id int not null, logdate date not null, peaktemp int, unitsales int ) PARTITION BY RANGE (logdate); ``` 此命令创建了一个名为 `measurement` 的表,并指定了该表将按照 `logdate` 列进行范围分区[^1]。 接着,可以根据具体的日期区间为这个父表创建子分区: ```sql CREATE TABLE measurement_y2006m02 PARTITION OF measurement FOR VALUES FROM ('2006-02-01') TO ('2006-03-01'); ``` 上述 SQL 语句创建了 `measurement` 表的一个分区,专门用于存储 2006 年 2 月的日志记录。 #### 管理分区表 当需要向现有分区方案中添加新的时间窗口时,只需继续创建对应时间段的新分区即可。例如增加一个新的月份分区: ```sql CREATE TABLE measurement_y2007m01 PARTITION OF measurement FOR VALUES FROM ('2007-01') TO ('2007-02-01'); ``` 如果要删除某个不再使用的旧分区,则可以直接执行 DROP 命令移除它: ```sql DROP TABLE measurement_y2006m02; ``` 需要注意的是,在实际操作前应当确认目标分区内的数据已经妥善备份或迁移完毕。 #### 查询分区表的信息 为了获取有关特定表及其所有关联分区结构的信息,可利用系统视图如 `pg_inherits` 或者专用函数来进行检索。比如通过如下方式查看某张表是否被划分为多个物理文件以及它们各自的边界条件: ```sql SELECT pg_get_partition_def('public.measurement'::regclass, true); ``` 这条指令会返回给定表对象名所对应的分区定义字符串表示形式,其中包含了各部分的关键属性描述[^3]。 另外还可以借助 `\d+ table_name` 这样的元命令快速浏览一张表的整体概况连同其下辖的所有继承自它的子表列表。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值