[Druid] 1 基本概念和架构概览

1 简介

Druid是针对时间序列数据提供低延时的数据写入以及快速交互式查询的分布式OLAP数据库。
分布式OLAP数据库:
(1)ES-明细数据检索(OLAP聚合分析支持不好)
(2)Kylin-预计算+kv存储(预计算无法做到低延时)
(3)Presto-可直接读HDFS文件的查询引擎

image.png

注意:如果需要使用到join,一般来说会在之前进行一步流处理,将数据处理成一个宽表,再进行Druid端计算。

Druid的核心架构,结合了数据仓库,时间序列数据库,日志搜索系统。其中主要的特点有:
<1> 列式存储格式
Druid使用列式存储,在特定查询中只需要指定需要的列,这样能够给速度带来很大的提升;另外,每列都针对其特定数据类型进行了优化存储,支持更快浏览和聚合。
<2> 可扩展的分布式系统
<3> 大量并行计算
<4> 支持实时或者批量的数据摄入
<5> ‘自愈’,自动平衡,容易维护
<6> 云原生的容错架构,不会丢失数据
一旦Druid摄入了数据,就会产生副本存储起来
<7> 支持索引快速过滤
Druid使用CONCISE或Roaring压缩的bitmap索引来创建索引,以支持快速过滤和跨多列搜索。
<8> 基于时间的分区
Druid首先按时间对数据进行分区,然后可以根据其他字段进行分区。 这意味着基于时间的查询将仅访问与查询时间范围匹配的分区。
<9> 近似算法
Druid包括用于近似计数区别,近似排名以及近似直方图和分位数计算的算法。 这些算法提供有限的内存使用量,通常比精确计算要快得多。 对于精度比速度更重要的情况,Druid还提供了精确的计数区别和精确的排名。
<10> 摄取时自动汇总
Druid可选地(需要预先配置)在摄取时支持数据汇总,这种汇总会部分地预先聚合您的数据,并可以节省大量成本并提高性能。

2 基本概念

1.数据

image.png

按照列的不同类型,可以将数据分为以下三类:
(1) 时间序列(Timestamp):
Druid既是内存数据库,又是时间序列数据库,Druid中所有查询以及索引过程都和时间维度息息相关。Druid底层使用绝对毫秒数保存时间戳,默认使用ISO-8601格式展示时间(形如:yyyy-MM-ddThh:mm:sss.SSSZ,其中“Z”代表零时区,中国所在的东八区可表示为+08:00)。
(2)维度列(Dimensions),Druid的维度概念和OLAP中一致,一条记录中的字符类型(String)数据可看作是维度列,维度列被用于过滤筛选(filter)、分组(group)数据。如图3.1中page、Username、Gender、City这四列。
(3)度量列(Metrics),Druid的度量概念也与OLAP中一致,一条记录中的数值(Numeric)类型数据可看作是度量列,度量列被用于聚合(aggregation)和计算(computation)操作。如图3.1中的Characters Added、Characters Removed这两列。

2.roll up
Druid会在摄入数据的时候,对原始数据进行聚合操作。该过程称为上卷(roll up)。Roll up可以看做是在选择的列上进行的第一级聚合操作,以减少sgements的数量。
以下面数据为例:

{"timestamp":"2018-01-01T01:01:35Z","srcIP":"1.1.1.1", "dstIP":"2.2.2.2","packets":20,"bytes":9024}
{"timestamp":"2018-01-01T01:01:51Z","srcIP":"1.1.1.1", "dstIP":"2.2.2.2","packets":255,"bytes":21133}
{"timestamp":"2018-01-01T01:01:59Z","srcIP":"1.1.1.1", "dstIP":"2.2.2.2","packets":11,"bytes":5780}
{"timestamp":"2018-01-01T01:02:14Z","srcIP":"1.1.1.1", "dstIP":"2.2.2.2","packets":38,"bytes":6289}
{"timestamp":"2018-01-01T01:02:29Z","srcIP":"1.1.1.1", "dstIP":"2.2.2.2","packets":377,"bytes":359971}
{"timestamp":"2018-01-01T01:03:29Z","srcIP":"1.1.1.1", "dstIP":"2.2.2.2","packets":49,"bytes":10204}
{"timestamp":"2018-01-02T21:33:14Z","srcIP":"7.7.7.7", "dstIP":"8.8.8.8","packets":38,"bytes":6289}
{"timestamp":"2018-01-02T21:33:45Z","srcIP":"7.7.7.7", "dstIP":"8.8.8.8","packets":123,"bytes":93999}
{"timestamp":"2018-01-02T21:35:45Z","srcIP":"7.7.7.7", "dstIP":"8.8.8.8","packets":12,"bytes":2818}

rollup-index.json

{
  "type" : "index",
  "spec" : {
    "dataSchema" : {
      "dataSource" : "rollup-tutorial",
      "parser" : {
        "type" : "string",
        "parseSpec" : {
          "format" : "json",
          "dimensionsSpec" : {
            "dimensions" : [
              "srcIP",
              "dstIP"
            ]
          },
          "timestampSpec": {
            "column": "timestamp",
            "format": "iso"
          }
        }
      },
      "metricsSpec" : [
        { "type" : "count", "name" : "count" },
        { "type" : "longSum", "name" : "packets", "fieldName" : "packets" },
        { "type" : "longSum", "name" : "bytes", "fieldName" : "bytes" }
      ],
      "granularitySpec" : {
        "type" : "uniform",
        "segmentGranularity" : "week",
        "queryGranularity" : "minute",
        "intervals" : ["2018-01-01/2018-01-03"],
        "rollup" : true
      }
    },
    "ioConfig" : {
      "type" : "index",
      "firehose" : {
        "type" : "local",
        "baseDir" : "quickstart/tutorial",
        "filter" : "rollup-data.json"
      },
      "appendToExisting" : false
    },
    "tuningConfig" : {
      "type" : "index",
      "maxRowsPerSegment" : 5000000,
      "maxRowsInMemory" : 25000
    }
  }
}

其中维度列是srcIP和dstIP,时间序列时timestamp,度量列有count,packets和bytes。
其中"queryGranularity" : "minute"

image.png

发现01:01内的数据被聚合了,按照时间序列和维度列,即{timestamp,srcIP,dstIP}进行第一步聚合,对应的指标列进行sum操作。

总结,roll-up对什么范围内的数据进行第一步的聚合,是由"queryGranularity" : "minute"来决定的。
queryGranularity:默认为None,允许查询的时间粒度,单位与segmentGranularity相同,如果为None那么允许以任意时间粒度进行查询。

注意:当设定roll-up为true时,会带来信息量的丢失,因为roll-up的粒度会变成最小的数据可视化粒度,即毫秒级别的原始数据,如果按照分钟粒度进行roll-up,那么入库之后我们能够查看数据的最小粒度即为分钟级别。

在界面中,点击如下按钮可以显示上面的json


image.png

3.Sharding和Segment
Druid是时间序列数据库,也存在分片(Sharding)的概念。Druid对原始数据按照时间维度进行分片,每一个分片称为段(Segment)。
Segment是Druid中最基本的数据存储单元,采用列式(columnar)存储某一个时间间隔(interval)内某一个数据源(dataSource)的部分数据所对应的所有维度值、度量值、时间维度以及索引。

注意:
Segment是按照time进行分区,默认的,一个Segment文件是每隔一个time interval创建的,而这个time interval是在granularitySpec中的segmentGranularity来配置的。

为了能够让Druid在查询下良好运行,segment文件的大小推荐在300mb~700mb中。
如果大小超过这个区间,考虑改变这个time interval的单位(换成更小的单位)或者重新对数据分区,在TunningConfig中增加partitionsSpec,调整targetPartitionSize(此参数的最佳起点是500万行)。


image.png

Druid将不同时间范围内的数据存储在不同的Segment数据快中,这就是所谓的数据横向切割,这种设计为Druid带来的显而易见的优点:按时间范围查询数据时,仅需要访问对应时间段内的这些Segment数据块,而不需要进行全表数据范围查询,大大提高效率。

将行式数据转换为列式存储结构:按需加载,提高查询速度。有3种类型的数据结构:
(1)timestamp列,long数组
(2)指标列,int数组或者float数组
(3)维度列,支持过滤分组。使用压缩的BitMap索引

3.1 Segment数据结构

image.png

Timestamp列和Metric列比较简单,他们在实现中被存储为通过lz4压缩的整型或浮点数组。当一个查询需要访问某列数据时,只需要解压缩这些列,然后读取出这些列的数据,然后执行预定义的聚合操作。对于查询过程中不需要的列,Druid会直接跳过对这些列数据的访问。

维度列Dimension列由于要实现filter和group by,实现有所不同,dimension列包含以下三种数据结构:
(1)一个字典:将值从字符串(所有的dimension列的数值都被当做字符串处理)映射到整数id
(2)一个值的列表(正排数据):把列中的数值通过字典编码以后,将数字id存储在列表里面
(3)一个bitmap倒排索引:对于列里面的每个不同的值,都对应存储为一个bitmap,用来表示哪一行数据包含该值

3.2 为什么我们需要这三种数据结构呢?
1.通过字典将字符串映射成数字id,数字id通常比字符串更小,因此可以被更紧凑的存储。
2.第三点中的bitmap,通常被称为倒排索引,用于支持快速的过滤操作(bitmap对AND和OR操作的速度非常快)。
3.最后,第二点中的值列表将用于支持group by和topN查询,而普通的查询只需要根据filter出来的行去来聚合相应的metirc就可以了,而不需要访问上述2中的值列表。

image.png

下面以上面表格中的page列数据为例,来演示一下这三种数据结构,如下所示:

1.编码了列值的字典:
{
    'Justin Bieber': 0,
    'Ke$ha':         1
}
 
2.列值数据(正排):
[0, 0, 1, 1](第一个和第二个0代表第一,二行数据编码,即上述字典中的'Justin BieBer',第三个和第四个代表第三四行数据,即上述字典中的'Ke$ha')
 
3. Bitmaps:字典中的每个值对应一个bitmap
value='Justin Bieber': [1, 1, 0, 0]
value='Ke$ha': [0, 0, 1, 1]

注意:bitmap跟其他两种数据结构不同,其他两种数据结构都是跟随数据量的增长而线性增长的(最差情况下),而 bitmap的大小=数据的总行数 * 列中值的种类数 (也就是字典的size)。这就意味着如果值得的种类很多,那么bitmap中为1的数量将会非常稀疏,这种情况下bitmap有可能被大幅压缩。Druid针对这种情况,采用了特殊的压缩算法,比如roaring bitmap压缩算法。

倒排索引相关文档:http://hbasefly.com/2018/06/19/timeseries-database-8/?jqdajo=dz6ta

疑问:

图片1

图片2

公司的Druid设置的按小时进行分区,但是每个小时时间段生成的Segment文件数目却不是一份,另外发现有些文件的size为0,是因为这部分数据还没有push到磁盘上。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,978评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,954评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,623评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,324评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,390评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,741评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,892评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,655评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,104评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,451评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,569评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,254评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,834评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,725评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,950评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,260评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,446评论 2 348

推荐阅读更多精彩内容

  • 我们知道Druid能够同时提供对大数据集的实时摄入和高效复杂查询的性能,主要原因就是它独到的架构设计和基于Data...
    allin8116阅读 478评论 0 2
  • 我们知道Druid能够同时提供对大数据集的实时摄入和高效复杂查询的性能,主要原因就是它独到的架构设计和基于Data...
    零度沸腾_yjz阅读 21,508评论 3 17
  • Druid.io(以下简称Druid)是面向海量数据的、用于实时查询与分析的OLAP存储系统。Druid的四大关键...
    大诗兄_zl阅读 6,449评论 0 9
  • 概述 设计原则 快速查询:部分数据的聚合 + 内存化 + 索引 水平扩展能力:分布式数据 + 并行化处理 实时分析...
    zfylin阅读 2,650评论 0 1
  • 什么是Druid Druid是一个高效的数据查询系统,主要解决的是对于大量的基于时序的数据进行聚合查询。数据可以实...
    Hanze2111阅读 55,761评论 5 29