背景

聚合操作指将分组的数据聚合为一个结果输出。

聚合通常用在统计应用中,例如统计分组的最大值,最小值,记录数,平均值,方差,截距,相关性。

聚合也可能被用于文本分析或者图像分析等,例如最佳相似度,行列变换,聚合为数组或JSON,图像堆叠等。

因此聚合通常需要启动值,行的处理,以及结果的格式转换3个过程。

PostgreSQL的聚合也包含了以上三个过程,创建一个聚合函数的语法如下:

  1. CREATE AGGREGATE name ( [ argmode ] [ argname ] arg_data_type [ , ... ] ) (
  2. SFUNC = sfunc,
  3. STYPE = state_data_type
  4. [ , SSPACE = state_data_size ]
  5. [ , FINALFUNC = ffunc ]
  6. [ , FINALFUNC_EXTRA ]
  7. [ , COMBINEFUNC = combinefunc ]
  8. [ , SERIALFUNC = serialfunc ]
  9. [ , DESERIALFUNC = deserialfunc ]
  10. [ , INITCOND = initial_condition ]
  11. [ , MSFUNC = msfunc ]
  12. [ , MINVFUNC = minvfunc ]
  13. [ , MSTYPE = mstate_data_type ]
  14. [ , MSSPACE = mstate_data_size ]
  15. [ , MFINALFUNC = mffunc ]
  16. [ , MFINALFUNC_EXTRA ]
  17. [ , MINITCOND = minitial_condition ]
  18. [ , SORTOP = sort_operator ]
  19. [ , PARALLEL = { SAFE | RESTRICTED | UNSAFE } ]
  20. )

例子

  1. CREATE AGGREGATE avg (float8)
  2. (
  3. sfunc = float8_accum,
  4. stype = float8[],
  5. finalfunc = float8_avg,
  6. initcond = '{0,0,0}'
  7. );

参考

https://www.postgresql.org/docs/9.6/static/xaggr.html

https://www.postgresql.org/docs/9.6/static/sql-createaggregate.html

PostgreSQL 聚合处理流程如图

pic1

  1. 使用initcond指定internal-state的初始值,没有则为空。

  2. 每条记录(作为next-data-values输入),调用 sfunc( internal-state, next-data-values ) —> next-internal-state
    输出的结果作为中间结果继续调用sfunc

  3. ffunc( internal-state ) —> aggregate-value 可选,作为结果转换使用

9.6 聚合OP优化

pic2

如果initcond与sfunc一致,在同一个聚合分组内,sfunc只需要计算一遍所有记录,而不需要计算多遍。

https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=804163bc25e979fcd91b02e58fa2d1c6b587cc65

  1. Share transition state between different aggregates when possible.
  2. If there are two different aggregates in the query with same inputs,
  3. and the aggregates have the same initial condition and transition function,
  4. only calculate the state value once, and only call the final functionsseparately.
  5. For example, AVG(x) and SUM(x) aggregates have the same transition function, which accumulates the sum and number of input tuples.
  6. For a query like "SELECT AVG(x), SUM(x) FROM x", we can therefore accumulate the state function only once, which gives a nice speedup.
  7. David Rowley, reviewed and edited by me.

我们可以通过以下SQL查看可以共享OP的聚合函数,rank一致的都可以共享。

  1. postgres=# select rank() over (partition by 1 order by aggtransfn,agginitval),
  2. row_number() over (partition by aggtransfn,agginitval order by aggfnoid) rn,
  3. aggfnoid,aggtransfn,agginitval from pg_aggregate ;
rankrnaggfnoidaggtransfnagginitval
11pg_catalog.sumfloat4plnone
21pg_catalog.avgfloat4_accum{0,0,0}
22pg_catalog.variancefloat4_accum{0,0,0}
23pg_catalog.stddevfloat4_accum{0,0,0}
24pg_catalog.var_sampfloat4_accum{0,0,0}
25pg_catalog.stddev_sampfloat4_accum{0,0,0}
26pg_catalog.var_popfloat4_accum{0,0,0}
27pg_catalog.stddev_popfloat4_accum{0,0,0}
91pg_catalog.maxfloat4largernone
101pg_catalog.minfloat4smallernone
111pg_catalog.sumfloat8plnone
121pg_catalog.avgfloat8_accum{0,0,0}
122pg_catalog.variancefloat8_accum{0,0,0}
123pg_catalog.stddevfloat8_accum{0,0,0}
124pg_catalog.var_sampfloat8_accum{0,0,0}
125pg_catalog.stddev_sampfloat8_accum{0,0,0}
126pg_catalog.var_popfloat8_accum{0,0,0}
127pg_catalog.stddev_popfloat8_accum{0,0,0}
191pg_catalog.maxfloat8largernone
201pg_catalog.minfloat8smallernone
211pg_catalog.maxtext_largernone
221pg_catalog.mintext_smallernone
231pg_catalog.maxarray_largernone
241pg_catalog.minarray_smallernone
251pg_catalog.maxint4largernone
252pg_catalog.maxint4largernone
271pg_catalog.minint4smallernone
272pg_catalog.minint4smallernone
291pg_catalog.maxint2largernone
301pg_catalog.minint2smallernone
311pg_catalog.sumcash_plnone
321pg_catalog.maxcashlargernone
331pg_catalog.mincashsmallernone
341pg_catalog.maxbpchar_largernone
351pg_catalog.minbpchar_smallernone
361pg_catalog.maxdate_largernone
371pg_catalog.mindate_smallernone
381pg_catalog.suminterval_plnone
391pg_catalog.mintimestamptz_smallernone
401pg_catalog.maxtimestamptz_largernone
411pg_catalog.mininterval_smallernone
421pg_catalog.maxinterval_largernone
431pg_catalog.countint8inc0
441pg_catalog.maxint8largernone
451pg_catalog.minint8smallernone
461pg_catalog.maxtime_largernone
471pg_catalog.mintime_smallernone
481pg_catalog.maxtimetz_largernone
491pg_catalog.mintimetz_smallernone
501pg_catalog.bit_andbitandnone
511pg_catalog.bit_orbitornone
521pg_catalog.minnumeric_smallernone
531pg_catalog.maxnumeric_largernone
541pg_catalog.variancenumeric_accumnone
542pg_catalog.stddevnumeric_accumnone
543pg_catalog.var_sampnumeric_accumnone
544pg_catalog.stddev_sampnumeric_accumnone
545pg_catalog.var_popnumeric_accumnone
546pg_catalog.stddev_popnumeric_accumnone
601pg_catalog.varianceint2_accumnone
602pg_catalog.stddevint2_accumnone
603pg_catalog.var_sampint2_accumnone
604pg_catalog.stddev_sampint2_accumnone
605pg_catalog.var_popint2_accumnone
606pg_catalog.stddev_popint2_accumnone
661pg_catalog.varianceint4_accumnone
662pg_catalog.stddevint4_accumnone
663pg_catalog.var_sampint4_accumnone
664pg_catalog.stddev_sampint4_accumnone
665pg_catalog.var_popint4_accumnone
666pg_catalog.stddev_popint4_accumnone
721pg_catalog.varianceint8_accumnone
722pg_catalog.stddevint8_accumnone
723pg_catalog.var_sampint8_accumnone
724pg_catalog.stddev_sampint8_accumnone
725pg_catalog.var_popint8_accumnone
726pg_catalog.stddev_popint8_accumnone
781pg_catalog.sumint2_sumnone
791pg_catalog.sumint4_sumnone
801pg_catalog.avginterval_accum{0 second,0 second}
811pg_catalog.bit_andint2andnone
821pg_catalog.bit_orint2ornone
831pg_catalog.bit_andint4andnone
841pg_catalog.bit_orint4ornone
851pg_catalog.bit_andint8andnone
861pg_catalog.bit_orint8ornone
871pg_catalog.avgint2_avg_accum{0,0}
881pg_catalog.avgint4_avg_accum{0,0}
891pg_catalog.maxoidlargernone
901pg_catalog.minoidsmallernone
911pg_catalog.mintimestamp_smallernone
921pg_catalog.maxtimestamp_largernone
931pg_catalog.array_aggarray_agg_transfnnone
941bool_andbooland_statefuncnone
942everybooland_statefuncnone
961bool_orboolor_statefuncnone
971pg_catalog.avgint8_avg_accumnone
972pg_catalog.sumint8_avg_accumnone
991pg_catalog.maxtidlargernone
1001pg_catalog.mintidsmallernone
1011pg_catalog.countint8inc_any0
1021regr_countint8inc_float8_float80
1031regr_sxxfloat8_regr_accum{0,0,0,0,0,0}
1032regr_syyfloat8_regr_accum{0,0,0,0,0,0}
1033regr_sxyfloat8_regr_accum{0,0,0,0,0,0}
1034regr_avgxfloat8_regr_accum{0,0,0,0,0,0}
1035regr_avgyfloat8_regr_accum{0,0,0,0,0,0}
1036regr_r2float8_regr_accum{0,0,0,0,0,0}
1037regr_slopefloat8_regr_accum{0,0,0,0,0,0}
1038regr_interceptfloat8_regr_accum{0,0,0,0,0,0}
1039covar_popfloat8_regr_accum{0,0,0,0,0,0}
10310covar_sampfloat8_regr_accum{0,0,0,0,0,0}
10311corrfloat8_regr_accum{0,0,0,0,0,0}
1141pg_catalog.avgnumeric_avg_accumnone
1142pg_catalog.sumnumeric_avg_accumnone
1161xmlaggxmlconcat2none
1171json_aggjson_agg_transfnnone
1181json_object_aggjson_object_agg_transfnnone
1191jsonb_aggjsonb_agg_transfnnone
1201jsonb_object_aggjsonb_object_agg_transfnnone
1211pg_catalog.minenum_smallernone
1221pg_catalog.maxenum_largernone
1231pg_catalog.string_aggstring_agg_transfnnone
1241pg_catalog.string_aggbytea_string_agg_transfnnone
1251pg_catalog.maxnetwork_largernone
1261pg_catalog.minnetwork_smallernone
1271pg_catalog.percentile_discordered_set_transitionnone
1272pg_catalog.percentile_contordered_set_transitionnone
1273pg_catalog.percentile_contordered_set_transitionnone
1274pg_catalog.percentile_discordered_set_transitionnone
1275pg_catalog.percentile_contordered_set_transitionnone
1276pg_catalog.percentile_contordered_set_transitionnone
1277modeordered_set_transitionnone
1341pg_catalog.rankordered_set_transition_multinone
1342pg_catalog.percent_rankordered_set_transition_multinone
1343pg_catalog.cume_distordered_set_transition_multinone
1344pg_catalog.dense_rankordered_set_transition_multinone
1381pg_catalog.array_aggarray_agg_array_transfnnone

我接下来抽取几个数据统计相关的,验证9.6的优化效果

  1. 103 | 1 | regr_sxx | float8_regr_accum | {0,0,0,0,0,0}
  2. 103 | 2 | regr_syy | float8_regr_accum | {0,0,0,0,0,0}
  3. 103 | 3 | regr_sxy | float8_regr_accum | {0,0,0,0,0,0}
  4. 103 | 4 | regr_avgx | float8_regr_accum | {0,0,0,0,0,0}
  5. 103 | 5 | regr_avgy | float8_regr_accum | {0,0,0,0,0,0}
  6. 103 | 6 | regr_r2 | float8_regr_accum | {0,0,0,0,0,0}
  7. 103 | 7 | regr_slope | float8_regr_accum | {0,0,0,0,0,0}
  8. 103 | 8 | regr_intercept | float8_regr_accum | {0,0,0,0,0,0}
  9. 103 | 9 | covar_pop | float8_regr_accum | {0,0,0,0,0,0}
  10. 103 | 10 | covar_samp | float8_regr_accum | {0,0,0,0,0,0}
  11. 103 | 11 | corr | float8_regr_accum | {0,0,0,0,0,0}

这几个聚合函数的用法如下

https://www.postgresql.org/docs/9.6/static/functions-aggregate.html

FunctionArgument TypeReturn TypePartial ModeDescription
corr(Y, X)double precisiondouble precisionYescorrelation coefficient
covar_pop(Y, X)double precisiondouble precisionYespopulation covariance
covar_samp(Y, X)double precisiondouble precisionYessample covariance
regr_avgx(Y, X)double precisiondouble precisionYesaverage of the independent variable (sum(X)/N)
regr_avgy(Y, X)double precisiondouble precisionYesaverage of the dependent variable (sum(Y)/N)
regr_intercept(Y, X)double precisiondouble precisionYesy-intercept of the least-squares-fit linear equation determined by the (X, Y) pairs
regr_r2(Y, X)double precisiondouble precisionYessquare of the correlation coefficient
regr_slope(Y, X)double precisiondouble precisionYesslope of the least-squares-fit linear equation determined by the (X, Y) pairs
regr_sxx(Y, X)double precisiondouble precisionYessum(X^2) - sum(X)^2/N (“sum of squares” of the independent variable)
regr_sxy(Y, X)double precisiondouble precisionYessum(XY) - sum(X) sum(Y)/N (“sum of products” of independent times dependent variable)
regr_syy(Y, X)double precisiondouble precisionYessum(Y^2) - sum(Y)^2/N (“sum of squares” of the dependent variable)

对比测试

测试5000万条记录

  1. postgres=# create table agg_test(x float8, y float8);
  2. postgres=# insert into agg_test select 10000*random(), 10000*random() from generate_series(1,50000000);

1. 9.6 非并行
聚合计算耗费了7.1秒

  1. postgres=# show max_parallel_workers_per_gather ;
  2. max_parallel_workers_per_gather
  3. ---------------------------------
  4. 0
  5. (1 row)
  6. postgres=# explain (analyze,verbose,timing,costs,buffers) select corr(y,x), covar_pop(y,x), covar_samp(y,x), regr_avgx(y,x), regr_avgy(y,x), regr_intercept(y,x), regr_r2(y,x), regr_slope(y,x), regr_sxx(y,x), regr_sxy(y,x), regr_syy(y,x) from agg_test ;
  7. QUERY PLAN
  8. --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  9. Aggregate (cost=2145276.13..2145276.14 rows=1 width=88) (actual time=11703.472..11703.472 rows=1 loops=1)
  10. Output: corr(y, x), covar_pop(y, x), covar_samp(y, x), regr_avgx(y, x), regr_avgy(y, x), regr_intercept(y, x), regr_r2(y, x), regr_slope(y, x), regr_sxx(y, x), regr_sxy(y, x), regr_syy(y, x)
  11. Buffers: shared hit=270271
  12. -> Seq Scan on public.agg_test (cost=0.00..770272.36 rows=50000136 width=16) (actual time=0.010..4594.588 rows=50000000 loops=1)
  13. Output: x, y
  14. Buffers: shared hit=270271
  15. Planning time: 0.082 ms
  16. Execution time: 11703.541 ms
  17. (8 rows)

2. 9.5
聚合计算耗费了36.1秒

  1. postgres=# explain (analyze,verbose,timing,costs,buffers) select corr(y,x), covar_pop(y,x), covar_samp(y,x), regr_avgx(y,x), regr_avgy(y,x), regr_intercept(y,x), regr_r2(y,x), regr_slope(y,x), regr_sxx(y,x), regr_sxy(y,x), regr_syy(y,x) from agg_test ;
  2. QUERY PLAN
  3. --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  4. Aggregate (cost=2145276.13..2145276.14 rows=1 width=16) (actual time=40563.668..40563.669 rows=1 loops=1)
  5. Output: corr(y, x), covar_pop(y, x), covar_samp(y, x), regr_avgx(y, x), regr_avgy(y, x), regr_intercept(y, x), regr_r2(y, x), regr_slope(y, x), regr_sxx(y, x), regr_sxy(y, x), regr_syy(y, x)
  6. Buffers: shared hit=270271
  7. -> Seq Scan on public.agg_test (cost=0.00..770272.36 rows=50000136 width=16) (actual time=0.009..4481.032 rows=50000000 loops=1)
  8. Output: x, y
  9. Buffers: shared hit=270271
  10. Planning time: 0.063 ms
  11. Execution time: 40563.742 ms
  12. (8 rows)

3. 9.6 并行
聚合计算约耗费0.2秒

  1. postgres=# set max_parallel_workers_per_gather =128;
  2. postgres=# set force_parallel_mode =on;
  3. postgres=# alter table agg_test set (parallel_workers =32);
  4. postgres=# explain (analyze,verbose,timing,costs,buffers) select corr(y,x), covar_pop(y,x), covar_samp(y,x), regr_avgx(y,x), regr_avgy(y,x), regr_intercept(y,x), regr_r2(y,x), regr_slope(y,x), regr_sxx(y,x), regr_sxy(y,x), regr_syy(y,x) from agg_test ;
  5. QUERY PLAN
  6. Finalize Aggregate (cost=329869.02..329869.03 rows=1 width=88) (actual time=456.718..456.718 rows=1 loops=1)
  7. Output: corr(y, x), covar_pop(y, x), covar_samp(y, x), regr_avgx(y, x), regr_avgy(y, x), regr_intercept(y, x), regr_r2(y, x), regr_slope(y, x), regr_sxx(y, x), regr_sxy(y, x), regr_syy(y, x)
  8. Buffers: shared hit=275071
  9. -> Gather (cost=329864.90..329868.11 rows=32 width=352) (actual time=456.567..456.612 rows=33 loops=1)
  10. Output: (PARTIAL corr(y, x)), (PARTIAL covar_pop(y, x)), (PARTIAL covar_samp(y, x)), (PARTIAL regr_avgx(y, x)), (PARTIAL regr_avgy(y, x)), (PARTIAL regr_intercept(y, x)), (PARTIAL regr_r2(y, x)), (PARTIAL regr_slope(y, x)), (PARTIAL regr_sxx(y, x)), (PARTIAL regr_sxy(y, x)), (PARTIAL regr_syy(y, x))
  11. Workers Planned: 32
  12. Workers Launched: 32
  13. Buffers: shared hit=275071
  14. -> Partial Aggregate (cost=328864.90..328864.91 rows=1 width=352) (actual time=451.769..451.769 rows=1 loops=33)
  15. Output: PARTIAL corr(y, x), PARTIAL covar_pop(y, x), PARTIAL covar_samp(y, x), PARTIAL regr_avgx(y, x), PARTIAL regr_avgy(y, x), PARTIAL regr_intercept(y, x), PARTIAL regr_r2(y, x), PARTIAL regr_slope(y, x), PARTIAL regr_sxx(y, x), PARTIAL regr_sxy(y, x), PARTIAL regr_syy(y, x)
  16. Buffers: shared hit=270655
  17. Worker 0: actual time=448.888..448.888 rows=1 loops=1
  18. Buffers: shared hit=8265
  19. Worker 1: actual time=449.881..449.881 rows=1 loops=1
  20. Buffers: shared hit=8357
  21. Worker 2: actual time=450.175..450.176 rows=1 loops=1
  22. Buffers: shared hit=8295
  23. Worker 3: actual time=450.306..450.306 rows=1 loops=1
  24. Buffers: shared hit=8357
  25. Worker 4: actual time=449.567..449.567 rows=1 loops=1
  26. Buffers: shared hit=6844
  27. Worker 5: actual time=450.467..450.467 rows=1 loops=1
  28. Buffers: shared hit=8360
  29. Worker 6: actual time=450.574..450.574 rows=1 loops=1
  30. Buffers: shared hit=7898
  31. Worker 7: actual time=450.665..450.665 rows=1 loops=1
  32. Buffers: shared hit=8397
  33. Worker 8: actual time=450.719..450.719 rows=1 loops=1
  34. Buffers: shared hit=8084
  35. Worker 9: actual time=450.922..450.922 rows=1 loops=1
  36. Buffers: shared hit=8405
  37. Worker 10: actual time=451.004..451.004 rows=1 loops=1
  38. Buffers: shared hit=5421
  39. Worker 11: actual time=451.175..451.175 rows=1 loops=1
  40. Buffers: shared hit=8431
  41. Worker 12: actual time=451.316..451.316 rows=1 loops=1
  42. Buffers: shared hit=8276
  43. Worker 13: actual time=451.457..451.457 rows=1 loops=1
  44. Buffers: shared hit=8431
  45. Worker 14: actual time=451.506..451.506 rows=1 loops=1
  46. Buffers: shared hit=8163
  47. Worker 15: actual time=451.670..451.670 rows=1 loops=1
  48. Buffers: shared hit=7959
  49. Worker 16: actual time=451.797..451.797 rows=1 loops=1
  50. Buffers: shared hit=8428
  51. Worker 17: actual time=451.875..451.875 rows=1 loops=1
  52. Buffers: shared hit=8265
  53. Worker 18: actual time=451.982..451.982 rows=1 loops=1
  54. Buffers: shared hit=8444
  55. Worker 19: actual time=452.127..452.127 rows=1 loops=1
  56. Buffers: shared hit=7717
  57. Worker 20: actual time=452.232..452.232 rows=1 loops=1
  58. Buffers: shared hit=8450
  59. Worker 21: actual time=452.331..452.331 rows=1 loops=1
  60. Buffers: shared hit=8304
  61. Worker 22: actual time=452.450..452.450 rows=1 loops=1
  62. Buffers: shared hit=8455
  63. Worker 23: actual time=452.592..452.592 rows=1 loops=1
  64. Buffers: shared hit=8367
  65. Worker 24: actual time=452.679..452.679 rows=1 loops=1
  66. Buffers: shared hit=8460
  67. Worker 25: actual time=452.814..452.815 rows=1 loops=1
  68. Buffers: shared hit=8445
  69. Worker 26: actual time=452.969..452.969 rows=1 loops=1
  70. Buffers: shared hit=8465
  71. Worker 27: actual time=452.999..452.999 rows=1 loops=1
  72. Buffers: shared hit=8454
  73. Worker 28: actual time=453.193..453.193 rows=1 loops=1
  74. Buffers: shared hit=8462
  75. Worker 29: actual time=452.985..452.985 rows=1 loops=1
  76. Buffers: shared hit=8437
  77. Worker 30: actual time=453.482..453.483 rows=1 loops=1
  78. Buffers: shared hit=8348
  79. Worker 31: actual time=453.505..453.505 rows=1 loops=1
  80. Buffers: shared hit=8182
  81. -> Parallel Seq Scan on public.agg_test (cost=0.00..285896.04 rows=1562504 width=16) (actual time=0.046..248.331 rows=1515152 loops=33)
  82. Output: y, x
  83. Buffers: shared hit=270655
  84. Worker 0: actual time=0.058..247.983 rows=1526805 loops=1
  85. Buffers: shared hit=8265
  86. Worker 1: actual time=0.047..249.121 rows=1543825 loops=1
  87. Buffers: shared hit=8357
  88. Worker 2: actual time=0.047..249.206 rows=1532355 loops=1
  89. Buffers: shared hit=8295
  90. Worker 3: actual time=0.047..249.914 rows=1543825 loops=1
  91. Buffers: shared hit=8357
  92. Worker 4: actual time=0.069..244.072 rows=1263920 loops=1
  93. Buffers: shared hit=6844
  94. Worker 5: actual time=0.046..250.046 rows=1544380 loops=1
  95. Buffers: shared hit=8360
  96. Worker 6: actual time=0.047..247.860 rows=1458910 loops=1
  97. Buffers: shared hit=7898
  98. Worker 7: actual time=0.045..249.471 rows=1551225 loops=1
  99. Buffers: shared hit=8397
  100. Worker 8: actual time=0.047..247.850 rows=1493320 loops=1
  101. Buffers: shared hit=8084
  102. Worker 9: actual time=0.049..249.905 rows=1552705 loops=1
  103. Buffers: shared hit=8405
  104. Worker 10: actual time=0.048..240.578 rows=1000665 loops=1
  105. Buffers: shared hit=5421
  106. Worker 11: actual time=0.043..249.234 rows=1557515 loops=1
  107. Buffers: shared hit=8431
  108. Worker 12: actual time=0.044..248.830 rows=1528840 loops=1
  109. Buffers: shared hit=8276
  110. Worker 13: actual time=0.046..249.576 rows=1557515 loops=1
  111. Buffers: shared hit=8431
  112. Worker 14: actual time=0.043..248.819 rows=1507935 loops=1
  113. Buffers: shared hit=8163
  114. Worker 15: actual time=0.046..248.303 rows=1470195 loops=1
  115. Buffers: shared hit=7959
  116. Worker 16: actual time=0.045..249.997 rows=1556960 loops=1
  117. Buffers: shared hit=8428
  118. Worker 17: actual time=0.046..249.282 rows=1526805 loops=1
  119. Buffers: shared hit=8265
  120. Worker 18: actual time=0.043..249.849 rows=1559785 loops=1
  121. Buffers: shared hit=8444
  122. Worker 19: actual time=0.047..247.241 rows=1425425 loops=1
  123. Buffers: shared hit=7717
  124. Worker 20: actual time=0.043..250.134 rows=1561030 loops=1
  125. Buffers: shared hit=8450
  126. Worker 21: actual time=0.044..249.316 rows=1534020 loops=1
  127. Buffers: shared hit=8304
  128. Worker 22: actual time=0.043..250.169 rows=1561955 loops=1
  129. Buffers: shared hit=8455
  130. Worker 23: actual time=0.045..249.550 rows=1545675 loops=1
  131. Buffers: shared hit=8367
  132. Worker 24: actual time=0.044..250.062 rows=1562880 loops=1
  133. Buffers: shared hit=8460
  134. Worker 25: actual time=0.043..250.298 rows=1560105 loops=1
  135. Buffers: shared hit=8445
  136. Worker 26: actual time=0.043..249.939 rows=1563805 loops=1
  137. Buffers: shared hit=8465
  138. Worker 27: actual time=0.049..250.511 rows=1561770 loops=1
  139. Buffers: shared hit=8454
  140. Worker 28: actual time=0.045..250.523 rows=1563250 loops=1
  141. Buffers: shared hit=8462
  142. Worker 29: actual time=0.049..250.492 rows=1558625 loops=1
  143. Buffers: shared hit=8437
  144. Worker 30: actual time=0.053..247.131 rows=1542160 loops=1
  145. Buffers: shared hit=8348
  146. Worker 31: actual time=0.053..249.789 rows=1511450 loops=1
  147. Buffers: shared hit=8182
  148. Planning time: 0.101 ms
  149. Execution time: 483.888 ms
  150. (144 rows)

9.6的优化效果很明显,在没有使用并行的情况下,聚合操作已经有约5倍的性能提升。

结果对比

版本9.69.59.6并行(32)
5000万记录(11个聚合函数)耗时(秒)7.136.10.2

pic3

代码

https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=804163bc25e979fcd91b02e58fa2d1c6b587cc65

涉及如下

  1. src/backend/executor/execQual.c diff | blob | blame | history
  2. src/backend/executor/nodeAgg.c diff | blob | blame | history
  3. src/backend/executor/nodeWindowAgg.c diff | blob | blame | history
  4. src/backend/parser/parse_agg.c diff | blob | blame | history
  5. src/include/nodes/execnodes.h diff | blob | blame | history
  6. src/include/parser/parse_agg.h diff | blob | blame | history
  7. src/test/regress/expected/aggregates.out diff | blob | blame | history
  8. src/test/regress/sql/aggregates.sql diff | blob | blame | history

小结

在统计学中,大多数的统计算法的中间结果都是可以共用的,例如sum,avg; 方差,相关性,count,sum等运算;

PostgreSQL 9.6很好的抓住了这样的特征,对初始条件一致,中间算法一致的聚合函数,在同一个分组中数据只需要计算一遍,大大降低了CPU的开销,提高了统计效率。

这个思路与LLVM有一些神似的地方,不过LLVM的适用场景更广。