使用ElasticSearch作为大数据平台的实时OLAP框架 – lxw的大数据田地

标签: | 发表时间:2018-05-09 15:24 | 作者:
出处:http://lxw1234.com

关键字:elasticsearch、olap

一直想找一个用于大数据平台实时OLAP(甚至是实时计算)的框架,之前调研的Druid(druid.io)太过复杂,整个Druid由5、6个服务组成,而且加载数据也不太方便,性能一般,亦或是我还不太会用它。后来发现使用ElasticSearch就可以满足海量数据实时OLAP的需求。
ElasticSearch相信大家都很熟悉了,它在搜索领域已经有了举足轻重的地位,而且也支持越来越多的聚合统计功能,还和YARN、Hadoop、Hive、Spark、Pig、Flume等大数据框架兼容的越来越好,比如:可以将ElasticSearch跑在YARN上,还可以在Hive中建立外部表映射到ElasticSearch的Index中,直接在Hive中执行INSERT语句,将数据加载进ElasticSearch。
所谓OLAP,其实就是从事实表中统计任意组合维度的指标,也就是过滤、分组、聚合,其中,聚合除了一般的SUM、COUNT、AVG、MAX、MIN等,还有一个重要的COUNT(DISTINCT),看上去这些操作在SQL中是非常简单的统计,但在海量数据、低延迟的要求下,并不是那么容易做的。
ElasticSearch本来就是做实时搜索的,过滤自然不是问题,现在也支持各种聚合以及Pipeline aggregations(相当于SQL子查询的功能),而且ElasticSearch的安装部署也非常简单,一个节点只有一个服务进程,关于安装配置可参考: http://lxw1234.com/archives/2015/12/582.htm
本文以两个业务场景的例子,看一下ElasticSearch是如何满足我们的需求的。

例子1:网站流量报告

在我们的报表平台有这样一张报表,用于查看每个网站每天的流量指标:

elasticsearch

其中,维度有:天、小时、网站,指标有: PV 、UV 、访问次数、跳出率、平均停留时间、回访率等。另外,还有一张报表是地域报告,维度多了省份和城市,指标一样。目前的做法是将可选的维度组合及对应的指标先在Hive中分析好,再将结果同步至MySQL,供报表展现。

真正意义上的OLAP做法,我是这样做的:在Hive分析好一张最细粒度为visit_id(session_id)的事实表,字段及数据如下:

elasticsearch

然后将这张事实表的数据加载到ElasticSearch中的logs2/sitelog1211中。查看数据:

curl -XGET 'http://localhost:9200/logs2/sitelog1211/_search?pretty'
{
  "took" : 1015,
  "timed_out" : false,
  "_shards" : {
    "total" : 10,
    "successful" : 10,
    "failed" : 0
  },
  "hits" : {
    "total" : 3356328,
    "max_score" : 1.0,
    "hits" : [ {
      "_index" : "logs2",
      "_type" : "sitelog1211",
      "_id" : "AVGkoWowd8ibEMoyOhve",
      "_score" : 1.0,
      "_source":{"cookieid" : "8F97E07300BC7655F6945A","siteid" : "633","visit_id" : "feaa25e6-3208-4801-b7ed-6fa45f11ff42","pv" : 2,"is_return_cookie" : 0,
           "is_bounce_visit" : 0,"visit_stay_times" : 34,"visit_view_page_cnt" : 2, "region" : "浙江","city" : "绍兴"}
    },
……

该天事实表中总记录数为 3356328
接着使用下面的查询,完成了上图中网站ID为1127,日期为2015-12-11的流量报告:

curl -XGET 'http://localhost:9200/logs2/sitelog1211/_search?search_type=count&q=siteid:1127&pretty' -d '
{
    "size": 0,
    "aggs" : {
        "pv" : {"sum" : { "field" : "pv" } },
        "uv" : {"cardinality" : {"field" : "cookieid" ,"precision_threshold": 40000}},
        "return_uv" : {
           "filter" : {"term" : {"is_return_cookie" : 1}},
           "aggs" : {
             "total_return_uv" : {"cardinality" : {"field" : "cookieid" ,"precision_threshold": 40000}}
           }
        },
        "visits" : {"cardinality" : {"field" : "visit_id" ,"precision_threshold": 40000}},
        "total_stay_times" : {"sum" : { "field" : "visit_stay_times" }},
        "bounce_visits" : {
           "filter" : {"term" : {"is_bounce_visit" : 1}},
           "aggs" : {
             "total_bounce_visits" : {"cardinality" : {"field" : "visit_id" ,"precision_threshold": 40000}}
           }
        }
    }
}'

基本上1~2秒就可以返回结果:

{
  "took" : 1887,
  "timed_out" : false,
  "_shards" : {
    "total" : 10,
    "successful" : 10,
    "failed" : 0
  },
  "hits" : {
    "total" : 5888,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "uv" : {
      "value" : 5859
    },
    "visits" : {
      "value" : 5889
    },
    "return_uv" : {
      "doc_count" : 122,
      "total_return_uv" : {
        "value" : 119
      }
    },
    "bounce_visits" : {
      "doc_count" : 5177,
      "total_bounce_visits" : {
        "value" : 5177
      }
    },
    "pv" : {
      "value" : 10820.0
    },
    "total_stay_times" : {
      "value" : 262810.0
    }
  }
}

接着是地域报告中维度为省份的指标统计,查询语句为:

curl -XGET 'http://localhost:9200/logs2/sitelog1211/_search?search_type=count&q=siteid:1127&pretty' -d '
{
    "size": 0,
    "aggs" : {
       "area_count" : {
           "terms" : {"field" : "region","order" : { "pv" : "desc" }},
           "aggs" : {
               "pv" : {"sum" : { "field" : "pv" } },
               "uv" : {"cardinality" : {"field" : "cookieid" ,"precision_threshold": 40000}},
               "return_uv" : {
                  "filter" : {"term" : {"is_return_cookie" : 1}},
                  "aggs" : {
                    "total_return_uv" : {"cardinality" : {"field" : "cookieid" ,"precision_threshold": 40000}}
                  }
               },
               "visits" : {"cardinality" : {"field" : "visit_id" ,"precision_threshold": 40000}},
               "total_stay_times" : {"sum" : { "field" : "visit_stay_times" }},
               "bounce_visits" : {
                  "filter" : {"term" : {"is_bounce_visit" : 1}},
                  "aggs" : {
                    "total_bounce_visits" : {"cardinality" : {"field" : "visit_id" ,"precision_threshold": 40000}}
                  }
               }
           }
       }
    }
}'

因为要根据省份分组,比之前的查询慢一点,但也是秒级返回:

{
  "took" : 4349,
  "timed_out" : false,
  "_shards" : {
    "total" : 10,
    "successful" : 10,
    "failed" : 0
  },
  "hits" : {
    "total" : 5888,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "area_count" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 2456,
      "buckets" : [ {
        "key" : "北京",
        "doc_count" : 573,
        "uv" : {
          "value" : 568
        },
        "visits" : {
          "value" : 573
        },
        "return_uv" : {
          "doc_count" : 9,
          "total_return_uv" : {
            "value" : 8
          }
        },
        "bounce_visits" : {
          "doc_count" : 499,
          "total_bounce_visits" : {
            "value" : 499
          }
        },
        "pv" : {
          "value" : 986.0
        },
        "total_stay_times" : {
          "value" : 24849.0
        }
      }, {
        "key" : "山东",
        "doc_count" : 368,
        "uv" : {
          "value" : 366
        },
        "visits" : {
          "value" : 368
        },
        "return_uv" : {
          "doc_count" : 9,
          "total_return_uv" : {
            "value" : 9
          }
        },
        "bounce_visits" : {
          "doc_count" : 288,
          "total_bounce_visits" : {
            "value" : 288
          }
        },
        "pv" : {
          "value" : 956.0
        },
        "total_stay_times" : {
          "value" : 30266.0
        }
      },
……

这里需要说明一下,在ElasticSearch中,对于去重计数(COUNT DISTINCT)是基于计数估计( Cardinality),因此如果去重记录数比较大(超过40000),便可能会有误差,误差范围是0~2%。

例子2:用户标签的搜索统计

有一张数据表,存储了每个用户ID对应的标签,同样加载到ElasticSearch中,数据格式如下:

curl -XGET 'http://localhost:9200/lxw1234/user_tags/_search?&pretty'
{
  "took" : 220,
  "timed_out" : false,
  "_shards" : {
    "total" : 10,
    "successful" : 10,
    "failed" : 0
  },
  "hits" : {
    "total" : 820165,
    "max_score" : 1.0,
    "hits" : [ {
      "_index" : "lxw1234",
      "_type" : "user_tags",
      "_id" : "222222222222222",
      "_score" : 1.0,
      "_source":{"sex" : "女性","age" : "27到30岁","income" : "5000到10000","edu" : "本科",
"appcategory" : "娱乐类|1.0","interest" : "","onlinetime" : "9:00~12:00|1.0","os" : "IOS|1.0",
"hobby" : "游戏|28.57,房产|8.57,服饰鞋帽箱包|28.57,互联网/电子产品|5.71,家居|8.57,餐饮美食|5.71,体育运动|14.29","region" : "河南省"}
    }
......

每个用户都有性别、年龄、收入、教育程度、兴趣、地域等标签,其中使用_id来存储用户ID,也是主键。

查询1: SELECT count(1) FROM user_tags WHERE sex = ‘女性’ AND appcategory LIKE ‘%游戏类%';

curl -XGET 'http://localhost:9200/lxw1234/user_tags/_count?pretty' -d '
{
    "filter" : {
        "and" : [
          {"term" : {"sex" : "女性"}},
          {"match_phrase" : {"appcategory" : "游戏类"}}
        ]
    }
}'

返回结果:

{
  "count" : 106977,
  "_shards" : {
    "total" : 10,
    "successful" : 10,
    "failed" : 0
  }
}

查询2:先筛选,再分组统计:

SELECT edu,COUNT(1) AS cnt 
FROM user_tags 
WHERE sex = '女性' 
AND appcategory LIKE '%游戏类%' 
GROUP BY edu 
ORDER BY cnt DESC 
limit 10;

查询语句:

curl -XGET 'http://localhost:9200/lxw1234/user_tags/_search?search_type=count&pretty' -d '
{
    
    "filter" : {
        "and" : [
          {"term" : {"sex" : "女性"}},
          {"match_phrase" : {"appcategory" : "游戏类"}}
        ]
    },
    "aggs" : {
        "edu_count" : {
            "terms" : {
              "field" : "edu",
              "size" : 10
            }
        }
    }
}'

返回结果:

{
  "took" : 479,
  "timed_out" : false,
  "_shards" : {
    "total" : 10,
    "successful" : 10,
    "failed" : 0
  },
  "hits" : {
    "total" : 106977,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "edu_count" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [ {
        "key" : "本科",
        "doc_count" : 802670
      }, {
        "key" : "硕士研究生",
        "doc_count" : 16032
      }, {
        "key" : "专科",
        "doc_count" : 1433
      }, {
        "key" : "博士研究生",
        "doc_count" : 25
      }, {
        "key" : "初中及以下",
        "doc_count" : 4
      }, {
        "key" : "中专/高中",
        "doc_count" : 1
      } ]
    }
  }
}

从目前的调研结果来看,ElasticSearch没有让人失望,部署简单,数据加载方便,聚合功能完备,查询速度快,目前完全可以满足我们的实时搜索、统计和OLAP需求,甚至可以作为NOSQL来使用,接下来再做更深入的测试。
另外,还有一个开源的SQL for ElasticSearch的框架Crate(crate.io),是在ElasticSearch之上封装了SQL接口,使得查询统计更加方便,不过SQL支持的功能有限,使用的ElasticSearch版本较低,后面试用一下再看。

您可以关注 lxw的大数据田地,或者 加入邮件列表,随时接收博客更新的通知邮件。

 

如果觉得本博客对您有帮助,请 赞助作者

转载请注明: lxw的大数据田地» 使用ElasticSearch作为大数据平台的实时OLAP框架

喜欢 (20) 分享 (0)

相关 [elasticsearch 作为 大数据] 推荐:

使用ElasticSearch作为大数据平台的实时OLAP框架 – lxw的大数据田地

- -
关键字:elasticsearch、olap. 一直想找一个用于大数据平台实时OLAP(甚至是实时计算)的框架,之前调研的Druid(druid.io)太过复杂,整个Druid由5、6个服务组成,而且加载数据也不太方便,性能一般,亦或是我还不太会用它. 后来发现使用ElasticSearch就可以满足海量数据实时OLAP的需求.

Elasticsearch如何更新mapping_大数据_宁千阳博客-CSDN博客

- -
Elasticsearch 的 mapping 在创建. indices时即已确定,无法更改. 那么,当我们需要更新 mapping 时,该如何是好呢. 当我们在创建一条索引时,添加好 mapping 后,可设置一个. alias指向该索引,然后生产环境采用该. 当然,如果没有这样做的话,建议趁早备份,修改 API.

我的ElasticSearch集群部署总结--大数据搜索引擎你不得不知 - 王安琪

- - 博客园_首页
摘要:世上有三类书籍:1、介绍知识,2、阐述理论,3、工具书;世间也存在两类知识:1、技术,2、思想. 以下是我在部署ElasticSearch集群时的经验总结,它们大体属于第一类知识“techknowledge(技术)”. 关键词:ElasticSearch, 搜索引擎, 集群, 大数据, Solr, 大数据.

[译]elasticsearch mapping

- - an74520的专栏
es的mapping设置很关键,mapping设置不到位可能导致索引重建. 请看下面各个类型介绍^_^. 每一个JSON字段可以被映射到一个特定的核心类型. JSON本身已经为我们提供了一些输入,支持 string,  integer/ long,  float/ double,  boolean, and  null..

Elasticsearch as Database - taowen - SegmentFault

- -
【北京上地】滴滴出行基础平台部招聘 Elasticsearch 与 Mysql binlog databus 开发工程师. 内推简历投递给: [email protected]. 推销Elasticsearch. 时间序列数据库的秘密(1)—— 介绍. 时间序列数据库的秘密(2)——索引.

Clickhouse 在日志存储与分析方面作为 ElasticSearch 和 MySQL 的替代方案

- -
2021年,Clickhouse 在日志存储与分析方面作为 ElasticSearch 和 MySQL 的替代方案. 原文作者:Anton Sidashin. 关于Clickhouse的文章,这段内容在互联网上仍然很流行,甚至被多次翻译. 现在已经过去两年多,同时 Clickhouse 的开发节奏.

ElasticSearch 2 的节点调优(ElasticSearch性能)

- - 行业应用 - ITeye博客
一个ElasticSearch集群需要多少个节点很难用一种明确的方式回答,但是,我们可以将问题细化成一下几个,以便帮助我们更好的了解,如何去设计ElasticSearch节点的数目:. 打算建立多少索引,支持多少应用. elasticsearch版本: elasticsearch-2.x. 需要回答的问题远不止以上这些,但是第五个问题往往是容易被我们忽视的,因为单个ElasticSearch集群有能力支持多索引,也就能支持多个不同应用的使用.

Elasticsearch:使用 Elasticsearch 进行语义搜索

- - 掘金 后端
在数字时代,搜索引擎在通过浏览互联网上的大量可用信息来检索数据方面发挥着重要作用. 此方法涉及用户在搜索栏中输入特定术语或短语,期望搜索引擎返回与这些确切关键字匹配的结果. 虽然关键字搜索对于简化信息检索非常有价值,但它也有其局限性. 主要缺点之一在于它对词汇匹配的依赖. 关键字搜索将查询中的每个单词视为独立的实体,通常会导致结果可能与用户的意图不完全一致.

elasticsearch的javaAPI之query

- - CSDN博客云计算推荐文章
elasticsearch的javaAPI之query API. the Search API允许执行一个搜索查询,返回一个与查询匹配的结果(hits). 它可以在跨一个或多个index上执行, 或者一个或多个types. 查询可以使用提供的 query Java API 或filter Java API.

Elasticsearch基础教程

- - 开源软件 - ITeye博客
转自:http://blog.csdn.net/cnweike/article/details/33736429.     Elasticsearch有几个核心概念. 从一开始理解这些概念会对整个学习过程有莫大的帮助.     接近实时(NRT).         Elasticsearch是一个接近实时的搜索平台.