[基础工具]离线数据立方体构建工具

1. 背景

在数据的实际使用过程中,我们经常需要进行多维度的组合汇总,做一个数据立方体。常见的方法是在各个维度下进行group by,建立多个任务,这样不利于代码查看和维护,同时也浪费开发时间。

数据立方体与OLAP:http://webdataanalysis.net/web-data-warehouse/data-cube-and-olap/

2. 数据立方体

业务场景

有一张数据表的结构如下

字段名字段类型注释
fserver_ipstring服务器ip
finterfacestring服务接口名
ferror_codestring错误码
fcall_numint接口调用次数
fcall_timeint接口调用耗时

现在有一系列的查询需求

  1. 查看服务器ip、服务接口名、指定错误码下的接口调用次数、接口调用总耗时;
  2. 查看服务器ip下所有的接口调用次数、接口调用总耗时;
  3. 查看错误码下所有的接口调用次数、接口调用总耗时;
  4. 查看服务器ip、错误码下所有的接口调用次数、接口调用总耗时;

……

上面各种查询需求,都是针对不同维度进行组合统计的,用最笨的办法,可能需要写一堆group by xxx,但通过建立数据立方体,就可以在对某个(些)维度进行汇总统计时进行上卷等操作。

问题简化

建立数据立方体之前,先要明确需要处理哪些维度、指标。下面是一个数据立方体


维度 指标
排列组合 fserver_ip finterface ferror_code sum(fcall_num) sum(fcall_time)
不组合(明细)




维度1组合 ✔️

维度2组合
✔️


维度3组合

✔️

维度12组合 ✔️✔️


维度13组合 ✔️
✔️

维度23组合
✔️✔️

维度123组合 ✔️✔️✔️

问题分析

实际上对于每个维度而言,是“组合”与“不组合”两种可能,因此 M 个维度对应的组合数是 2M (或从排列组合角度,维度的组合数 = C0M+C1M+C2M+...+CMM=2M

3. 构建数据立方体

本文将使用hive streaming + python构建数据立方体

Hive Tranform方式的官方文档:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Transform

img_001

如上图,hive streaming方式实际上是将hive sql查询的记录,逐条传递给jar/python等进行处理。构建数据立方体的过程,是需要对每条明细数据处理 2M 次来生成各种维度组合,处理过程如下

img_002

3.1. Python代码

cube.py

def binary_reflect_gray_code(n):
    """
    生成n位的二进制反格雷码, 即n个元素的全组合
    :param n:
    :return:
    """
    if n == 1:
        return ["0", "1"]
    L1 = binary_reflect_gray_code(n-1)
    L2 = copy.deepcopy(L1)
    L2.reverse()
    L1 = ["0" + l for l in L1]
    L2 = ["1" + l for l in L2]
    L = L1 + L2
    return L


if __name__ == "__main__":
    not_group_set = []
    if len(sys.argv) < 4:
        print 'Error! Not Enough Params.'
        print 'Usage: python cube.py <field_num> <dimension_num> <> <not_group_set>'
        sys.exit(1)
    elif len(sys.argv) > 4:
        _, field_cnt, dimension_cnt, split_sign = sys.argv[0:4]
        not_group_set = sys.argv[4:]
    elif len(sys.argv) == 4:
        _, field_cnt, dimension_cnt, split_sign = sys.argv[0:4]

    combination_list = binary_reflect_gray_code(dimension_cnt)
    for line in sys.stdin:
        kv = line.replace('\n', '').split('\t')
        for combination in combination_list:
            if combination not in not_group_set:
                for item in range(int(field_cnt)):
                    if item < int(dimension_cnt):
                        if combination[item] == '0':
                            print '%s\t' % (kv[item]),
                        else:
                            print '%s\t' % (split_sign),
                    else:
                        print '%s\t' % (kv[item]),
                print
3.2. 参数说明
参数是否必传样例注释
参数15指的是传入TRANSFORM的列数
参数23指的是前几列是维度,故而需要把维度放在前面
参数3all指的是汇总列替代的值,比如all
参数4-n选填00 01 10指的是不需要维度汇总的组合,支持多个组合。比如不需要第一列汇总,则写10和11(两位数字表示维度数为2,其中第一位1表示按第一个维度汇总,比如指定了10,则表示过滤按第一列汇总的数据)
3.3. 使用说明

1) 新建文件cube.sql,添加如下HQL脚本

add file cube.py;
select
  fserver_ip,
  finterface,
  ferr_code,
  sum(fcall_num) as fcall_num,
  sum(fcall_time) as fcall_time
from (
  from dp_monitor.t_monitor_acc
  select TRANSFORM(fserver_ip, finterface, ferr_code, fcall_num, fcall_time)
  using 'python cube.py 5 3 all' as
  fserver_ip, finterface, ferr_code, fcall_num, fcall_time where fdate = '2017-10-16'
) tmp
group by fserver_ip, finterface, ferr_code
order by fserver_ip, finterface, ferr_code;

2) 使用hive命令行执行HQL脚本

hive -f cube.sql
3.4. 如何进行本地测试

本地导出数据进行测试:

hive -e "select fserver_ip, finterface, ferr_code, fcall_num, fcall_time from dp_monitor.t_monitor_acc where fdate = '2017-10-16' limit 100" > a.txt

cat a.txt | python cube.py 5 3 all
3.5. 性能效率

假设:

  • 维度数 = M(0 < M < 102 ),脚本会先生成 M 位的二进制组合,M 位的二进制组合共有 2M 个;
  • 表数据 = N(0 < N < 1010 ),脚本会逐条数据进行处理,每条数据会处理 2M 次;

时间复杂度为:

O(N2M)

数据立方体是复杂计算的抽象。Datacube 是用 Java 实现的,可插入数据库后端支持的数据立方体。 datacube 是用来存储大数据点的聚合信息。数据立方体存储的是有趣输入数据点的子集。比如,你正在编写一个 web 服务器日志分析工具,你的输入点可能是日志行,你可能会计算每个浏览器的类型,每个浏览器的版本,操作系统类型,操作系统版本和其他属性。同时你可能会需要计算一个特定的组合计数(浏览器类型,浏览器版本,操作系统类型), (浏览器类型,浏览器版本,操作系统类型,操作系统版本),等等。 这对快速添加和修改计数是个很大的挑战,会浪费很多时间在数据库代码和重新用新计数器处理旧数据。而数据立方体就可以帮忙解决这些问题。 Urban Airship 使用 datacube 项目来支持他们的移动端应用的分析栈,每个节点每秒处理大约 10 K 的事件。 datacube 要求 JDK 1.6。 特性 性能: 高速异步 IO 后端处理 使用 Hadoop MapReduce 进行批量加载 可插入数据库接口 datacube 暂时只支持 HBase 数据库后端。 示例: IdService idService = new CachingIdService(5, new MapIdService()); ConcurrentMap backingMap =          new ConcurrentHashMap(); DbHarness dbHarness = new MapDbHarness(backingMap, LongOp.DESERIALIZER,          CommitType.READ_COMBINE_CAS, idService); HourDayMonthBucketer hourDayMonthBucketer = new HourDayMonthBucketer(); Dimension time = new Dimension("time", hourDayMonthBucketer, false, 8); Dimension zipcode = new Dimension("zipcode", new StringToBytesBucketer(),          true, 5); DataCubeIo cubeIo = null; DataCube cube; Rollup hourAndZipRollup = new Rollup(zipcode, time, HourDayMonthBucketer.hours); Rollup dayAndZipRollup = new Rollup(zipcode, time, HourDayMonthBucketer.days); Rollup hourRollup = new Rollup(time, HourDayMonthBucketer.hours); Rollup dayRollup = new Rollup(time, HourDayMonthBucketer.days); List dimensions =  ImmutableList.of(time, zipcode); List rollups = ImmutableList.of(hourAndZipRollup, dayAndZipRollup, hourRollup,         dayRollup); cube = new DataCube(dimensions, rollups); cubeIo = new DataCubeIo(cube, dbHarness, 1, Long.MAX_VALUE, SyncLevel.FULL_SYNC); DateTime now = new DateTime(DateTimeZone.UTC); // Do an increment of 5 for a certain time and zipcode cubeIo.writeSync(new LongOp(5), new WriteBuilder(cube)         .at(time, now)         .at(zipcode, "97201"))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值