一、关于分区表

表分区是在大数据优化中的一种常见的分表方案,通过将大数据按照一定的规则(最常见的是按照时间)进行分表处理,将逻辑上的一个大表分割成物理上的几块表,插入数据时,数据会自动插入到不同的分区表中,从而实现查询或者其它操作的性能优化。相比于一个大表,分区表具有以下优点:

(1)当查询或者更新一个分区的大部分记录时,采用顺序扫描而不是随机扫描,可以获得巨大的性能提升。

(2)使用不频繁的历史数据可以转移到一些低廉的存储介质上,而热数据放到性能较好的存储介质上,可以最大限度的减少成本。

(3)位于同一个分区的热数据可以全部缓存到内存中,查询hit的几率更高,可以加快查询速度。

Postgresql中有两种建立分区表的方法——使用声明式分区和表继承分区,接下来,笔者就这两种方式分别进行介绍。

二、声明式分区

声明式分区的意思就是Postgresql提供了相应的DDL语句创建分区表,使用声明式的方法建立分区表的方法比较简单,下面,我们就使用一个例子来介绍这种创建分区表的方法。

假设,我们创建一个operate_log表,用来记录用户的操作记录。因为操作记录会随着时间的累积,越来越大,所以我们选择建立一个分区表,并按照时间分区,每一个一个分区。

stock_analysis_data=# create table operate_log (id int,user_id int,operate_type int,content text,operate_date date) partition by range(operate_date);
CREATE TABLE

注意partition by range这段语句,是在指明使用哪个字段进行分区。现在,我们只是创建了分区表的基础表,分区表目前是还没有创建,基础表是无法插入数据的。此时,向基础表里面插入数据,会报下面的错误:

stock_analysis_data=# insert into operate_log values(1,1,1,'用户注册','2021-06-24');
ERROR:  no partition of relation "operate_log" found for row
DETAIL:  Partition key of the failing row contains (operate_date) = (2021-06-24).
stock_analysis_data=# 

接下来,我们来创建该基础表的分区表:

stock_analysis_data=# create table operater_log_202106 partition of operate_log for values from ('2021-06-01') to ('2021-07-01');
CREATE TABLE

可以看到,创建分区表最关键的两个语句是:

(1)partition of —— 指定待分区的基础表是哪个

(2)for values from () to () —— 指定当前分区表的分区范围

现在,我们可以向基础表里面插入数据,它可以自动的插入到相应的分区中:

 stock_analysis_data=# insert into operate_log values(1,1,1,'用户注册','2021-06-24');
INSERT 0 1
stock_analysis_data=# select * from operater_log_202106;id | user_id | operate_type | content  | operate_date
----+---------+--------------+----------+--------------1 |       1 |            1 | 用户注册 | 2021-06-24
(1 row)

从基础表里面直接select,可以查询到分区表里面的数据:

stock_analysis_data=# select * from operate_log;id | user_id | operate_type | content  | operate_date
----+---------+--------------+----------+--------------1 |       1 |            1 | 用户注册 | 2021-06-24
(1 row)

但是,如果插入定义在分区范围之外的数据,就会报错:

stock_analysis_data=# insert into operate_log values(1,1,1,'用户注册','2021-07-24');
ERROR:  no partition of relation "operate_log" found for row
DETAIL:  Partition key of the failing row contains (operate_date) = (2021-07-24).
stock_analysis_data=# 

三、表继承方式实现分区表

Postgresql中所谓的表继承,就是我们在创建一个新表时,可以继承基础表,新表具有基础表的所有字段。而向新表中插入数据时,通过基础表也可以查询到这些数据。实际上,使用声明式方式创建分区表,内部实现就是利用表继承。我们还是利用operate_log表作为基础表,利用表继承的方式创建分区表。不过在此之前,我们需要先把原来的operate_log表drop掉,因为通过表继承的方式实现的分区表,基础表必须是普通表,而之前的operate_log表我们创建时声明成了partition。

stock_analysis_data=# drop table operate_log;
DROP TABLE
stock_analysis_data=# create table operate_log (id int,user_id int,operate_type int,content text,operate_date date);
CREATE TABLE

下面,以新的operate_log为基础表,进行分区表的创建:

stock_analysis_data=# create table operate_log_202105(check(operate_date>='2021-05-01' and operate_date<'2020-06-01')) inherits (operate_log);
CREATE TABLE
stock_analysis_data=# create table operate_log_202106(check(operate_date>='2021-06-01' and operate_date<'2020-07-01')) inherits (operate_log);
CREATE TABLE

可以看到,以表继承的方式创建分区表时需要使用inherits关键字显式的指明基础表,而且需要自己写检查约束来约束分区字段的取值范围。仅仅完成上述工作之后,你会发现,现在的分区表实际上还无法使用,我们向基础表里面插入数据时,并不能自动的插入到分区表中。

stock_analysis_data=# insert into operate_log values(1,1,1,'用户注册','2021-06-24');
INSERT 0 1

上述命令,向基础表operate_log里面插入了一条数据,按理来说,应该插入到operate_log_202106表中,但是实际上,并没有插入进去:

stock_analysis_data=# select * from operate_log_202106;id | user_id | operate_type | content | operate_date
----+---------+--------------+---------+--------------
(0 rows)

原因是我们使用表继承的方式实现分区表时,需要自己去控制向主表插入数据时,数据自动插入分区表的逻辑。目前主要有两种方式可以实现这一逻辑:一种是使用触发器,另一种是使用自定义Rule。接下来,笔者分别进行介绍。

3.1 使用触发器进行数据自动插入分区表

我们先创建触发器函数:

create or replace function operate_log_insert_trigger()
returns trigger as
$$beginif (NEW.operate_date >= date'2021-06-01' and NEW.operate_date < date'2021-07-01') theninsert into operate_log_202106 values (NEW.*);elsif(NEW.operate_date >= date'2021-05-01' and NEW.operate_date < date'2021-06-01') theninsert into operate_log_202105 values (NEW.*);elseraise exception 'out of range!!!';end if;  return null;end;
$$
language plpgsql;

然后创建触发器:

create trigger insert_sale_detail_trigger
before insert on operate_log
for each row execute procedure operate_log_insert_trigger();
CREATE TRIGGER

接下来,向基础表中插入数据:

insert into operate_log values (2,2,1,'用户退出',date'2021-06-24');

再从operate_log_202106表中查询:

stock_analysis_data=# select * from operate_log_202106;id | user_id | operate_type | content  | operate_date
----+---------+--------------+----------+--------------2 |       2 |            1 | 用户退出 | 2021-06-24
(1 row)

3.2 使用自定义Rule进行数据自动插入分区表

以上是使用触发器实现的自动从基础表中向分区表插入数据,接下来,笔者介绍下使用自定义Rule向分区表中插入数据的方法。示例如下:

create rule operate_log_insert_202106 as
on insert to operate_log where
(operate_date>=date'2021-06-01' and operate_date<date'2021-07-01')
do insteadinsert into operate_log values (NEW.*);create rule operate_log_insert_202105 as
on insert to operate_log where
(operate_date>=date'2021-05-01' and operate_date<date'2021-06-01')
do insteadinsert into operate_log values (NEW.*);

创建好上述规则之后,同样可以实现向基础表插入数据时自动插入到相应的分区表,但是和触发器相比,自定义的Rule有它自己的特点:

(1) 每次插入数据都要检查,找到合适的规则然后用insert语句替代原来的insert语句,开销明显比触发器要大。

(2) 创建的Rule不定义会一定触发,在COPY插入数据时就不能触发Rule,但是触发器可以正常使用

(3) 当插入的数据超过了定义的范围时,触发器会报错,但是Rule会直接插入到基础表里。

(4) 不管是触发器还是Rule,当我们需要扩展分区表时,都不得不修改触发器函数或者新建Rule。

四、约束排除

约束排除是种优化分区表性能的查询方法,简单说来就是:在从基础表进行查询时,如果where条件包含在某个分区表约束条件之内时。如果打开约束排除,则直接从改分区表进行查询,但是如果关闭约束排除,则遍历所有分区表查找。在postgresql.conf文件中可以设置约束排除开启或是关闭:constraint_exclusion = partition (默认开启),设置off关闭。

在约束排除开启的情况下,我们用explain查看查询情况:

stock_analysis_data=# explain (analyze,verbose,costs,buffers,timing) select count(*) from operate_log where operate_date >= date'2021-06-01';QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------Aggregate  (cost=26.05..26.06 rows=1 width=8) (actual time=0.023..0.025 rows=1 loops=1)Output: count(*)Buffers: shared hit=1->  Append  (cost=0.00..25.16 rows=357 width=0) (actual time=0.013..0.016 rows=1 loops=1)Buffers: shared hit=1->  Seq Scan on public.operater_log_202106  (cost=0.00..23.38 rows=357 width=0) (actual time=0.010..0.013 rows=1 loops=1)Filter: (operater_log_202106.operate_date >= '2021-06-01'::date)Buffers: shared hit=1Planning Time: 0.193 msExecution Time: 0.065 ms
(10 rows)

关闭约束排除,找到/var/lib/pgsql/11/data下的postgresql.conf文件,修改:

constraint_exclusion = off

重启数据库服务后,再explain上述查询语句(或者直接执行set constraint_exclusion = off,设置本次session中的参数):

stock_analysis_data=# explain (analyze,verbose,costs,buffers,timing) select count(*) from operate_log where operate_date >= date'2021-06-01';QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------Aggregate  (cost=78.16..78.17 rows=1 width=8) (actual time=0.029..0.030 rows=1 loops=1)Output: count(*)Buffers: shared hit=3->  Append  (cost=0.00..75.48 rows=1071 width=0) (actual time=0.010..0.022 rows=3 loops=1)Buffers: shared hit=3->  Seq Scan on public.operater_log_202104  (cost=0.00..23.38 rows=357 width=0) (actual time=0.008..0.010 rows=1 loops=1)Filter: (operater_log_202104.operate_date >= '2021-06-01'::date)Buffers: shared hit=1->  Seq Scan on public.operater_log_202105  (cost=0.00..23.38 rows=357 width=0) (actual time=0.003..0.004 rows=1 loops=1)Filter: (operater_log_202105.operate_date >= '2021-06-01'::date)Buffers: shared hit=1->  Seq Scan on public.operater_log_202106  (cost=0.00..23.38 rows=357 width=0) (actual time=0.003..0.004 rows=1 loops=1)Filter: (operater_log_202106.operate_date >= '2021-06-01'::date)Buffers: shared hit=1Planning Time: 0.120 msExecution Time: 0.076 ms
(16 rows)

五、总结

根据本文的内容,我们可以得到如下结论:

(1)利用表分区可以实现将一个大表化整为零,数据按照日期或者其它分表规则划分到分区表上,一方面可以加快查询和更新效率,另一方面可以将不同热度的数据分布到不同的存储介质上,减少部署成本。

(2)表分区可以使用声明式分区或者表继承分区,前者的内部实现实际上也是利用表继承分区,但是前者使用较为简单,建议使用声明式的分区方式。

(3)使用分区表时,要开启约束排除,设置postgresql.conf文件中的constraint_exclusion = partition(默认值)。

Postgresql杂谈 10—Postgresql中的分区表相关推荐

  1. Postgresql杂谈 09—Postgresql中的Gist索引的深入学习

    本文,我们进一步学习下Gist索引.Gist是Generalized Search Tree的意思,意思是通用搜索树,底层结构也是一种平衡树,它是一套索引模板,可以支持用户实现自定义的索引.相比于BT ...

  2. Postgresql杂谈 04—Postgresql中的五种常规索引

    一.索引的分类 Postgresql中索引一共分为5种,每一种都有它合适的应用场景,我们在使用时要根据不同业务的特点,选择合适的索引,这样才能加快sql语句的查询效率.下面,我们将就每种不同的索引,介 ...

  3. Postgresql杂谈 23——Postgresql中的全文检索

    今天我们来聊一下全文检索,想必做搜索相关业务朋友对这个概念不会陌生,尤其是做搜索引擎,或者类似CSDN.知乎类的社区网站,全文检索是逃不开的业务.文,即文章.文档.全文搜索就是给定关键词,在所有的文档 ...

  4. Postgresql杂谈 06—Postgresql中的范围和数组类型

    本文主要介绍下Postgresql的另外两种特殊的类型Range类型(范围类型)和数组类型.两种类型,适用于不同的场景,但是最终的目的相同,就是使用传统的数据类型,建立常规的索引无法满足查询的性能要求 ...

  5. Postgresql杂谈 16—Postgresql中的锁机制

    今天,我们学习下Postgresql中的锁机制.锁是数据库事务的基础,通过锁才能保证数据库在并发时能够保证数据的安全和一致,才能够达到事务的一致性和隔离性.但是任何事物都有它的两面性,引入锁同样会增加 ...

  6. Postgresql杂谈 22——Postgresql中的模糊匹配

    Postgresql对模糊查询的支持,主要有三种方法:传统的like操作符.SQL99新增的SIMILAR TO操作符以及POSIX正则表达式.除了前面两种SQL标准的模糊查询手段,Postgresq ...

  7. Postgresql杂谈 18—Postgresql中的备份和恢复(二)

    上一篇文章中,我们主要学习了Postgresql的逻辑备份和恢复,接着上一篇的内容,今天我们介绍下Postgresql的物理备份.所谓物理备份,就是针对数据库的数据文件或者目录进行备份,物理备份的好处 ...

  8. PostgreSQL中的分区表(创建分区)–第1部分

    PostgreSQL中的分区表很容易做到,它涉及PostgreSQL的继承概念和触发器. 在这里,我提供了一个示例来演示如何在PostgreSQL中对表进行分区. 在继续之前,请了解一些基本概念,例如 ...

  9. PostgreSQL中的分区表

    PostgreSQL中的分区表 参考:https://www.xmmup.com/pgzhongdefenqubiao.html#PG_11xin_te_xing PostgreSQL分区的意思是把逻 ...

最新文章

  1. (03) spring Boot 的配置
  2. 085_html5服务器发送事件
  3. 控件与组件的区别_5分钟学交互:手机交互组件归纳 导航篇
  4. 【转】oracle之错误处理
  5. AFNnetworking快速教程,官方入门教程译
  6. SQL语法基础篇 —— 常用的SQL标准
  7. ffmpeg 安装教程
  8. SpringBoot整合log4j2
  9. 基于JAVA点餐系统的开题报告_开题报告-餐厅点餐系统的设计与实现.doc
  10. Python串口通信发送浮点型数据
  11. 石子合并问题(no circle)
  12. 苹果电脑开机长android,苹果笔记本开机白屏时间太长
  13. 我的世界服务器核心文件,minecraft1.9服务端核心Paper
  14. bzoj3659 Which Dreamed It BEST定理(公式绝对没错doge)
  15. props的动态传值 以及 v-for列表渲染
  16. c语言贪吃蛇大作业报告,C语言贪吃蛇实验报告
  17. C++ 流 文件操作(输入输出流)
  18. 矩概念与图像矩详解及其hu矩的运用
  19. 华为HCIE RS笔记-16以太网技术端口隔离,Smart Link,Monitor Link,端口镜像
  20. Barsetto百胜图BAC731B智能胶囊咖啡机测评,醇香相伴随性生活

热门文章

  1. linux配置文件密码加密工具,Linux下利用openssl对文件进行加密和解密
  2. 通信科技人员职业道德
  3. 英语拼读规则28条(必知)
  4. 华为的“少年天才”攀登者,出发向智能存储的“奥林帕斯山”
  5. [置顶] 代码审查工具FxCop建议采用的规则总结
  6. 什么是安迪比尔定理?
  7. There is no 'root'@'%' registered解决
  8. 意大利西西里百年历史Asaro家族橄榄油
  9. 应用程序清单 Manifest 中各种 UAC 权限级别的含义和效果
  10. 求助计算机程序员,程序员用代码求救:几近绝望时竟是老本行救了他