天天看点

PostgreSQL 11 preview - 并行排序、并行索引 (性能线性暴增) 单实例100亿TOP-K仅40秒

PostgreSQL , 并行排序 , 外排 , merge sort , TOP-K , 并行创建索引 , 分区并行 , ranking , percent , midean , 数据分布

在OLAP场景,排序是一个非常重要的功能。也是衡量数据库是否适合OLAP场景的一个重要指标。例如这些场景:

1、求TOP-K,例如在对数据进行分组后,取出每个分组的TOP-K。

2、求中位数,要求对数据排序,并取出处于中间水位的值。

3、数据柱状图,需要对数据进行排序,并按记录均匀分割成若干BUCKET,获得bucket边界。

4、ranking排序,对数据进行排序,取出TOP-K。

5、数据按窗口分组,求数据分布,或groupAGG等。

<a href="https://www.postgresql.org/docs/devel/static/functions-window.html">https://www.postgresql.org/docs/devel/static/functions-window.html</a>

6、海量数据,取percent value,例如每一天的网站访问延迟水位数(99%, 95%, ......)。

7、创建索引(在TP场景,如果数据频繁更新删除,索引可能会逐渐膨胀,PG 11并行创建索引功能推出,索引维护性能杠杠的了(不堵塞DML的语法也支持并行, create index concurrently))。

排序能力真的很重要对吧,怪不得那么多企业参加计算机排序性能大赛呢。

PostgreSQL 11 在排序上有了长足的进步,支持了并行排序。

并行排序, 创建索引流程如下(并行扫描 -&gt; 并行排序(阶段性quicksort+external sort) -&gt; 合并排序并写索引文件),如果是求TOP-K,采用top-n heapsort并且不需要合并落盘的过程,爆快:

PostgreSQL 11 preview - 并行排序、并行索引 (性能线性暴增) 单实例100亿TOP-K仅40秒

下面有一组测试数据,以及如何操纵并行排序的并行度,以及一些简单的原理介绍。有两个数字值得关注(也说明了PostgreSQL 进步飞速):

64线程,512G,NVME SSD

CentOS 7.x x64

PostgreSQL 11

一些预设参数,帮助控制并行度。

测试数据量1亿、10亿、100亿。排序字段为INT8类型。

1、表结构

2、使用dblink异步调用,并行加载数据。

3、创建生成dblink连接的函数,重复创建不报错。

4、并行加载1亿

5、并行加载10亿

6、并行加载100亿

非并行

并行(强制N个并行)

看一个执行计划,这个不涉及索引落盘,所以不受max_parallel_maintenance_workers参数控制,仅与其他几个并行参数相关。

为了保证每个Worker至少有32MB的内存用于并行排序,需要设置足够大的maintenance_work_mem(64个WORKER + 1 leader,至少需要2080MB内存)。

并行

PostgreSQL对分区表创建索引时,并不是所有分区同时并行开始创建(至少目前没有在内核中实现所有分区同时开始创建索引)。

但是,我们可以通过并行创建索引,控制每个分区的并行度。

让每个分区以16个并行度并行创建索引。

耗时与单表开64个并行差不多。(为什么不开64个并行,这里主要是因为分了4个分区,单分区数据量已经不大,16个并行度足矣。)

按照前面的测试,我们将100亿的表,分成100个分区,那么每个分区1亿数据,创建索引应该仅需1555秒(26分钟)。

并行排序,仅与这些参数有关:

具体并行度算法,参考:

<a href="https://github.com/digoal/blog/blob/master/201610/20161002_01.md">《PostgreSQL 9.6 并行计算 优化器算法浅析 - 以及如何强制并行度》</a>

1、基本参数,保证可以开足够多的并行计算worker进程数

2、成本模型,自动计算并行度

3、限定基于成本模型计算得到的并行度,单条创建索引SQL最大开多少个并行度

4、表上parallel_workers参数,覆盖根据成本模型计算得到的并行度(忽略max_parallel_maintenance_workers)

5、共享内存大小,每个创建索引的worker进程至少需要32MB,(注意还有一个leader process,因此为了保证开N个并行,那么需要 (N+1)*32MB ).

那么,要强制开N个并行创建索引,在满足条件1的情况下,只需要控制这两个参数maintenance_work_mem,以及表级parallel_workers。

用户可以自己去看文档

1、

<a href="https://www.postgresql.org/docs/devel/static/runtime-config-resource.html#GUC-MAX-PARALLEL-WORKERS-MAINTENANCE">https://www.postgresql.org/docs/devel/static/runtime-config-resource.html#GUC-MAX-PARALLEL-WORKERS-MAINTENANCE</a>

2、

<a href="https://www.postgresql.org/docs/devel/static/sql-createindex.html">https://www.postgresql.org/docs/devel/static/sql-createindex.html</a>

开启trace_sort可以跟踪排序过程。例子:

注意,以上提到的workMem,都是maintenance_work_mem决定的,除以(并行度+1)。

maintenance_work_mem的大小对并行创建索引的性能影响非常大。

想了解索引的内部构造、统计信息,可以使用pageinspect插件。

<a href="https://github.com/digoal/blog/blob/master/201605/20160528_01.md">《深入浅出PostgreSQL B-Tree索引结构》</a>

<a href="https://www.postgresql.org/docs/devel/static/pageinspect.html">https://www.postgresql.org/docs/devel/static/pageinspect.html</a>

<a href="https://github.com/digoal/blog/blob/master/201105/20110527_02.md">《Use pageinspect EXTENSION view PostgreSQL Page's raw infomation》</a>

用法这里不再赘述,可以看上面几篇文档。

PostgreSQL 11 内置了4种排序方法。

src/backend/utils/sort/tuplesort.c

数据量

结构

表占用空间

并行建索引占用空间

1亿

INT8+TEXT

4.2 GB

2.1 GB

10亿

41 GB

21 GB

100亿

413 GB

209 GB

硬件

非并行求TOP-K耗时

64并行度求TOP-K耗时

N倍性能

64线程机器

9.375 秒

0.5 秒

18.75 倍

95.16 秒

5.112 秒

18.615 倍

1107.55 秒

40 秒

27.69 倍

TOP-K用到的是top-N heapsort排序方法。

非并行创建索引耗时

64并行度创建索引耗时

性能提升倍数

26.244 秒

15.55 秒

1.69 倍

1442.7 秒

160.77 秒

8.97 倍

7456.9 秒

1869.5 秒

4 倍

并行创建索引用到的是external sorting algorithm 排序方法(external sort + external merge)。

由于并行创建索引分为并行扫描 -&gt; 并行排序 -&gt; merge的过程(merge阶段只有leader process在干活)。所以时间上并不是1亿(单进程)等于64亿(64并行)。

非并行的情况下,排序会随着记录数增多,非线性的增加耗时(26 -&gt; 1442 -&gt; 7456)。

并行的情况下,随着记录数增多,耗时基本是线性增加的,创建索引的基本时间可以预期(16 -&gt; 161 -&gt; 1870)。

值得注意的是,100亿时(创建索引耗时31分钟),并行过程IO已经开始有压力了(所有进程加起来写的文件已经超过400 GB),但是MERGE过程依旧是时间占比的大头。

PostgreSQL 每一个大版本都有令人非常惊喜的进步。单实例可以在26分钟(分区表)、31分钟(单表)左右创建一个100亿的索引。

maintenance_work_mem参数的大小对并行创建索引的性能影响较大。

目前PG暂时只支持BTREE索引的并行创建。期待后面加入另外N种(gin, hash, gist, sp-gist, brin, bloom)索引接口的并行创建。

<a href="https://github.com/digoal/blog/blob/master/201706/20170627_01.md">《PostgreSQL 9种索引的原理和应用场景》</a>

1、src/backend/utils/sort/tuplesort.c

2、src/include/utils/tuplesort.h

并行创建索引动作分解介绍:

继续阅读