热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

PostgreSQL时序数据库设计最佳实践关联citus,columnar,partition,timescaledb,压缩,高速写,parallelappend多分区并行查询,分区

digoal德哥专栏PostgreSQL时序数据库设计最佳实践-关联citus,columnar,partition,timescaledb,压缩,高速写,parallelappe

作者

digoal


日期

2021-04-28


标签

PostgreSQL , citus , columnar , partition , timescaledb




背景

时序场景对数据库的需求:

1、要求高速写入

2、要求存储支持压缩

3、要求高效率的区间查询和区间分析

PostgreSQL的时序解决方案:

1、采用分区表, 按时间分区, 当前分区使用heap存储格式(提供高性能写入, 批量入库, 单机每秒几十万条不是问题.)

2、将历史分区表 转换为columnar 分区存储. (使用citus的columnar引擎) 提供很好的压缩比. 也就是说一张表, 老的分区是columnar存储, 新的分区是heap存储.

3、PG 支持并行查询, 可以提供很好的分析效率.

4、需要点查或按时间小范围查询的通常是近期的数据(例如最近1个月), 这种分区建议保持heap引擎, 因为它支持index. columnar暂时不支持index.

columnar和heap的差别:

1、columnar是列存储格式, 支持压缩

2、heap是行存储格式, 支持变长字段toast动态压缩, 但是压缩比没有columnar高

3、columnar只支持批量写入(copy或insert into), 适合静态数据, 一次性转换.

4、heap支持任意写入操作, 高并发小事务很匹配.

5、columnar不支持索引, 所以全表扫描效率高, 点查或小范围查询效率低.

6、heap支持索引, 全表扫描比columnar低, 但是点查或小范围查询效率高.

7、columnar不支持单表并行计算. 所以需要通过多个分区, parallel append scan来实现并行计算.

8、heap支持单表并行计算.

另一个关联产品是timescaledb, 本文不做讨论.

https://github.com/timescale/timescaledb


DEMO

PostgreSQL 13为例.

citus columnar 列存储使用举例

https://github.com/citusdata/citus

创建插件

```

vi postgresql.conf

shared_preload_libraries='citus'

pg_ctl restart -m fast

postgres=# create extension citus;

CREATE EXTENSION

```

创建列存表, 写入1000万测试数据

```

postgres=# create table cstore (id int, c1 int, c2 int, c3 int, c4 int, c5 int, c6 int, c7 int, crt_time timestamp, info text) using columnar;

CREATE TABLE

postgres=# insert into cstore select generate_series(1,10000000), random()10, random()20,random()100,random()1000,random()10000, random()500, random()*2000, clock_timestamp(), random()::text;

INSERT 0 10000000

```

创建行存表, 写入1000万数据

```

postgres=# create table heap (like cstore);

CREATE TABLE

postgres=# insert into heap select * from cstore ;

INSERT 0 10000000

```

查看columnar列存储参数, 包括压缩比等配置

```

postgres=# show columnar.compression;

columnar.compression



zstd

(1 row)

postgres=# show columnar.compression_level;

columnar.compression_level



3

(1 row)

postgres=# show columnar.stripe_row_limit;

columnar.stripe_row_limit



150000

(1 row)

postgres=# show columnar.chunk_group_row_limit;

columnar.chunk_group_row_limit



10000

(1 row)

```

对比列存储和行存储表的空间占用情况

```

postgres=# \dt+ cstore

List of relations

Schema | Name | Type | Owner | Persistence | Size | Description

--------+--------+-------+----------+-------------+--------+-------------

public | cstore | table | postgres | permanent | 243 MB |

(1 row)

postgres=# \dt+ heap

List of relations

Schema | Name | Type | Owner | Persistence | Size | Description

--------+------+-------+----------+-------------+--------+-------------

public | heap | table | postgres | permanent | 888 MB |

(1 row)

```

简单的聚合查询性能

```

postgres=# select count(*) from heap ;

count



10000000

(1 row)

Time: 512.432 ms

postgres=# select count(*) from cstore;

count



10000000

(1 row)

Time: 386.415 ms

```

执行计划

```

postgres=# explain (analyze,verbose,costs,settings,buffers,wal,timing,summary) select count(*) from heap ;

QUERY PLAN



Aggregate (cost=238637.75..238637.76 rows=1 0'

Planning Time: 0.035 ms

JIT:

Functions: 2

Options: Inlining false, Optimization false, Expressions true, Deforming true

Timing: Generation 0.186 ms, Inlining 0.000 ms, Optimization 0.081 ms, Emission 1.006 ms, Total 1.273 ms

Execution Time: 1142.839 ms

(13 rows)

Time: 1143.141 ms (00:01.143)

postgres=# explain (analyze,verbose,costs,settings,buffers,wal,timing,summary) select count(*) from cstore;

QUERY PLAN



Aggregate (cost=25000.00..25000.01 rows=1 0'

Planning:

Buffers: shared hit=6

Planning Time: 0.127 ms

Execution Time: 996.249 ms

(11 rows)

Time: 996.632 ms

```

2、时序场景实践

创建按月分区的行存表

```

CREATE table ts (id int, c1 int, c2 int, c3 int, c4 int, c5 int, c6 int, c7 int, crt_time timestamp, info text)

PARTITION by range (crt_time);

create table ts_202101 PARTITION OF ts FOR VALUES FROM ('2021-01-01') to ('2021-02-01');

create table ts_202102 PARTITION OF ts FOR VALUES FROM ('2021-02-01') to ('2021-03-01');

create table ts_202103 PARTITION OF ts FOR VALUES FROM ('2021-03-01') to ('2021-04-01');

create table ts_202104 PARTITION OF ts FOR VALUES FROM ('2021-04-01') to ('2021-05-01');

create table ts_202105 PARTITION OF ts FOR VALUES FROM ('2021-05-01') to ('2021-06-01');

```

写入1000万测试数据

insert into ts select generate_series(1,10000000), random()*10, random()*20, random()*30,
random()*100, random()*200, random()*300, random()*1000,
'2021-01-01'::timestamp + ((random()*5*30*24)||' hour')::interval,
random()::text;

月初时创建列存表存放若干个月以前的分区数据, 将若干个月以前的分区数据转入列存表, 交换列存和行存储分区

begin;
lock table heap in exclusive mode;
CREATE table ts_columnar_202101 (like ts) using columnar;
insert into ts_columnar_202101 select * from ts_202101;
alter table ts DETACH PARTITION ts_202101;
alter table ts ATTACH PARTITION ts_columnar_202101 FOR VALUES FROM ('2021-01-01') to ('2021-02-01');
end;

2秒多点交换结束

INSERT 0 2066564
Time: 2654.981 ms (00:02.655)

交换结束后, TS表202101的分区就是列存分区了.

```

postgres=# \dt+ ts*

List of relations

Schema | Name | Type | Owner | Persistence | Size | Description

--------+--------------------+-------------------+----------+-------------+---------+-------------

public | ts | partitioned table | postgres | permanent | 0 bytes |

public | ts_202101 | table | postgres | permanent | 184 MB |

public | ts_202102 | table | postgres | permanent | 166 MB |

public | ts_202103 | table | postgres | permanent | 184 MB |

public | ts_202104 | table | postgres | permanent | 178 MB |

public | ts_202105 | table | postgres | permanent | 178 MB |

public | ts_columnar_202101 | table | postgres | permanent | 58 MB |

(7 rows)

postgres=# \d+ ts

Partitioned table "public.ts"

Column | Type | Collation | Nullable | Default | Storage | Stats target | Description

----------+-----------------------------+-----------+----------+---------+----------+--------------+-------------

id | integer | | | | plain | |

c1 | integer | | | | plain | |

c2 | integer | | | | plain | |

c3 | integer | | | | plain | |

c4 | integer | | | | plain | |

c5 | integer | | | | plain | |

c6 | integer | | | | plain | |

c7 | integer | | | | plain | |

crt_time | timestamp without time zone | | | | plain | |

info | text | | | | extended | |

Partition key: RANGE (crt_time)

Partitions: ts_202102 FOR VALUES FROM ('2021-02-01 00:00:00') TO ('2021-03-01 00:00:00'),

ts_202103 FOR VALUES FROM ('2021-03-01 00:00:00') TO ('2021-04-01 00:00:00'),

ts_202104 FOR VALUES FROM ('2021-04-01 00:00:00') TO ('2021-05-01 00:00:00'),

ts_202105 FOR VALUES FROM ('2021-05-01 00:00:00') TO ('2021-06-01 00:00:00'),

ts_columnar_202101 FOR VALUES FROM ('2021-01-01 00:00:00') TO ('2021-02-01 00:00:00')

```

查询ts表, 正确的路由到对应分区

```

postgres=# select count(*) from ts where crt_time between '2021-01-02' and '2021-01-15';

count



866004

(1 row)

Time: 140.311 ms

postgres=# explain select count(*) from ts where crt_time between '2021-01-02' and '2021-01-15';

QUERY PLAN



Aggregate (cost=766.30..766.31 rows=1 2021-01-02 00:00:00'::timestamp without time zone) AND (crt_time <= '2021-01-15 00:00:00'::timestamp without time zone))

(3 rows)

Time: 0.435 ms

```

列存多分区并行计算例子:

```

postgres=# explain select count(*) from ts;

QUERY PLAN



Finalize Aggregate (cost=140940.59..140940.60 rows=1 s github - 公益是一辈子的事.

digoal's wechat


推荐阅读
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 关于我们EMQ是一家全球领先的开源物联网基础设施软件供应商,服务新产业周期的IoT&5G、边缘计算与云计算市场,交付全球领先的开源物联网消息服务器和流处理数据 ... [详细]
  • MySQL中的MVVC多版本并发控制机制的应用及实现
    本文介绍了MySQL中MVCC的应用及实现机制。MVCC是一种提高并发性能的技术,通过对事务内读取的内存进行处理,避免写操作堵塞读操作的并发问题。与其他数据库系统的MVCC实现机制不尽相同,MySQL的MVCC是在undolog中实现的。通过undolog可以找回数据的历史版本,提供给用户读取或在回滚时覆盖数据页上的数据。MySQL的大多数事务型存储引擎都实现了MVCC,但各自的实现机制有所不同。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 如何在服务器主机上实现文件共享的方法和工具
    本文介绍了在服务器主机上实现文件共享的方法和工具,包括Linux主机和Windows主机的文件传输方式,Web运维和FTP/SFTP客户端运维两种方式,以及使用WinSCP工具将文件上传至Linux云服务器的操作方法。此外,还介绍了在迁移过程中需要安装迁移Agent并输入目的端服务器所在华为云的AK/SK,以及主机迁移服务会收集的源端服务器信息。 ... [详细]
  • HTML学习02 图像标签的使用和属性
    本文介绍了HTML中图像标签的使用和属性,包括定义图像、定义图像地图、使用源属性和替换文本属性。同时提供了相关实例和注意事项,帮助读者更好地理解和应用图像标签。 ... [详细]
  • 关于CMS收集器的知识介绍和优缺点分析
    本文介绍了CMS收集器的概念、运行过程和优缺点,并解释了垃圾回收器的作用和实践。CMS收集器是一种基于标记-清除算法的垃圾回收器,适用于互联网站和B/S系统等对响应速度和停顿时间有较高要求的应用。同时,还提供了其他垃圾回收器的参考资料。 ... [详细]
  • 在Oracle11g以前版本中的的DataGuard物理备用数据库,可以以只读的方式打开数据库,但此时MediaRecovery利用日志进行数据同步的过 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
author-avatar
chucai
这个家伙很懒,什么也没留下,只留下了这个默认个签!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有