hll 估值插件 在Greenplum中的使用 以及 分布式聚合函数优化思路

本文涉及的产品
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS SQL Server,基础系列 2核4GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
简介: 背景 在大数据分析中,通常会有一些估值的需求,例如估计某个时间段有多少新增用户,估计某个时间段有多少用户。 常用的估值算法如HyperLogLog,还有一些其他的估值算法。 可以参考http://www.pipelinedb.com/ 我在几年前写过如何在PostgreSQL中使用HLL,请

背景

在大数据分析中,通常会有一些估值的需求,例如估计某个时间段有多少新增用户,估计某个时间段有多少用户。

常用的估值算法如HyperLogLog,还有一些其他的估值算法。

可以参考
http://www.pipelinedb.com/

我在几年前写过如何在PostgreSQL中使用HLL,请参考
https://github.com/aggregateknowledge/postgresql-hll
http://blog.163.com/digoal@126/blog/static/16387704020131264480325/
http://blog.163.com/digoal@126/blog/static/1638770402013127917876/

这里要说的是在Greenplum中的使用。

Greenplum是个分布式数据库系统,特别需要注意的是它的聚合用法与单节点不一样。

我之前写过基于PostgreSQL的另一个分布式系统Postgres-XC的聚合函数的原理和写法,有兴趣的同学可以参考如下

http://blog.163.com/digoal@126/blog/static/16387704020134222140958/

Greenplum聚合函数原理简介

其实Greenplum的聚合函数用法和postgres-xc及其的相似。

语法如下

CREATE [ORDERED] AGGREGATE name (input_data_type [ , ... ])  
( SFUNC = sfunc,  
STYPE = state_data_type  
[, PREFUNC = prefunc]  
[, FINALFUNC = ffunc]  
[, INITCOND = initial_condition]  
[, SORTOP = sort_operator] )  

有两种聚合运算模式可选
1. 如果只配置了sfunc,则相关数据全部收集到master节点,在master节点对所有数据依条加上sfunc的结果(第一次可选为initcond)输入给sfunc计算,直到所有数据都跑完sfunc,最后如果设置了finalfunc,则计算并得到最终结果。

2. 如果同时配置了sfunc和prefunc,则在segment节点并行完成sfunc,然后将segment节点执行的结果发给master,在master调用prefunc进行再次聚合,输出结果,如果配置了finalfunc,则这个结果再给finalfunc执行并输出最终结果。

过程如下 :

sfunc( internal-state, next-data-values ) ---> next-internal-state  
prefunc( internal-state, internal-state ) ---> next-internal-state  
ffunc( internal-state ) ---> aggregate-value  

在Greenplum上编译安装hll

git clone https://github.com/aggregateknowledge/postgresql-hll  
cd postgresql-hll  
export PATH=/home/digoal/gp/bin:$PATH  
make clean  
make  
make install  

确认hll.so正确的安装

$ ll /home/digoal/gp/lib/postgresql/hll.so  
-rwxr-xr-x 1 digoal digoal 116889 Aug 25 22:25 /home/digoal/gp/lib/postgresql/hll.so  

因为postgres-hll是兼容9.0以上的create extension模式的,所以得看看控制文件

cat hll.control   

comment = 'type for storing hyperloglog data'  
default_version = '2.10.0'  
module_pathname = '$libdir/hll'  

然后需要修改sql文件

vi hll--2.10.0.sql   

# MODULE_PATHNAME替换成$libdir/hll  

:%s/MODULE_PATHNAME/\$libdir\/hll/g  

在需要使用hll的数据库执行hll--2.10.0.sql

psql -f ./hll--2.10.0.sql  

测试

测试的数据类型包括int2, int4, int8, text, bytea.
分成20个组,一共100万数据。

postgres=# create table test(groupid int, id int, info text, si int2, bi int8, f bytea );  
CREATE TABLE  
postgres=# insert into test select mod(id,20), id, md5(id::text), mod(id, 32760), id*2, md5(id::text)::bytea from generate_series(1,1000000) t(id);  
INSERT 0 1000000  
postgres=# create table hll_test(groupid int, id hll, info hll, si hll, bi hll, f hll);  
CREATE TABLE  

按分组聚合后,插入一张结果表

postgres=# insert into hll_test select groupid, hll_add_agg(hll_hash_integer(id)), hll_add_agg(hll_hash_text(info)), hll_add_agg(hll_hash_smallint(si)), hll_add_agg(hll_hash_bigint(bi)), hll_add_agg(hll_hash_bytea(f)) from test group by groupid;  
INSERT 0 20  

查询每个分组的唯一值以及按阶段的唯一值。

每组,每个维度有多少唯一值

postgres=# select groupid, hll_cardinality(id), hll_cardinality(si), hll_cardinality(bi), hll_cardinality(f), hll_cardinality(info) from hll_test order by 1;  
 groupid | hll_cardinality  | hll_cardinality  | hll_cardinality  | hll_cardinality  | hll_cardinality    
---------+------------------+------------------+------------------+------------------+------------------  
       0 | 48978.0103680088 | 1647.82616880183 |  50061.344798351 | 50291.2509120929 | 50291.2509120929  
       1 | 51741.8614936294 | 1612.36207917836 | 49284.9020691283 | 49566.9703531116 | 49566.9703531116  
       2 | 50125.2622181007 | 1656.78898398222 | 48857.7015986003 | 50829.2972953538 | 50829.2972953538  
       3 | 49432.4073263084 | 1638.90240740621 | 51253.0385002203 | 50091.9306033461 | 50091.9306033461  
       4 | 48917.5103132368 | 1652.30267331635 | 50832.4721751387 | 50482.4523284526 | 50482.4523284526  
       5 | 48869.3346787424 | 1686.19202905657 | 48969.8013300078 | 49222.6882488941 | 49222.6882488941  
       6 | 51705.6275895237 | 1661.28514385613 | 49935.7562032158 |  50130.294716446 |  50130.294716446  
       7 | 50394.3167326675 | 1679.36914656619 | 51657.2414098721 | 48798.3228462258 | 48798.3228462258  
       8 |  48718.737519243 | 1668.04794581865 | 49049.1473914624 |  50781.921725461 |  50781.921725461  
       9 | 50838.4114711808 | 1697.61425609909 | 49083.5959035864 | 51081.0313252514 | 51081.0313252514  
      10 | 49902.2001005683 |   1627.802109229 |  50436.564830256 |  50081.537109579 |  50081.537109579  
      11 |   48111.38122336 | 1634.45506581054 | 49123.4624391955 | 49922.9150526361 | 49922.9150526361  
      12 | 48846.4713840826 |  1599.2197880897 | 49787.1437434036 | 50234.5603529724 | 50234.5603529724  
      13 | 50941.2225741094 | 1679.36914656619 | 49016.4627290325 | 49298.6342587365 | 49298.6342587365  
      14 | 49463.2045504603 | 1683.91520836626 | 48084.4839509038 | 48703.0775230689 | 48703.0775230689  
      15 | 51273.4575447966 | 1652.30267331635 | 50053.6251645345 | 50288.1045937257 | 50288.1045937257  
      16 | 48350.9891007992 | 1634.45506581054 | 49485.1660515215 | 49953.3728129477 | 49953.3728129477  
      17 | 49278.5780270676 | 1661.28514385613 | 50607.8043111473 |  49688.123157263 |  49688.123157263  
      18 | 48704.9059740248 | 1650.06319797018 | 52629.3748697963 |  49426.729576471 |  49426.729576471  
      19 | 49326.5758972919 | 1634.45506581054 | 52245.5594837649 | 48453.7968119413 | 48453.7968119413  
(20 rows)  

所有数据范围,每个维度有多少唯一值

postgres=# SELECT hll_cardinality(hll_union_agg(id)), hll_cardinality(hll_union_agg(si)), hll_cardinality(hll_union_agg(bi)), hll_cardinality(hll_union_agg(f)), hll_cardinality(hll_union_agg(info)) FROM hll_test;  
 hll_cardinality  | hll_cardinality  | hll_cardinality  | hll_cardinality  | hll_cardinality    
------------------+------------------+------------------+------------------+------------------  
 981424.027645005 | 32006.8065955451 | 1008610.09767335 | 1027038.36191106 | 1027038.36191106  
(1 row)  

指定数据范围,每个维度又多少唯一值

postgres=# SELECT hll_cardinality(hll_union_agg(id)), hll_cardinality(hll_union_agg(si)), hll_cardinality(hll_union_agg(bi)), hll_cardinality(hll_union_agg(f)), hll_cardinality(hll_union_agg(info)) FROM hll_test where groupid in (1,3,9);  
 hll_cardinality  | hll_cardinality  | hll_cardinality  | hll_cardinality  | hll_cardinality    
------------------+------------------+------------------+------------------+------------------  
 149863.294108911 | 5227.10834581951 | 150151.868022589 | 150497.046827851 | 150497.046827851  
(1 row)  

对比PostgreSQL结果一致

结果略

debug

postgres=# select hll_print(hll_add(hll_add_agg(hll_hash_bigint(t)),hll_hash_bigint(1))) from generate_series(3,10) g(t);  
                                 hll_print                                   
---------------------------------------------------------------------------  
 EXPLICIT, 9 elements, nregs=2048, nbits=5, expthresh=-1(160), sparseon=1:   
 0: -5469109305088493887                                                     
 1:    19144387141682250                                                     
 2:   489182038263080531                                                     
 3:  1140754268591781659                                                     
 4:  1310323436750511730                                                     
 5:  1960224177162737638                                                     
 6:  3522142095546486706                                                     
 7:  4145513480871534457                                                     
 8:  6574508035858270988   
(1 row)  

调整精度

调整当前会话精度

返回为老的值

digoal=> select * from hll_set_defaults(15,5,-1,1);  
 o_log2m | o_regwidth | o_expthresh | o_sparseon   
---------+------------+-------------+------------  
      12 |          5 |          -1 |          1  
(1 row)  
Time: 0.330 ms  

调整默认精度
需要重新编译, 在hll.c中 :

// Defaults if type modifier values are not specified.  
//  
#define DEFAULT_LOG2M           15        
#define DEFAULT_REGWIDTH        5  
#define DEFAULT_EXPTHRESH       -1  
#define DEFAULT_SPARSEON        1  

static int32 g_default_log2m = DEFAULT_LOG2M;  
static int32 g_default_regwidth = DEFAULT_REGWIDTH;  
static int64 g_default_expthresh = DEFAULT_EXPTHRESH;  
static int32 g_default_sparseon = DEFAULT_SPARSEON;  

优化

我们前面讲了GP的聚合分两种使用,一种是全部收到MASTER节点执行,另一种是两阶段聚合。
如果记录数少,其实没有必要使用两阶段聚合。

以hll_union_agg聚合为例

临时结果保存为stype: internal类型.

每次调用hll_union_trans函数,输入一条hll值以及临时结果internal,输出internal。

最后调用hll_pack,将internal转换为hll输出

-- Union aggregate function, returns hll.  
--  
CREATE AGGREGATE hll_union_agg (hll) (  
       SFUNC = hll_union_trans,  
       STYPE = internal,  
       FINALFUNC = hll_pack  
);  

-- Union aggregate transition function, first arg internal data  
-- structure, second arg is a packed multiset.  
--  
CREATE FUNCTION hll_union_trans(internal, hll)  
     RETURNS internal  
     AS '$libdir/hll'  
     LANGUAGE C;  

-- Converts internal data structure into packed multiset.  
--  
CREATE FUNCTION hll_pack(internal)  
     RETURNS hll  
     AS '$libdir/hll'  
     LANGUAGE C;  

优化方法如下

在节点调用sfunc聚合,输入参数为(input_type数据 , 临时结果stype),输出为stype

sfunc( internal-state, next-data-values ) ---> next-internal-state  

segment第一阶段收集结果传输到master调用prefunc,输入(stype , stype),得到的结果为stype

prefunc( internal-state, internal-state ) ---> next-internal-state  

最后再将stype转换为聚合的输出类型即可(可选使用finalfunc)。

hll_union_agg 优化例子

CREATE AGGREGATE gp_hll_union_agg (hll) ( 
  SFUNC = hll_union, 
  prefunc = hll_union, -- 第二阶段函数
  STYPE = hll 
); 

hll_add_agg 优化例子

# select hll_empty();
  hll_empty   
--------------
 \021\213\177
(1 row)

CREATE AGGREGATE gp_hll_add_agg (hll_hashval) (
  SFUNC = hll_add, 
  STYPE = hll, 
  prefunc = hll_union, -- 第二阶段函数
  initcond='\021\213\177'  -- 初始值
); 
相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
9天前
|
机器学习/深度学习 存储 运维
分布式机器学习系统:设计原理、优化策略与实践经验
本文详细探讨了分布式机器学习系统的发展现状与挑战,重点分析了数据并行、模型并行等核心训练范式,以及参数服务器、优化器等关键组件的设计与实现。文章还深入讨论了混合精度训练、梯度累积、ZeRO优化器等高级特性,旨在提供一套全面的技术解决方案,以应对超大规模模型训练中的计算、存储及通信挑战。
33 4
|
2月前
|
算法
基于粒子群算法的分布式电源配电网重构优化matlab仿真
本研究利用粒子群算法(PSO)优化分布式电源配电网重构,通过Matlab仿真验证优化效果,对比重构前后的节点电压、网损、负荷均衡度、电压偏离及线路传输功率,并记录开关状态变化。PSO算法通过迭代更新粒子位置寻找最优解,旨在最小化网络损耗并提升供电可靠性。仿真结果显示优化后各项指标均有显著改善。
|
7月前
|
算法 调度
电动汽车集群并网的分布式鲁棒优化调度matlab
电动汽车集群并网的分布式鲁棒优化调度matlab
|
2月前
|
存储 缓存 数据处理
深度解析:Hologres分布式存储引擎设计原理及其优化策略
【10月更文挑战第9天】在大数据时代,数据的规模和复杂性不断增加,这对数据库系统提出了更高的要求。传统的单机数据库难以应对海量数据处理的需求,而分布式数据库通过水平扩展提供了更好的解决方案。阿里云推出的Hologres是一个实时交互式分析服务,它结合了OLAP(在线分析处理)与OLTP(在线事务处理)的优势,能够在大规模数据集上提供低延迟的数据查询能力。本文将深入探讨Hologres分布式存储引擎的设计原理,并介绍一些关键的优化策略。
131 0
|
4月前
|
存储 缓存 负载均衡
【PolarDB-X 技术揭秘】Lizard B+tree:揭秘分布式数据库索引优化的终极奥秘!
【8月更文挑战第25天】PolarDB-X是阿里云的一款分布式数据库产品,其核心组件Lizard B+tree针对分布式环境优化,解决了传统B+tree面临的数据分片与跨节点查询等问题。Lizard B+tree通过一致性哈希实现数据分片,确保分布式一致性;智能分区实现了负载均衡;高效的搜索算法与缓存机制降低了查询延迟;副本机制确保了系统的高可用性。此外,PolarDB-X通过自适应分支因子、缓存优化、异步写入、数据压缩和智能分片等策略进一步提升了Lizard B+tree的性能,使其能够在分布式环境下提供高性能的索引服务。这些优化不仅提高了查询速度,还确保了系统的稳定性和可靠性。
97 5
|
4月前
|
机器学习/深度学习 人工智能 负载均衡
【AI大模型】分布式训练:深入探索与实践优化
在人工智能的浩瀚宇宙中,AI大模型以其惊人的性能和广泛的应用前景,正引领着技术创新的浪潮。然而,随着模型参数的指数级增长,传统的单机训练方式已难以满足需求。分布式训练作为应对这一挑战的关键技术,正逐渐成为AI研发中的标配。
209 5
|
4月前
|
机器学习/深度学习 资源调度 PyTorch
面向大规模分布式训练的资源调度与优化策略
【8月更文第15天】随着深度学习模型的复杂度不断提高,对计算资源的需求也日益增长。为了加速训练过程并降低运行成本,高效的资源调度和优化策略变得至关重要。本文将探讨在大规模分布式训练场景下如何有效地进行资源调度,并通过具体的代码示例来展示这些策略的实际应用。
449 1
|
4月前
|
C# UED 定位技术
WPF控件大全:初学者必读,掌握控件使用技巧,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,控件是实现用户界面交互的关键元素。WPF提供了丰富的控件库,包括基础控件(如`Button`、`TextBox`)、布局控件(如`StackPanel`、`Grid`)、数据绑定控件(如`ListBox`、`DataGrid`)等。本文将介绍这些控件的基本分类及使用技巧,并通过示例代码展示如何在项目中应用。合理选择控件并利用布局控件和数据绑定功能,可以提升用户体验和程序性能。
77 0
|
4月前
|
自然语言处理 Java
自研分布式训练框架EPL问题之实现显存的极致优化如何解决
自研分布式训练框架EPL问题之实现显存的极致优化如何解决
|
4月前
|
SQL 存储 分布式计算
神龙大数据加速引擎MRACC问题之RDMA技术帮助大数据分布式计算优化如何解决
神龙大数据加速引擎MRACC问题之RDMA技术帮助大数据分布式计算优化如何解决
65 0