Druid数据格式

  1. 时间列(Timesatmp):表明每行数据的时间值,默认使用UTC时间格式并且精确到毫秒级别。这个列是数据聚合与范围查询的重要维度。
  2. 维度列(Dimension):维度来自于OLAP的概念,用来标识数据行的各个类别信息。
  3. 指标列(Metrics):指标对应于OLAP概念中的Fact,是用于计算和聚合的列。指标列通常是一些数字,计算操作通常包括Count,Sum,Mean等。

image

从上表可知维度列是:publisher,advertiser,gender,country。指标列是:click,price。

预聚合roll up

无论是实时数据消费还是批量的离线数据处理,Druid基于DataSource结构存储数据时间即可选择对任何指标列进行预聚合(roll up)。

  • 同维度列的值做聚合:所有维度列的值都相同时,比对于所有组合维度“publisher advertise gender country”维度值同为“ultratrimfast.com google.com Male USA”或同为“bieberfever.com google.com Male USA”的行。
  • 对指定时间粒度内的值做聚合:符合参数queryGranularity指定的范围,比如时间列同为一分钟内所有的行,聚合操作相当于数据表所有列做了Group by操作。比如“group by timestamp, publisher,advertiser,gender,country::impressions=Count(1),clicks=SUM(click),revenue=SUM(price)”。下表即为定义粒度为HOUR聚合之后的数据源情况。

image

这种预聚合的方式可以很显著的减少数据的存储(可减少100倍)。 Druid也是通过这种方式来减少数据的存储。 这种减少存储的方式也会带来副作用,比如我们没有办法再查询到每条数据具体的明细。换句话说,数据聚合的粒度是我们能查询数据的最小粒度。

数据分片

第一级分片-segment

DataSource只是一个逻辑概念,而Segment是数据的实际物理存储格式。Druid正是通过Segment实现对数据横向切割操作。从数据时间分布的角度来看,通过参数segmentGranularity的设置,Druid将不同的时间范围内的数据存储在不同的segment块中,这便是所谓的横向切割。这样访问Druid的数据仅需要访问对应时间的segment数据块,而不需要进行全表的数据范围查询。
image

同时在segment中也面向列进行数据压缩存储,这便是所谓的数据纵向切割。而且在Segment中使用了Bitmap进行了数据访问进行了优化。

第二级分片-shard

支持两种类型的分区策略:“散列”、“单维度”。在大多数情况下,建议使用散列分区,可以提高索引性能和创造大小更均匀的数据段。基于哈希散列首先选择第一级分片的segments,然后根据segments每一行的所有维度的hash来划分。

"partitionsSpec": {
    "type”: “hashed",
    "targetPartitionSize: 5000000
}

numShards:可以直接指定shard个数。当配置这个参数时,还可以配置partitionDimensions来指定维度,而不用全部维度。

单维度划分

首先自动选择一个维度划分,然后分离该维度成连续的范围。每一部分将包含所有行维度的值范围。

“partitionsSpec”: {
    “type”: “dimension”,
    “targetPartitionSize”: 5000000
 }

可以通过partitionDimension指定维度进行划分,不用自动选择的维度。

shard大小推荐

根据官方文档,shard大小推荐为300MB-700MB,需要调整两级分片的参数segmentGranularity和partitioningSpec。shard相当于是druid数据最小存储单位,分得太大的话,可能在各个历史节点分散不开。分得合适的话,查询的时候在历史节点比较分散,可以充分利用每个历史节点的cpu。

数据查询

Druid的查询是使用REST风格的HTTP请求查询服务节点(Broker、Historical、Realtime),这些服务节点暴露REST查询接口,客户端发送Json对象请求查询接口。一般情况下,查询服务接口发布在Broker节点,POST请求查询如下所示:
image

Druid查询类型

Druid在不同场景下,有很多的查询类型。对于各种类型的查询类型的配置可以json属性文件设置。Druid查询类型,概括一下为3大类:

  1. 聚合查询 - 时间序列查询(Timeseries)、排名查询(TopN)、分组查询(GroupBy)
  2. 元数据查询 - 时间范围(Time Boundary) 、段元数据(Segment Metadata)、数据源(Datasource)
  3. Search查询 - Search以聚合查询为主,与其它查询类型比较相对简单,使用上相对比较少,暂不介绍。

    如何对查询进行选择呢?

    在可能的情况下,我们建议使用的时间序列和TopN查询代替分组查询,分组查询是Druid最灵活的的查询,但是性能最差。时间序列查询是明显快于GROUPBY查询,因为聚合不需要分组尺寸。对于分组和排序在一个单一的维度,TopN查询更优于GROUPBY。

Druid Json查询属性

Druid json查询比较重要的几个属性是:queryType、dataSource、granularity、filter、aggregator等。

  • 查询类型(queryType):对应聚合查询下的3种类型值:timeseries、topN、groupBy
  • 数据源(dataSource):数据源,类似数据库中表的概念,对应数据导入时Json配置属性dataSource值。
  • 聚合粒度(granularity): 粒度决定如何得到数据块在跨时间维度,在配置查询聚合粒度里有三种配置方法:
  • 简单聚合粒度 - 支持字符串值有:all、none、second、minute、five_minute,ten_minute,fifteen_minute、thirty_minute、hour、day、week、month、quarter、year,all - 将所有块变成一块, none - 不使用块数据(它实际上是使用最小索引的粒度,none意味着为毫秒级的粒度);按时间序列化查询时不建议使用none,因为所有的毫秒不存在,系统也将尝试生成0值,这往往是很多。
  • 时间段聚合粒度 - Druid指定一精确的持续时间(毫秒)和时间戳返回UTC(世界标准时间)。
  • 常用时间段聚合粒度 - 与时间段聚合粒度差不多,但是常用时间指平时我们常用时间段,如年、月、周、小时等。下面对3种聚合粒度配置举例说明。

简单聚合粒度

查询粒度比数据采集时配置的粒度小,则不合理,也无意义,因较小粒度(相比)者无索引数据;如查询粒度小于采集时配置的查询粒度时,则Druid的查询结果与采集数据配置的查询粒度结果一样。
假设我们存储在Druid的数据使用毫秒粒度获取,数据如下:

{"timestamp": "2017-08-31T01:02:33Z", "name": "yangxuan", "classes" : "chinese"}  
{"timestamp": "2017-09-01T01:02:33Z", "name": "liuboyu", "language" : "english"}  
{"timestamp": "2017-09-02T23:32:45Z", "name": "luojiangyu", "language" : "english"}  
{"timestamp": "2017-09-03T03:32:45Z", "name": "DDD", "language" : "english"}

以"小时" 粒度提交一个groupby查询,查询配置如下:

{  
   "queryType":"groupBy",  
   "dataSource":"dataSource",  
   "granularity":"hour",  
   "dimensions":[  
      "language"  
   ],  
   "aggregations":[  
      {  
         "type":"count",  
         "name":"count"  
      }  
   ],  
   "intervals":[  
      "2000-01-01T00:00Z/3000-01-01T00:00Z"  
   ]  
}

按小时粒度进行的groupby查询结果中timestamp值精确到小时间,比小时粒度更小粒度值自动补填零,以此类推按天查询,则小时及小粒度补零。timestamp值为UTC查询结果如下:

[ {  
  "version" : "v1",  
  "timestamp" : "2017-08-31T01:00:00.000Z",  
  "event" : {  
    "count" : 1,  
    "language" : "chinese"  
  }  
}, {  
  "version" : "v1",  
  "timestamp" : "2017-09-01T01:00:00.000Z",  
  "event" : {  
    "count" : 1,  
    "language" : "english"  
  }  
}, {  
  "version" : "v1",  
  "timestamp" : "2017-09-02T23:00:00.000Z",  
  "event" : {  
    "count" : 1,  
    "language" : "english"  
  }  
}, {  
  "version" : "v1",  
  "timestamp" : "2017-09-03T03:00:00.000Z",  
  "event" : {  
    "count" : 1,  
    "language" : "english"  
  }  
} ]

如果指定查询粒度为none,则返回结果与数据导入时设置粒度(queryGranularity属性值)结果一样,此处的导入粒度为毫秒,结果如下:

[ {  
  "version" : "v1",  
  "timestamp" : "2017-08-31T01:02:33.000Z",  
  "event" : {  
    "count" : 1,  
    "language" : "chinese"  
  }  
}, {  
  "version" : "v1",  
  "timestamp" : "2017-09-01T01:02:33.000Z",  
  "event" : {  
    "count" : 1,  
    "language" : "english"  
  }  
}, {  
  "version" : "v1",  
  "timestamp" : "2017-09-02T23:32:45.000Z",  
  "event" : {  
    "count" : 1,  
    "language" : "english"  
  }  
}, {  
  "version" : "v1",  
  "timestamp" : "2017-09-03T03:32:45.000Z",  
  "event" : {  
    "count" : 1,  
    "language" : "english"  
  }  
} ]

时间段聚合粒度

指定一个精确时间持续时长(毫秒表示),返回UTC时间;支持可选项属性origin,不指定时默认开始时间(1970-01-01T00:00:00Z)

/**持续时间段2小时,从1970-01-01T00:00:00Z开始*/  
{"type": "duration", "duration": 7200000}

/**持续时间1小时,从origin开始*/  
{"type": "duration", "duration": 3600000, "origin": "2012-01-01T00:30:00Z"}

过滤(Filters)

等价于sql 查询的where。也是支持and,or,in,not等。

"filter": { "type": "selector", "dimension": <dimension_string>, "value": <dimension_value_string> }

聚合(Aggregations)

聚合类型如下:Count aggregator、Sum aggregators、Min / Max aggregators、Approximate Aggregations、Miscellaneous Aggregations

/**Druid进行Count查询的数据量并不一定等于数据采集时导入的数据量,因为Druid在采集数据并导入时已经对数据进行了聚合*/  
{ "type" : "count", "name" : <output_name> }  

/**longSumaggregator:计算值为有符号位64位整数*/
{ "type" : "longSum", "name" : <output_name>, "fieldName" : <metric_name> }  

/**doubleSum aggregator:与longSum类似,计算值为64位浮点型*/
{ "type" : "doubleSum", "name" : <output_name>, "fieldName" : <metric_name> }  

/** doubleMin aggregator */
{ "type" : "doubleMin", "name" : <output_name>, "fieldName" : <metric_name> }

/**doubleMax aggregator*/
{ "type" : "doubleMax", "name" : <output_name>, "fieldName" : <metric_name> }

/**longMin aggregator*/
{ "type" : "longMin", "name" : <output_name>, "fieldName" : <metric_name> }  

/** longMax aggregator*/
{ "type" : "longMax", "name" : <output_name>, "fieldName" : <metric_name> }

类似聚合(Approximate Aggregations)

基数聚合(Cardinality aggregator)
计算Druid多种维度基数,Cardinality aggregator使用HyperLogLog评估基数,这种聚合比带有索引的
hyperUnique聚合慢;一般我们强力推荐使用hyperUniqueaggregator而不是Cardinality aggregator,格式如下:

{  
  "type": "cardinality",  
  "name": "<output_name>",  
  "fieldNames": [ <dimension1>, <dimension2>, ... ],  
  "byRow": <false | true> # (optional, defaults to false)  
}

维度值聚合-当设置属性byRow为false(默认值)时,通过合并所有给定的维度列来计算值集合。单维度等价于:

SELECT COUNT(DISTINCT(dimension)) FROM <datasource>

对于多维度,等价如下:

SELECT COUNT(DISTINCT(value)) FROM (  
  SELECT dim_1 as value FROM <datasource>  
  UNION  
  SELECT dim_2 as value FROM <datasource>  
  UNION  
  SELECT dim_3 as value FROM <datasource>

行聚合-当设置属性byRow为true时,根所不同维度的值合并来计算行值,等价如下:

SELECT COUNT(*) FROM ( SELECT DIM1, DIM2, DIM3 FROM <datasource> GROUP BY DIM1, DIM2, DIM3 )

HyperUnique aggregator

“hyperunique”在创建索引时聚合的维度值使用HyperLogLog计算估计,更多资料请参考官网:

{ "type" : "hyperUnique", "name" : <output_name>, "fieldName" : <metric_name> }

后聚合(post-aggregators)

后聚合是对Druid进行聚合后的值进行聚全,如果查询中包括一个后聚合,那么确保所有聚合满足后聚合要求;后聚合有以下几种类型:

  1. Arithmetic post-aggregators
  2. Field accessor post-aggregator
  3. Constant post-aggregator
  4. JavaScript post-aggregator
  5. HyperUnique Cardinality post-aggregator
Arithmetic post-aggregators

算术后聚合应用已提供的函数从左到右获取字段,这些字段可聚合或后聚合;支持+, -, *, /, and quotient。

算术后聚合语法如下:

postAggregation : {  
  "type"  : "arithmetic",  
  "name"  : <output_name>,  
  "fn"    : <arithmetic_function>,  
  "fields": [<post_aggregator>, <post_aggregator>, ...],  
  "ordering" : <null (default), or "numericFirst">  
}

时间序列查询(Timeseries)

这些类型的查询以时间序列查询对象和返回一个JSON数组对象,每个对象表示时间序列查询的值,时间序列查询请求的Json的7个主要属性如下:

image

排名查询(TopN query)

TopN查询根据规范返回给定维度的有序的结果集,从概念上来讲,TopN查询被认为单维度、有序的类似分组查询。在某些情况下,TopN查询比分组查询(groupby query)快。TopN查询结果返回Json数组对象。TopN在每个节点将顶上K个结果排名,在Druid默认情况下最大值为1000。在实践中,如果你要求前1000个项顺序排名,那么从第1-999个项的顺序正确性是100%,其后项的结果顺序没有保证。你可以通过增加threshold值来保证顺序准确。

image


GitHub 加速计划 / druid / druid
27.83 K
8.56 K
下载
阿里云计算平台DataWorks(https://help.aliyun.com/document_detail/137663.html) 团队出品,为监控而生的数据库连接池
最近提交(Master分支:3 个月前 )
f060c270 - 8 天前
1613a765 * Improve gaussdb ddl parser * fix temp table 10 天前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐