Apache Druid 学习:组件以及查询类型
OLAP
基本概念
维度(Dimension): 指的是观察数据的一个角度,是考虑问题的一类属性,这些属性的集合统称为一个维。
维的级别(Level): 对数据的观察还存在细节程度的不同,在druid中一般表示为时间的粒度(granularity),比如一秒,一分钟,一小时,一天……
度量(Measure): 度量是用来聚合分析计算的数字信息,在druid中称为”metrics”,它可以是存储在数据库中,也可以是通过策略计算得出的。比如一篇文章的点击数、或者是根据评论数、点击数、转发数计算出的热点值
对于数据处理
向下钻取(Drill-down)/上卷(Roll-up): 改变维的层次和级别,变换分析的粒度。Roll-up在于提升维的级别(或者称粒度)或者减少维度来聚合数据,展现总览,Drill-down反之,降低维的级别(或者称粒度)或增加维度来查看细节
切片(slice)和切块(dice): 当维度为两个时,我们对获取数据(查询)的操作称之为切片,当维度的数量大于两个时,我们称之为切块
旋转(Pivoting): 变换维的方向,例如表格中的行列互换
查询条件参数
字段名 | 描述 | 是否必须 |
---|---|---|
queryType | 查询类型,对应聚合查询下的类型值:timeseries、topN、groupBy等 | 是 |
dataSource | 数据源,类似关系数据库中表的概念,对应数据导入时Json配置属性dataSource值 | 是 |
descending | 返回结果是否逆序,默认值为否(正序) | 否 |
intervals | 查询时间区间 | 是 |
filter | 对Dimension进行过滤,可以根据情况对几个维度组合不同的filter类型(and、or、not、bound),还可以根据需要定义javascript function进行过滤 | 否 |
aggregations | 指定度量在聚合时候的计算策略,例如相加、或者求平均值、又或者取最后一个值,在内置类型不满足的情况下可以使用javascript。比如某手游中我统计了我每一局击杀小怪数量,以及野怪的数量,通过聚合策略sum,我能知道我从开号以来击杀了多少小怪和野怪。 | 否 |
postAggregations | 后聚合策略,提供了多个度量组合生成新度量的能力,主要有利于聚合计算的抽象,避免对一些指标的重复计算。举个例子,假如我需要一个度量,是我击杀小怪和野怪的总和,那么,我只需要在后聚合阶段计算,只需要拿小怪和野怪的数量相加一次,大大地提高了计算效率。 | 否 |
granularity | 查询的时间粒度,最细粒度为秒,最大粒度为all,提供了时间维度级别的调整并对数据进行上卷和向下钻取的能力 | 是 |
dimensionSpec | 提供了维度在聚合前输出展示值定制的能力,比如在Dimension age一列中,拿到的是字符串类型的数字,我希望转成数字类型,又或者定制一个javascript function,统一以 ${age} year old的形式展现 | 否 |
limit | 返回结果数量限制 | 否 |
context | 表示对当前查询本身的一些配置,比如设置查询超时的时间,又比如是否使用缓存,在通用的配置基础上,每种查询类型还有特定的配置,详见文档 | 否 |
基本组件
filter
过滤器,在查询语句中是一个json对象,用来对维度进行筛选,表示满足filter的是我们需要的数据。类似于SQL中的where。
类型 | 功能 |
---|---|
SelectorFilter | 功能类似于SQL中的where key=value |
AndFilter, OrFilter, NotFilter | 功能类似于SQL中and、or、not三种过滤器。支持递归嵌套,可以构造出丰富的逻辑表达式 |
RegexFilter | 正则表达式,支持任意维度值的java正则 |
SearchFilter | 通过字符串匹配维度,支持多种表达式 |
InFilter | 功能类似于SQL中where key in (value1, value2) |
IntervalFilter | 针对于时间维度过滤 |
BoundFilter | 功能类似于SQL中的大于、小于、等于三种算子 |
JavaScriptFilter | 上述filter均不能满足可以自己写JavaScript来过滤维度 |
aggregator
聚合可以在采集数据时规格部分的一种方式,汇总数据进入Druid之前提供。
聚合也可以被指定为在查询时多查询的部分,聚合类型如下:
类型 | 功能 |
---|---|
CountAggregator | SQL count(key) |
SumAggregator | SQL sum(key) |
MaxAggregator, MinAggregator | SQL max(key), min(key) |
DistinctCountAggregator | SQL count(distinct key) |
JavaScriptAggregator | 上述aggregator均不能满足可以自己写JavaScript来定义计算 |
post-aggregator
类型 | 功能 |
---|---|
ArithmeticPostAggregator | 支持对聚合后指标进行”+ - * / quotient”计算 |
FieldAccessPostAggregator | 直接获取聚合的字段(维度,指标) |
ConstantPostAggregator | 返回常数 |
JavaScriptPostAggregator | 上述postAggregator均不能满足可以自己写JavaScript来定义计算 |
查询类型
聚合查询
timeseries
时序查询,实际上即是对数据基于时间点(timestamp)的一次上卷。适合用来看某几个度量在一个时间段内的趋势。排序可按时间降序或升序
字段名 | 描述 | 是否必须 |
---|---|---|
queryType | 查询类型,必须为 “timeseries” | 是 |
dataSource | 数据源,比如 “wikipedia” | 是 |
descending | 返回结果是否逆序,默认值为否(正序) | 否 |
intervals | 查询时间区间 | 是 |
granularity | 聚合粒度,粒度决定如何在跨时间维度得到数据块 | 是 |
filter | 否 | |
aggregations | 否 | |
postAggregations | 否 | |
limit | 否 | |
context | 否 |
context:
1.grandTotal
2.零填充
如果时间范围内没有值,则会填充0
时间序列查询通常用零填充空的内部时间段。例如,如果您对间隔2012-01-01 / 2012-01-04发出“天”粒度时间序列查询,而2012-01-02没有数据存在,您将收到:
1 | [ |
topN
在时间点的基础上,又增加了一个维度(OLAP的概念算两个维度),进而对源数据进行切片,切片之后分别上卷,最后返回一个聚合集,你可以指定某个指标作为排序的依据。官方文档称这对比单个druid dimension 的groupBy 更高效。适合看某个维度下的时间趋势,(比如美国和中国十年内GDP的增长趋势比对,在这里除了时间外国家就是另外一个维度)
字段名 | 描述 | 是否必须 |
---|---|---|
queryType | 查询类型,必须为 “topN” | 是 |
dataSource | 数据源,比如 “wikipedia” | 是 |
intervals | 查询时间区间 | 是 |
granularity | 聚合粒度,粒度决定如何在跨时间维度得到数据块 | 是 |
filter | 否 | |
aggregations | 否 | |
postAggregations | 否 | |
dimension | 除了时间之外聚合的维度,只能定义一个维度 | 是 |
threshold | topN中的N,例如:希望查询到top2,则值为2 | 是 |
metric | topN中用来排序的指标 | 是 |
context | 否 |
groupBy
适用于两个维度以上的查询,druid会根据维度切块,并且分别上卷,最后返回聚合集。相对于topN而言,这是一个向下钻取的操作,每多一个维度意味着保留更多的细节。(比如增加一个行业的维度,就可以知道美国和中国十年内,每一年不同行业贡献GDP的占比)
注意:如果要使用时间作为唯一分组进行聚合,或者在单个维度上使用有序groupBy进行聚合,请优先考虑使用Timeseries和TopN查询。在某些情况下,它们的性能可能会更好。有关更多详细信息,请参见下面的替代方法。
字段名 | 描述 | 是否必须 |
---|---|---|
queryType | 查询类型,必须为 “groupBy” | 是 |
dataSource | 数据源,比如 “wikipedia” | 是 |
dimensions | 需要聚合的所有维度 | 是 |
limitSpec | 同关系型数据库中的limit | 否 |
having | 同关系型数据库中的having | 否 |
granularity | 聚合粒度,粒度决定如何在跨时间维度得到数据块 | 是 |
filter | 否 | |
aggregations | 否 | |
postAggregations | 否 | |
intervals | 查询时间区间 | 是 |
subtotalsSpec | 类似于的grouping sets | 否 |
context | 否 |
普通查询
select
类似SQL中的select操作,select用来查看Druid中存储的数据,并支持按照指定过滤器和时间查看指定维度和指标。不支持aggregations和post aggregations
注意:建议您尽可能使用scan查询类型而不是select。在涉及大量segment的情况下,select查询可能具有很高的内存和性能开销,但是scan查询没有此问题。
两者之间的主要区别是“扫描”查询不支持分页。但是,即使没有分页,scan查询类型也能够返回几乎无限数量的结果,使得分页在许多情况下是不必要的。
字段名 | 描述 | 是否必须 |
---|---|---|
queryType | 查询类型,必须为 “select” | 是 |
dataSource | 数据源,比如 “wikipedia” | 是 |
intervals | 查询时间区间 | 是 |
descending | 返回结果是否逆序,默认值为否(正序) | 否 |
filter | 否 | |
dimensions | 需要查询的维度列表 | 否 |
metrics | 需要查询的指标列表 | 否 |
granularity | 聚合粒度,粒度决定如何在跨时间维度得到数据块,默认是Granularity.ALL | 否 |
pagingSpec | 分页 | 是 |
context | 否 |
scan
扫描查询以流模式返回行,Select查询和Scan查询之间的最大区别是,Scan查询子返回给客户端数据之前不会将所有行数据保留在内存中
而select查询将把行保留在内存中,如果返回太多行,则会导致内存压力。扫描查询可以返回所有行,而无需发出另一个分页查询。
除了将scan查询发送给server的用法外,还可以直接向historical历史记录进程或streaming ingestion流式提取任务发出扫描查询。如果要并行检索大量数据,这将很有用。
字段名 | 描述 | 是否必须 |
---|---|---|
queryType | 查询类型,必须为 “scan” | 是 |
dataSource | 数据源,比如 “wikipedia” | 是 |
intervals | 查询时间区间 | 是 |
resultFormat | 返回结果类型:list,compactedList或valueVector。目前仅list和compactedList受支持。默认是list | 否 |
filter | 否 | |
columns | 需要scan的维度和指标,默认为所有 | 否 |
batchSize | 返回数据之前默认缓存最多多少行 | 否 |
limit | 查询返回最大的数据条目,如果不指定,则返回所有的数据 | 否 |
order | 返回数据的order,基于timestamp,并且只有__time被包含在columns中才生效 | 否 |
legacy | 否 | |
context | 否 |
search
类似SQL中的Like操作,但是支持更多的匹配操作
字段名 | 描述 | 是否必须 |
---|---|---|
queryType | 查询类型,必须为 “search” | 是 |
dataSource | 数据源,比如 “wikipedia” | 是 |
granularity | 聚合粒度,粒度决定如何在跨时间维度得到数据块 | 是 |
filter | 否 | |
limit | 每个历史进程的最大查询返回数据条目(默认是1000) | 否 |
intervals | 查询时间区间 | 是 |
searchDimensions | 需要search的维度(默认是所有维度),key like value中的key | 否 |
query | search维度需要匹配的value,key like value中的value | 是 |
sort | 指定应如何对搜索结果进行排序,包括字典编排(默认排序),字母数字,strlen和数字排序 | 否 |
context | 否 |