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

1. 背景

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

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

2. 数据立方体

业务场景

有一张数据表的结构如下

字段名 字段类型 注释
fserver_ip string 服务器ip
finterface string 服务接口名
ferror_code string 错误码
fcall_num int 接口调用次数
fcall_time int 接口调用耗时

现在有一系列的查询需求

  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

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

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   <> '
        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. 参数说明

参数 是否必传 样例 注释
参数1 5 指的是传入TRANSFORM的列数
参数2 3 指的是前几列是维度,故而需要把维度放在前面
参数3 all 指的是汇总列替代的值,比如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)

你可能感兴趣的:([大数据]Hive)