1. 引言

在做OLAP数据分析时,常常会遇到过滤分析需求,比如:除去只有性别、常驻地标签的用户,计算广告媒体上的覆盖UV。OLAP解决方案Kylin不支持复杂数据类型(array、struct、map),要求数据输入Schema必须是平铺的,但是平铺后丢失了用户的聚合标签信息,而没有办法判断某一个用户是否只有性别、常驻地标签。显然,我们需要一种支持复杂数据类型的OLAP数据库;底层为Lucene的Elasticsearch正在向OLAP融合,腾讯内部已经用基于Lucene的分析数据库Hermes来做多维数据分析。

Elasticsearch(ES)在设计之初是用来做全文检索的搜索引擎,但随着倒排索引所表现出来优秀的查询性能,有越来越多人拿它做分析数据库使。可将ES视作文档型NoSQL数据库,一般情况下将具有相同schema的文档(document)归属于一个type,所有的文档存储于某一个index;ES与RDBMS的概念对比如下:

Relational DB ⇒ Databases ⇒ Tables ⇒ Rows ⇒ Columns

Elasticsearch ⇒ Indices ⇒ Types ⇒ Documents ⇒ Fields

2. 写数据

广告日志与标签数据均落在Hive表,并且ES官方提供与Hive的集成。因此,我们首选用Hive向ES写数据。首先,采用ES做OLAP分析引擎,创建表如下:

  1. add jar /path/elasticsearch-hadoop-2.3.1.jar;
  2. create external table ad_tag (
  3. dvc string,
  4. medias array < string >,
  5. c1_arr array < string >,
  6. week_time string
  7. ) stored by 'org.elasticsearch.hadoop.hive.EsStorageHandler' tblproperties(
  8. 'es.nodes' = '<ip1>:9200,<ip2>:9200',
  9. 'es.resource' = 'ad-{week_time}/tag',
  10. 'es.mapping.exclude' = 'week_time'
  11. );

在设计Hive表结构时,ES的计算UV的distinct count(cardinality)存在着计算误差;因此,我们按dvc对其他字段做了聚合,UV的计算转换成了ES doc命中数。其中,es.nodes表示ES的节点,只需配置一个节点即可;es.resource对应于ES的Index/Type;es.mapping.exclude在写ES时不会被索引的字段。因我们只有写操作而没有通过Hive查询ES数据,因此并没有设置es.query。Hive向ES写数据如下:

  1. set hive.map.aggr = false;
  2. insert overwrite table ad_tag
  3. select
  4. media,
  5. a.dvc as dvc,
  6. case when c1_arr is null then array('empty') else c1_arr end as c1_arr,
  7. '2016-10-08' as week_time
  8. from
  9. (
  10. select
  11. dvc,
  12. app_name as media
  13. from
  14. ad_log
  15. where
  16. is_exposure = '1'
  17. and day_time between date_sub('2016-10-08', 6)
  18. and '2016-10-08'
  19. group by
  20. dvc,
  21. app_name
  22. ) a
  23. left outer join (
  24. select
  25. dvc,
  26. collect_set(c1) as c1_arr
  27. from
  28. tag lateral view inline(tag) in_tb
  29. where
  30. day_time = '2016-10-08'
  31. group by
  32. dvc
  33. ) b on a.dvc = b.dvc;

在写ES时,在构建索引时不需要分词,通过PUT index template方式实现之:

  1. {
  2. "template": "ad*",
  3. "mappings": {
  4. "_default_": {
  5. "dynamic_templates": [
  6. {
  7. "string_template": {
  8. "mapping": {
  9. "include_in_all": false,
  10. "index": "not_analyzed",
  11. "type": "string",
  12. "index_options": "docs"
  13. },
  14. "match": "*"
  15. }
  16. }
  17. ]
  18. }
  19. }
  20. }

3. 多维分析

ES官方的查询语言是DSL,主要分为两类:

  • Query,相当于SQL中的where部分,可套用filter、match等;
  • Aggregation,相当于SQL中的group by部分,在aggs内部也可以套用filter。

DSL可以嵌套,表达异常复杂的查询操作;但是,若以字符串拼接的方式实现DSL,则显得可维护性太差。因此,官方提供了elasticsearch-dsl-py,可以将DSL等同于一段Python代码。我们的多维分析器便是基于此实现的(Python 3.5 + elasticsearch_dsl 2.1.0)

整体上曝光UV、有标签的UV、除去常用标签UV,以及每一个媒体上曝光UV、有标签的UV、除去常用标签UV的分析(相当于group by media with cube):

  1. client = Elasticsearch(['<host1>'], port=20009, timeout=50)
  2. def per_media(index_name):
  3. """count(distinct dvc) group by media with cube"""
  4. ms = MultiSearch(using=client, index=index_name)
  5. all_doc = Search()
  6. all_doc.aggs.bucket('per_media', 'terms', field='medias', size=1000)
  7. tagged = Search().query('filtered', filter=~Q('term', c1_arr='empty'))
  8. tagged.aggs.bucket('per_media', 'terms', field='medias', size=1000)
  9. useful = Search().query('filtered', filter=~Q('term', c1_arr='empty') & Q('script',
  10. script="""['常驻地', '性别'].intersect(doc['c1_arr'].values).size() < doc['c1_arr'].values.size()"""))
  11. useful.aggs.bucket('per_media', 'terms', field='medias', size=1000)
  12. ms = ms.add(all_doc)
  13. ms = ms.add(tagged)
  14. ms = ms.add(useful)
  15. responses = ms.execute()
  16. result_list = []
  17. result_dict = defaultdict(lambda: [])
  18. for resp in responses: # get per media uv(all, tagged, useful_tagged)
  19. print("Query %d: %r." % (responses.index(resp), resp.search.to_dict()))
  20. result_list.append(resp.hits.total)
  21. for buck in resp.aggregations['per_media']['buckets']:
  22. result_dict[buck['key']].append(buck['doc_count'])
  23. for k, v in result_dict.items(): # fill up default value 0
  24. if len(v) < 3:
  25. result_dict[k] = v + [0] * (3 - len(v))
  26. return result_list, result_dict

媒体与标签组合维度下的UV统计:

  1. def per_media_c1(index_name):
  2. """return {(media, c1) -> tagged_uv}"""
  3. s = Search(using=client, index=index_name)
  4. tagged = s.query('filtered', filter=~Q('term', c1_arr='empty'))
  5. tagged.aggs.bucket('per_media', 'terms', field='medias', size=1000) \
  6. .bucket('per_c1', 'terms', field='c1_arr', size=100)
  7. result = {}
  8. response = tagged.execute()
  9. for buck in response.aggregations['per_media']['buckets']:
  10. key = buck['key']
  11. for b in buck['per_c1']['buckets']:
  12. result[(key, b['key'])] = b['doc_count']
  13. return result

轻量级OLAP(二):Hive + Elasticsearch的更多相关文章

  1. 二 Hive分桶

    二.Hive分桶 1.创建分桶表 create table t_buck (id string ,name string) clustered by (id) //根据id分桶 sorted by ( ...

  2. Elasticsearch入门教程(二):Elasticsearch核心概念

    原文:Elasticsearch入门教程(二):Elasticsearch核心概念 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:ht ...

  3. HA分布式集群二hive配置

    一,概念 hive:是一种数据仓库,数据储存在:hdfs上,hsql是由替换简单的map-reduce,hive通过mysql来记录映射数据 二,安装 1,mysql安装: 1,检测是否有mariad ...

  4. ELK学习记录二 :elasticsearch、logstash及kibana的安装与配置

    注意事项: 1.ELK版本要求5.X以上,本人使用版本:elasticsearch-6.0.0.kibana-6.0.0-linux-x86_64.logstash-6.0.0.tar 2.Elast ...

  5. DDD实战进阶第一波(三):开发一般业务的大健康行业直销系统(搭建支持DDD的轻量级框架二)

    了解了DDD的好处与基本的核心组件后,我们先不急着进入支持DDD思想的轻量级框架开发,也不急于直销系统需求分析和具体代码实现,我们还少一块, 那就是经典DDD的架构,只有了解了经典DDD的架构,你才能 ...

  6. ES之二:Elasticsearch原理

    Elasticsearch是最近两年异军突起的一个兼有搜索引擎和NoSQL数据库功能的开源系统,基于Java/Lucene构建.最近研究了一下,感觉 Elasticsearch 的架构以及其开源的生态 ...

  7. 〈二〉ElasticSearch的认识:索引、类型、文档

    目录 上节回顾 本节前言 索引index 创建索引 查看索引 查看单个索引 查看所有索引 删除索引 修改索引 修改副本分片数量 关闭索引 索引别名 增加索引别名: 查看索引别名: 删除索引别名: 补充 ...

  8. 轻量级OLAP(一):Cube计算

    有一个数据多维分析的任务: 日志的周UV: APP的收集量及标注量,TOP 20 APP(周UV),TOP 20 APP标注分类(周UV): 手机机型的收集量及标注量,TOP 20 机型(周UV),T ...

  9. ELK 之二:ElasticSearch 和Logstash高级使用

    一:文档 官方文档地址:1.x版本和2.x版本 https://www.elastic.co/guide/en/elasticsearch/guide/index.html 硬件要求: 1.内存,官方 ...

随机推荐

  1. 背后的故事之 - 快乐的Lambda表达式(一)

    快乐的Lambda表达式(二) 自从Lambda随.NET Framework3.5出现在.NET开发者眼前以来,它已经给我们带来了太多的欣喜.它优雅,对开发者更友好,能提高开发效率,天啊!它还有可能 ...

  2. [内核笔记1]内核文件结构与缓存——inode和对应描述

    由来:公司内部外网记录日志的方式现在都是通过Nginx模块收到数据发送到系统消息队列,然后由另外一个进程来从消息队列读取然后写回磁盘这样的操作,尽量的减少Nginx的阻塞. 但是由于System/V消 ...

  3. 强强联合,Testin云测&云层天咨众测学院开课了!

    Testin&云层天咨众测学院开课了! 共享经济时代,测试如何赶上大潮,利用碎片时间给女票或者自己赚点化妆品钱?   2016年12月13日,Testin联手云层天咨带领大家一起推开众测的大门 ...

  4. bzoj3037--贪心

    题目大意: applepi手里有一本书<创世纪>,里面记录了这样一个故事--上帝手中有着N 种被称作"世界元素"的东西,现在他要把它们中的一部分投放到一个新的空间中去以 ...

  5. yaf的简单入门

    1.目录结构: 2.入口文件 入口文件是所有请求的入口,一般都借助于rewrite规则,把所有的请求都重定向到这个入口文件. 一个经典的入口文件  public/index.php 3.重写规则 需要 ...

  6. python10作业思路及源码:类Fabric主机管理程序开发(仅供参考)

    类Fabric主机管理程序开发 一,作业要求 1, 运行程序列出主机组或者主机列表(已完成) 2,选择指定主机或主机组(已完成) 3,选择主机或主机组传送文件(上传/下载)(已完成) 4,充分使用多线 ...

  7. linux拷贝命令,移动命令

    http://blog.sina.com.cn/s/blog_7479f7990101089d.html

  8. BPM配置故事之案例9-根据表单数据调整审批线路2

    老李:好久不见啊,小明. 小明:-- 老李:不少部门有物资着急使用,现在的审批流程太慢了,申请时增加一个是否加急的选项吧.如果选加急,金额1000以下的直接到我这里,我审批完就通过,超过1000的直接 ...

  9. 如何理解MySQL中auto_increment?

    1.auto_increment用于主键自动增长.比如从1开始增长,当把第一条数据删除,再插入第二条数据时,主键值为2,不是1.

  10. [转]NopCommerce How to add a menu item into the administration area from a plugin

    本文转自:http://docs.nopcommerce.com/display/nc/How+to+code+my+own+shipping+rate+computation+method Go t ...