2019独角兽企业重金招聘Python工程师标准>>>
应用场景
设计一个系统来预估未来一年的广告流量,不是总流量,是任意时间段任何定向(Targeting)条件约束情况下的流量。定向条件有近百种(内容类别,设备平台,用户地域,用户人口属性等),整个时间区间不同组合数(也就是数据行数)是亿级别。目标是秒级的查询响应时间。
一个简单的数据例子如下:
存储系统选择
MySQL不是适合的选择
最容易想到的是用Mysql作为数据存放和查询引擎,由于数据行数太多,Mysql必须通过创建索引或者组合索引来加速查询。
典型的查询包含若干个定向类别,这些定向条件的组合是非常多的(top 80%的查询也会包含几十种组合),故需要创建非常多的组合索引,代价很高。
另外,对于那些没有创建组合索引的查询,查询时间完全不能接受。
为什么没有用Hbase或者Hive
Hbase本身是一个经典的基于hdfs的分布式存储系统,通常来说其是行存储的,当创建column families之后,每个column family是列存储的。在这个应用中,可以为每个定向类别(包括日期)创建一个单独的column family,但Hbase本身没有为column family创建bitmap indexing,查询速度应该会受到影响。
另外不用Hbase的一个原因是希望存储系统尽量轻量级,最好不要安装hadoop
Hive将查询转化为M/R任务,没法保证查询的快速响应(比如M/R cluster资源竞争很激烈时),而且使用Hive需要以来hadoop cluster,对这个应用来说也略微重量级。
我们需要一个高可用的分布式的列存储系统
核心需求包含2点,一是查询速度快,二是系统的拓展性好,最好是分布式的。
- 第一点要求意味着最好用column-store而不是row-store,在这个应用中,虽然定向类别有近百种,但是单次查询通常只会涉及几个。对于修改操作较少且查询往往只涉及少数几列的场景使用column-store可以获得快一个量级的查询速度。而且column-store可以通过bitmap indexing,encoding,以及compression来优化查询速度和存储开销
- 第二点要求一方面是由于我们的数据量较大,并行存储和查询可以减少时间开销,另一方面是数据量每年还在快速上涨,以后可以简单地通过加机器来应对。
对系统的其他要求比较普遍:系统可用性要高,稳定,轻量级,易于上手。
为什么Druid是适合的选择
Druid满足我们上面2点要求,其是一个开源的、分布式的、列存储系统,特别适用于大数据上的(准)实时分析统计。且具有较好的稳定性(Highly Available)。 其相对比较轻量级,文档非常完善,也比较容易上手。
Druid介绍
概念
Segment: Druid中有个重要的数据单位叫segment,其是Druid通过bitmap indexing从raw data生成的(batch or realtime)。
segment保证了查询的速度。可以自己设置每个segment对应的数据粒度,这个应用中广告流量查询的最小粒度是天,所以每天的数据会被创建成一个segment。注意segment是不可修改的,如果需要修改,只能够修改raw data,重新创建segment了
架构
image
Druid本身包含5个组成部分:Broker nodes, Historical nodes, Realtime nodes, Coordinator Nodes和indexing services. 分别的作用如下:
- Broker nodes: 负责响应外部的查询请求,通过查询Zookeeper将请求划分成segments分别转发给Historical和Real-time nodes,最终合并并返回查询结果给外部;
- Historial nodes: 负责’Historical’ segments的存储和查询。其会从deep storage中load segments,并响应Broder nodes的请求。Historical nodes通常会在本机同步deep storage上的部分segments,所以即使deep storage不可访问了,Historical nodes还是能serve其同步的segments的查询;
- Real-time nodes: 用于存储和查询热数据,会定期地将数据build成segments移到Historical nodes。一般会使用外部依赖kafka来提高realtime data ingestion的可用性。如果不需要实时ingest数据到cluter中,可以舍弃Real-time nodes,只定时地batch ingestion数据到deep storage;
- Coordinator nodes: 可以认为是Druid中的master,其通过Zookeeper管理Historical和Real-time nodes,且通过Mysql中的metadata管理Segments
- Druid中通常还会起一些indexing services用于数据导入,batch data和streaming data都可以通过给indexing services发请求来导入数据。
Druid还包含3个外部依赖
- Mysql:存储Druid中的各种metadata(里面的数据都是Druid自身创建和插入的),包含3张表:”druid_config”(通常是空的), “druid_rules”(coordinator nodes使用的一些规则信息,比如哪个segment从哪个node去load)和“druid_segments”(存储每个segment的metadata信息);
- Deep storage: 存储segments,Druid目前已经支持本地磁盘,NFS挂载磁盘,HDFS,S3等。Deep Storage的数据有2个来源,一个是batch Ingestion, 另一个是real-time nodes;
- ZooKeeper: 被Druid用于管理当前cluster的状态,比如记录哪些segments从Real-time nodes移到了Historical nodes;
查询
Druid的查询是通过给Broker Nodes发送HTTP POST请求(也可以直接给Historical or Realtime Node),具体可见Druid官方文档。查询条件的描述是json文件,查询的response也是json格式。Druid的查询包含如下4种:
- Time Boundary Queries: 用于查询全部数据的时间跨度
- groupBy Queries: 是Druid的最典型查询方式,非常类似于Mysql的groupBy查询。query body中几个元素可以这么理解:
- “aggregation”: 对应mysql”select XX from”部分,即你想查哪些列的聚合结果;
- “dimensions”: 对应mysql”group by XX”,即你想基于哪些列做聚合;
- “filter”: 对应mysql”where XX”条件,即过滤条件;
- “granularity”: 数据聚合的粒度;
- Timeseries queries: 其统计满足filter条件的”rows”上某几列的聚合结果,相比”groupBy Queries”不指定基于哪几列进行聚合,效率更高;
- TopN queries: 用于查询某一列上按照某种metric排序的最常见的N个values;
本文小结
- Druid是一个开源的,分布式的,列存储的,适用于实时数据分析的系统,文档详细,易于上手;
- Druid在设计时充分考虑到了Highly Available,各种nodes挂掉都不会使得druid停止工作(但是状态会无法更新);
- Druid中的各个components之间耦合性低,如果不需要streaming data ingestion完全可以忽略realtime node;
- Druid的数据单位Segment是不可修改的,我们的做法是生成新的segments替换现有的;
- Druid使用Bitmap indexing加速column-store的查询速度,使用了一个叫做CONCISE的算法来对bitmap indexing进行压缩,使得生成的segments比原始文本文件小很多;
- 在我们的应用场景下(一共10几台机器,数据大概100列,行数是亿级别),平均查询时间<2秒,是同样机器数目的Mysql cluter的1/100 ~ 1/10;
- Druid的一些“局限”:
- Segment的不可修改性简化了Druid的实现,但是如果你有修改数据的需求,必须重新创建segment,而bitmap indexing的过程是比较耗时的;
- Druid能接受的数据的格式相对简单,比如不能处理嵌套结构的数据
上一篇对druid做了简单的介绍,这一篇我介绍一下druid的基本概念,druid的数据结构以及druid的集群架构;
Data
druid的数据格式和关系型数据库数据较为类似, 如下:
timestamp publisher advertiser gender country click price
2011-01-01T01:01:35Z bieberfever.com google.com Male USA 0 0.65
2011-01-01T01:03:63Z bieberfever.com google.com Male USA 0 0.62
2011-01-01T01:04:51Z bieberfever.com google.com Male USA 1 0.45
2011-01-01T01:00:00Z ultratrimfast.com google.com Female UK 0 0.87
2011-01-01T02:00:00Z ultratrimfast.com google.com Female UK 0 0.99
2011-01-01T02:00:00Z ultratrimfast.com google.com Female UK 1 1.53
熟悉OLAP的同学,对以下这些概念一定不陌生,druid也把数据分为以下三个部分:
Timestamp Column:将时间单独处理,是因为druid所有的操作都是围绕时间轴来进行的。
Dimension Columns:维度字段,是数据的属性, 一般被用来过滤数据。上面的例子,我们有四个维度, publisher, advertiser, gender, country. 他们每一个都可以看是数据立方体的一个轴,都可以用来用来做横切。
Metric Columns: 度量字段,是用来做聚合或者相关计算的。 上边的数据, click和price是俩个度量。度量是可以衡量的数据,一般可以有如下的操作,count ,sum等等
ROLL-UP
roll-up (上卷)是olap的基本操作(除此之外还有下钻,切片等, 基本理论是一样的)。 在数据统计里,由于数据量太多,一般对细分的数据不是特别干兴趣,或者说没有太大关注的意义。
但是按照维度的汇总或者统计,确实很有用的。druid通过一个roll-up的处理,将原始数据在注入的时候就进行汇总处理。roll-up 是在维度过滤之前的第一层聚合操作,如下:
GROUP BY timestamp, publisher, advertiser, gender, country
:: impressions = COUNT(1), clicks = SUM(click), revenue = SUM(price)
聚合后数据就变成了如下的样子
timestamp publisher advertiser gender country impressions clicks revenue
2011-01-01T01:00:00Z ultratrimfast.com google.com Male USA 1800 25 15.70
2011-01-01T01:00:00Z bieberfever.com google.com Male USA 2912 42 29.18
2011-01-01T02:00:00Z ultratrimfast.com google.com Male UK 1953 17 17.31
2011-01-01T02:00:00Z bieberfever.com google.com Male UK 3194 170 34.01
我们可以看到,roll-up可以压缩我们需要保存的数据量。 druid 通过roll-up 减少了 我们存储在后台的数据量。 但这种缩减是有损失的, 当我们做了roll-up, 我们就无法查询细分的数据了。 或许,我们在可以在rollup的时候,将其粒度控制在我们可以查询到我们需要查看的最细数据为止。druid可以通过 queryGranularity 来控制注入数据的粒度。 最小的queryGranularity 是 millisecond(毫秒级)
Sharding the Data
druid的数据分片称为 segments, 一般的,druid会通过时间来进行分片, 上面例子中我们聚合后的数据,可以按小时分为俩片,如下
Segment sampleData_2011-01-01T01:00:00:00Z_2011-01-01T02:00:00:00Z_v1_0
contains
2011-01-01T01:00:00Z ultratrimfast.com google.com Male USA 1800 25 15.70
2011-01-01T01:00:00Z bieberfever.com google.com Male USA 2912 42 29.18
Segment sampleData_2011-01-01T02:00:00:00Z_2011-01-01T03:00:00:00Z_v1_0
contains
2011-01-01T02:00:00Z ultratrimfast.com google.com Male UK 1953 17 17.31
2011-01-01T02:00:00Z bieberfever.com google.com Male UK 3194 170 34.01
segment 是个包含数据的独立的容器, 内部的数据以时间分割。 segment 为聚合的列做索引,数据依赖索引,按列方式存储。 所以druid得查询就转为了如何扫描segments了。
segment 由datasource, interval, version, 和一个可选的分区号唯一的确定。 例如上面例子中,我们的segment的名字就是这种格式dataSource_interval_version_partitionNumber
写到这里,大家应该也有了初步的了解,druid 在注入的数据的时候,就已经将索引按照指定的格式处理好,并保存在deepstore中, 其余的查询都转换为了对数据的扫描过程。 所以druid是典型的MOLAP
Indexing the Data
druid能达到这样的速度,主要取决于数据的存储格式。 druid为数据创建了不可变的数据镜像, 并已便于分析搜索的的结构存储下来。 druid是列存储的, 也就是说,每一个单独的列都是分开存储的。查询过程中,也只有与查询有关联的列参与。 druid对于只有扫描的查询更有优势。 不同的列可以调用不同的压缩方式。不同的列也可以有不同的索引。
druid的索引是segment级别的。
Loading the Data
druid有俩种数据load的方式,一种是realtime的,一种是batch的。 druid的实时数据注入是很费力的。 Exactly once semantics are not guaranteed with real-time ingestion in Druid, although we have it on our roadmap to support this. Batch ingestion provides exactly once guarantees and segments created via batch processing will accurately reflect the ingested data。常用的做法是通过real-time 方式来管理实时的数据分析,通过batch 方式来管理精确备份的数据。
Querying the Data
druid原生的查询是以json参数调用http接口,社区也分享了各种其他语言的查询库, 包括sql
druid 主要是为单表的数据操作儿设计的,所以目前不支持join操作。 很多产品需要在etl阶段做join, 可以把join放在数据注入druid之前来进行。
The Druid Cluster
druid集群是由很多功能不同的节点组成的。
Historical Nodes:historical nodes 可以看做是druid集群的脊椎, 它将segment固话到本地,供集群查询时使用。 historical nodes 采用了一个无共享架构设计, 它知道如何去加载segment, 删除segment以及如果基于segment查询。
Broker Nodes:broker Nodes 是客户端和相关应用从druid集群上查询数据的节点,它的职责是对客户端过来的查询做负载,聚集和合并查询结果。 broker节点知道每个segment都在哪儿
Coordinator Nodes:coordinator nodes 用来管理druid集群放在historical nodes上的segment。coordinatenodes 告诉historical nodes去加载新的segment, 移除就得segment, 对节点上的segment做均衡
Real-time Processing:实时数据处理可以在单点实时节点或者索引服务(indexing service)完成, 实时的逻辑在这二者上是很常见的。实时处理主要包括加载数据,创建索引(创建segment), 以及将segment迁移到historical nodes。经过实时处理后的数据及可查询。迁移处理也是无损的, 迁移后数据仍然是可以查询的。
overload Nodes: 主要是用于批量索引服务,我会在druid-索引服务中详细讲解
External Dependencies
druid集群需要依赖
Zookeeper 用于集群内部通讯
Metadata Storage 用户存储segment,configuration 等的metadata信息; 服务创建segments后,会向metadatastore中写一个新的标记, coordinatenode监控metadatastore来获取有哪些新的数据需要被重新load,或者有哪些旧的数据需要被去除。查询的时候并不需要metadatastor的数据。 在生产集群中,mysql 和postgresql是比较常用的metadatastor, derby可以用于单机测试环境
Deep Storage deepstorage作为segments一种持久的备份。 服务创建segments后,上传到deepstore。 coordinatenode从deepstorage下载segments。查询的时候也不会用到deepstorage。 常用的deepstorage有S3和hdfs。
亚秒级OLAP查询 德鲁伊的列方向和反向索引可实现复杂的多维过滤,并准确扫描查询所需的内容。以毫秒为单位聚合和过滤数据。
实时流式传输 典型的分析数据库通过批量获取数据。一次摄取事件通常伴随着事务锁和其他开销,这会降低摄取速率。德鲁伊使用无锁数据集来附加大量数据集,以便每个节点每秒同时摄取和查询10,000多个事件。简而言之,事件发生和可见事件之间的延迟仅受事件传递给德鲁伊的速度限制。
Power Analytic Applications 德鲁伊拥有众多内置多租户功能。面向用户的高级分析应用程序,旨在供数千个并发用户使用。
具有成本效益的 德鲁伊在规模上具有极高的成本效益,并且内置了众多功能以降低成本。通过简单的配置旋钮折衷成本和性能。
高度可用的 德鲁伊用于支持需要一直处于启动状态的SaaS实施。Druid支持滚动更新,因此您的数据在软件更新期间仍然可用且可查询。向上或向下扩展而不会丢失数据。
可扩展的 现有德鲁伊部署每秒处理数万亿个事件,数PB的数据和数千个查询。
什么是OLAP(联机分析处理)?
这个是和数据处理非常相关的一个概念。接触过BI(商务智能)的同学一定清楚。 数据处理大致可以分成两大类:联机事务处理OLTP(on-line transaction processing)、联机分析处理OLAP(On-Line Analytical Processing); OLTP是传统的关系型数据库的主要应用,主要是基本的、日常的事务处理,例如银行交易。通俗的讲,就是对数据的增删改查等操作。 OLAP是数据仓库系统的主要应用,支持复杂的分析操作,侧重决策支持,并且提供直观易懂的查询结果。通俗的讲,就是对数据按不同维度的聚合,维度的上钻,下卷等。
OLAP可以分为ROLAP,MOLAP和HOLAP
ROLAP: 使用关系型数据库或者扩展的关系型数据库来管理数据仓库数据,而OLAP中间件支持其余的功能。ROLAP包含了每个后端关系型数据库的优化,聚合,维度操作逻辑的实现,附件的工具以及服务等。所以ROLAP比MOLAP有更好的可伸缩性。 比较典型的ROLAP有mondrian, Presto(facebook)。 目前阿里的DRDS也可以看作是ROLAP的框架
MOLAP: 通过基于数据立方体的多位存储引擎,支持数据的多位视图。即通过将多维视图直接映射到数据立方体上,使用数据立方体能够将预计算的汇总数据快速索引。比较典型的MOLAP框架有kylin(apache), Lylin(ebay)、pinot(linkedin)和druid
也就是说MOLAP是空间换时间,即把所有的分析情况都物化为物理表或者视图,查询的时候直接从相应的物化表中获取数据, 而ROLAP则通过按维度分库,分表等方式,实现单一维度下的快速查询,通过分布式框架,并行完成分析任务,来实现数据的分析功能。MOLAP 实现较简单,但当分析的维度很多时,数据量呈指数增长,而ROLAP在技术实现上要求更高,但扩展性也较好。
HOLAP: 混合OLAP结合ROLAP和MOLAP,得益于ROLAP较大的可伸缩性和MOLAP的快速查询。
更多的关于OLAP的知识,推介大家看机械工业出版社出版的《数据挖掘-概念与技术》
什么是Druid
Druid是一个高效的数据查询系统,主要解决的是对于大量的基于时序的数据进行聚合查询。数据可以实时摄入,进入到Druid后立即可查,同时数据是几乎是不可变。通常是基于时序的事实事件,事实发生后进入Druid,外部系统就可以对该事实进行查询。
Druid系统架构
Druid是一组系统,按照职责分成不同的角色。目前存在五种节点类型:
- Historical: 历史节点的职责主要是对历史的数据进行存储和查询,历史节点从Deep Storage下载Segment,然后响应Broker对于Segment的查询将查询结果返回给Broker节点,它们通过Zookeeper来声明自己存储的节点,同时也通过zookeeper来监听加载或删除Segment的信号。
- Coordinator:协调节点监测一组历史节点来保证数据的可用和冗余。协调节点读取元数据存储来确定哪些Segment需要load到集群中,通过zk来感知Historical节点的存在,通过在Zookeeper上创建entry来和Historical节点通信来告诉他们加载或者删除Segment
- Broker:节点接收外部客户端的查询,并且将查询路由到历史节点和实时节点。当Broker收到返回的结果的时候,它将结果merge起来然后返回给调用者。Broker通过Zook来感知实时节点和历史节点的存在。
- Indexing Service: 索引服务是一些worker用来从实时获取数据或者批量插入数据。
- Realtime:获取实时数据
下面这张图片展示了一次查询数据在整个架构中的流向
Druid数据流向
除了上述五个节点,Druid还有三个外部依赖:
- Zookeeper集群
- 元数据存储实例:Mysql
- Deep Storage:HDFS
Segments
Druid 把它的索引存储到一个Segment文件中,Segment文件是通过时间来分割的。
Segment数据结构
对于摄入到Druid的数据的列,主要分三种类型,时间列,指标列和维度列。如下图
示例数据
对于时间列和指标列处理比较简单,直接用LZ4压缩存起来就ok,一旦查询知道去找哪几行,只需要将它们解压,然后用相应的操作符来操作它们就可以了。维度列就没那么简单了,因为它们需要被过滤和聚合,因此每个维度需要下面三个数据结构。
- 一个map,Key是维度的值,值是一个整型的id
- 一个存储列的值得列表,用1中的map编码的list
- 对于列中的每个值对应一个bitmap,这个bitmap用来指示哪些行包含这个个值。
对于上图的Page列,它的存储是这样的
1: 字典
{
"Justin BIeber": 0,
"Ke$ha": 1
}
2. 值的列表
[0,
0,
1,
1]
3. bitMap
value="Justin Bieber": [1, 1, 0, 0]
value="Ke$ha": [0, 0, 1, 1]
历史节点
每个历史节点维持一个和Zookeeper的长连接监测一组path来获取新的Segment信息。历史节点互相不进行通信,他们依靠zk来等待协调节点来协调。
协调节点负责把新的Segment分发给历史节点,协调节点通过在zk的指定路径下创建一个entry来向历史节点做分发。
当历史节点发现一个新的entry出现在path中,它首先会检查本地文件缓存看有有没Segment信息,如果没有Segment信息,历史节点会从zk上下载新的Segment的元信息。Segment的元信息包括Segment存在Deep Storage的位置和如何解压和处理Segment。一旦一个历史节点完成对一个Segment的处理,这个历史节点会在zk上的一个路径声明对这个Segment提供查询服务,此刻这个Segment就可以查询了。
查询节点
Broker节点负责将查询路由到历史节点和实时节点,Broker节点通过zk来知道哪些Segment存在哪个节点上。Broker也会把查询的结果进行Merge
大多数Druid查询包含一个区间对象,这个对象用来指定查询所要查的区间段。Druid的Segment也通过时间段进行分割散落在整个集群中。假设有一个简单的数据源,这个数据源有七个Segment,每个Segment包含一周中的某一天的数据。任何一个时间范围超过一天的查询都会落到不止一个Segment上。这些Segment可能分布在集群中不同的节点上。因此这种查询就会涉及到多个节点。
为了确定发送到哪个节点上,Broker会从Historial和RealTime的节点来获取他们提供查询的Segment的信息,然后构建一个时间轴,当收到特定的时间区间的查询时,Broker通过时间轴来选择节点。
Broker节点会维护一个LRU缓存,缓存存着每个Segment的结果,缓存可以是一个本地的缓存或者多个节点共用的外部的缓存如 memcached。当Broker收到查询时候,它首先将查询映射成一堆Segment的查询,其中的一个子集的结果可能已经存在缓存中,他们可以直接从缓存中拉出来,那些没在缓存中的将被发送到相应节点。
协调节点
协调节点负责Segment的管理和分发,协调节点指挥历史节点来加载或者删除Segment,以及Segment的冗余和平衡Segment。协调节点会周期性的进行扫描,每次扫描会根据集群当前的状态来决定进一步的动作。和历史节点和Broker一样,协调节点通过zk来获取Segment信息,同时协调节点还通过数据库来获取可用的Segment信息和规则。在一个Segment提供查询之前,可用的历史节点会按照容量去排序,容量最小的具有最高的优先级,协调节点就会让它去加载这个Segment然后提供服务。
- 清理Segment,Druid会将集群中的Segment和数据库中的Segment进行对比,如果集群有的的数据库中没有的会被清理掉。同事那些老的被新的替换的Segment也会被清理掉。
- Segment可用性, 历史节点可能因为某种原因不可用,协调节点会发现节点不可用了,会将这个节点上的Segment转移到其他的节点。Segment不会立即被转移,如果在配置的时间段内节点恢复了,历史节点会从本地缓存加载Segment。恢复服务
- Segment负载均衡,协调节点会找到Segment最多的节点和Segment最少的节点,当他们的比例超过一个设定的值的时候,协调节点会从Segment最多的节点转移到Segment最少的节点。
索引服务
索引服务是一个高可用的,分布式的服务来运行索引相关的Task。索引服务会创建或者销毁Segment。索引服务是一个Master/Slave架构。索引服务是三个组件的集合
- peon组件用来跑索引任务。
- Middle Manager组件用来管理peons
- Overlord向MiddleManager分发任务。
索引服务
索引服务
Overlord节点负责接受任务,协调任务分发,创建锁,和返回状态给调用者。Overlord节点可以以本地模式或者远程模式运行。本地模式会直接创建Peon,远程模式会通过Middle Manager创建任务。
实时节点
实时节点提供实时索引服务,通过实时节点索引的数据立即可查。实时节点会周期性的构建Segment,并且把这些Segment推到历史节点并修改元数据。
实时节点
介绍
Druid是一个拥有大数据实时查询和分析的高容错、高性能开源分布式系统,旨在快速处理大规模的数据,并能够实现快速查询和分析。尤其是当发生代码部署、机器故障以及其他产品系统遇到宕机等情况时,Druid仍然能够保持100%正常运行。创建Druid的最初意图主要是为了解决查询延时问题,当时试图使用hadoop来实现交互式查询分析,但是很难满足实时分析的需要。而Druid提供了以交互方式访问数据的能力,并权衡了查询的灵活性和性能二采取了特殊的存储格式。
Druid允许以类似Dremel和PowerDrill的方式进行单表查询,同时还增加了一些新特性,如为局部嵌套数据结构提供列式存储格式、为快速过滤做索引、实时摄取和查询、高容错的分布式体系架构等。
特性
为分析而设计:为OLAP工作流的探索性分析而构建,支持各种过滤、聚合和查询等类;
快速的交互式查询:Druid的低延迟数据摄取架构允许事件在他们创建后毫秒内可被查询到;
高可用性:Druid的数据在系统更新时依然可用,规模的扩大和缩小都不会造成数据丢失;
可扩展:Druid已实现每天能够处理数十亿事件和TB级数据。
使用场景
1、需要交互式聚合和快速探究大量数据时;
2、需要实时查询分析时;
3、具有大量数据时,如每天数亿事件的新增、每天数10T数据的增加;
4、对数据尤其是大数据进行实时分析时;
5、需要一个高可用、高容错、高性能数据库时。
架构
Historical:对非实时数据进行处理存储和查询;
Realtime:实时摄取数据、监听输入数据流
Coordinator:监控historical节点
Broker:接收来自外部客户端的查询,和将查询转发到Realtime和historical
Indexer:负责索引服务
对比
Spark+Redis+Hbase 实时数据探索
代存在下述问题:
-
流量高峰期处理延迟
-
纬度交叉分析,不灵活
-
消耗资源大
-
系统故障,重算慢
这是第一代、消耗大、系统故障,在大内存情况下很容易导致崩溃。马蜂窝之前就遇到突发,一组三台,每一台 512 个 G,这个时候内存太大了,哪天一个内存条坏的话,这一天的数据可能就要重新算,而且对于现在当前整个实时数据量来看,完全就不符合当前的现状,算一天需要十几个小时。
当时考虑到,在数据量大的情况下,是不是我们可以去牺牲 UV 的计算。所以就引入在 Druid 里面。把 Druid 引入到 MES,误差基本上保持在 2% 左右。后面我们又通过雅虎提供的data sketch,可以精确调控 UV 的计算,它的默认值是 16384,16384 以下可以是精确的。当然这个值是可以控制的,就是 2 的 N 次幂,当前我们是调到特别大,800 多万。但 Druid 里面不支持MES第一代的虚拟 key。
在 Druid 里面对于datasource 有一个按时间密度去分的,我们历史数据在查询力度这个层面,只能让他查到按每小时去查,其他按天去分配。最新的数据就在最近 15 天,我们可以让他精确到一分钟的查询,对于历史数据,力度越精确,数据量到 Druid 里面越大。
在离线批量导入,现在 Druid 支持,T+1 的数据校正。如果在 PSPARK+TRANQUILITY 这一阶段,因为 SPARK 的 task 失败的话,可能会导致这个数据到 Druid 里面 PV 会上升。所以说需要每天凌晨通过批量导入的方法把上一天的数据做一个数据校准。同样的是需要打平在 attr 里打平所有工程师上报的数据制定的值。
|Druid 集群注意事项
在 Druid 里面配置,
1、维度不要太多,像蚂蜂窝最开始 700 多个维度。每天导进去将近 100 个 G,导进去十分耗时。
2、维度大小,不要太大。比如你来一个维度值几兆的,这个不行。
3、要去合理配置比例。在最开始,我们就拿了跟我们之前节点挂上了 10 个 T 的磁盘,作为整个 Druid 节点的数据存储,但是发现在你去查,无论你是去查任务,或者查历史数据。10 个 T 的磁盘跟不上来,查询各种超时,各种响应。
4、磁盘选用。其实采用的固态盘,基本上像我们现在的配置,就是 256 个 G 内存,1.2T 的固态盘。这个配置起来,你去查询整个历史数据,或者无论你查询其他的数据都是很快的。
5、在segment大小,我们最开始是按天的,100个G,后面拆分成每小时去分。这个时候到几个G,几个G也不行,我们就是要在去拆分几个G,到最终查询相当于是在在300-700兆左右。
6、在Druid里面,不支持逗号,因为 Druid 里在底层逗号是用来分隔。
7、优先去升级 Druid 的版本。我们在最早从 0.6 慢慢升级到 0.8,我们现在用的是 0.9。每一次 Druid 的发版,优化了很多东西。你觉得每一个查询有问题,或者说你想要去更快查询这些数据,可以优先考虑一下去 github 上面去看看 Druid 的最新近况。
这个就是今天给大家分享的一些东西。当然我们在使用 Druid 的过程当中,其实还遇到其他很多问题。也希望 Druid 能越来越好。
其他
Druid已基于Apache License 2.0协议开源,代码托管在github,当前最稳定版本是0.7.11,已经有63个代码Contributer和近2000个关注。Druid的主要贡献者包括广告分析创业公司Metamarkets、电影流媒体网站Metflix、Yahoo等公司。Druid官方对Druid通Shark、Vertica、Cassandra、Hadoop、Spark、Elasticsearch等在容错能力、灵活性、查询性能等方面进行了对比说明。