在 PostgreSQL 中,分区表的使用并不像 oracle 那么智能, PostgreSQL 中是靠继承和触发器来实现分区表的,由于trigger 的使用,当业务繁忙时会大大降低数据库性能,所以 trigger 并不建议使用;因此 PostgreSQL 中的分区表对应用不再透明,例如,如果不使用 trigger,那么插入时程序需要指定子表等。今天测试了下取模分区的场景:以下为详细步骤
创建父表
创建父表并插入测试数据,如下:
1 |
francs=> create table tbl_name (id int4 primary key,name varchar(32),remark varchar(64)); |
父表备份
1 |
francs=> create table tbl_name_old as select * from tbl_name; |
备注:这里备份父表,用于之后做性能比较,因为分表后 tbl_name 数据要清空。
创建子表
创建子表, 分区字段 id,如下:
1 |
create table tbl_name_0 ( like tbl_name including constraints including defaults INCLUDING COMMENTS ) inherits ( tbl_name ); |
备注:创建子表时这里没有继承 index,是因为先创建索引再导数据速度会比较慢;
将数据分发到子表
1 |
insert into tbl_name_0 select * from only tbl_name where mod(id,32) =0; |
创建子表约束
1 |
alter table tbl_name_0 add CONSTRAINT con_tbl_name_0 check ( mod(id,32) =0); |
创建索引
增加主键,如下
1 |
ALTER TABLE tbl_name_0 ADD CONSTRAINT pk_tbl_name_id_0 PRIMARY KEY (id); |
创建 unique 索引
1 |
CREATE UNIQUE INDEX idx_tbl_name_name_0 ON tbl_name_0 USING btree (name); |
清空父表数据
1 |
truncate table only tbl_name; |
执行计划
查看参数 constraint_exclusion
1 |
francs=> show constraint_exclusion; |
显示查询父表的PLAN
1 |
francs=> explain select id,name,remark from tbl_name where id =32 ; |
备注:查询父表时,扫描了所有分区。
修改SQL,再次显示PLAN
1 |
francs=> explain select id,name,remark from tbl_name where mod(id,32)=0 and id =32 ; |
备注:查询父表时,如果加上 “where mod(id,32)= ? “时,不再扫描所有分区,而只扫描一个分区。
关闭 constraint_exclusion 参数,再次查看 PALN
1 |
francs=> set constraint_exclusion=off; |
备注: 关闭 constraint_exclusion 参数后,即扫描所有分区,关于这个参数的作用可以参考本文末尾的附录。
性能测试
下面测试两种场景:
- 场景一: 根据分区字段 id 查询测试
- 场景二: 根据非分区字段 name 查询测试
根据分区字段 id 查询测试
分表前查询
1 |
francs=> select id,name,remark from tbl_name_old where id =128 ; |
分表后查询
1 |
francs=> select id,name,remark from tbl_name_0 where id =64 ; |
skyid 测试 | 测试一 | 测试二 | 测试三 |
---|---|---|---|
分表前 | 0.305 ms | 0.277 ms | 0.351 ms |
分表后 | 0.331 ms | 0.322 ms | 0.301 ms |
备注:分表后,这里建议应用程序根据 id 取模,然后直接定位子表查询,这里数据来看,性能无明显变化,数据量大的时候分表后的这种场景性能有小辐上升。
根据非分区字段 name 查询测试
分表前
1 |
francs=> select id,name,remark from tbl_name_old where name='32_a' ; |
分表后
1 |
francs=> select id,name,remark from tbl_name where name='32_a' ; |
name 测试 | 测试一 | 测试二 | 测试三 |
---|---|---|---|
分表前 | 0.466 ms | 0.348 ms | 0.326 ms |
分表后 | 1.587 ms | 1.478 ms | 2.227 ms |
备注:根据非分区字段查询,分表后由于需要扫描所有分区,性能有下降辐度较大。
总结
- pg 中的表分区后,如果根据 “ where 分区键= ?” 查询,如果扫描父表,性能降低!
- pg 中的表分区后,如果根据 “ where 分区键= ?” 查询,如果程序定位到子表,性能小辐度上升!
- pg 中的表分区后,如果根据 “ where 非分区键= ?” 查询,且非分区键有索引,性能大辐下降。
- pg 中的表分区后,非分区字段的唯一性无法保证。
- PG 中的表分区后,为后期的分库奠定了基础。
备注:签于以上特点,故业务表分区需要权衡各方面的利弊,例如如果应用 90% 以上都是根据分区键查询,则建议分区。
附
constraint_exclusion (enum)
Controls the query planner is use of table constraints to optimize queries. The allowed values of constraint_exclusion are on (examine constraints for all tables), off (never examine constraints), and partition (examine constraints only for inheritance child tables and UNION ALL subqueries). partition is the default setting. It is often used with inheritance and partitioned tables to improve performance.
原创文章,作者:745907710,如若转载,请注明出处:https://blog.ytso.com/237859.html