架构

什么是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还有三个外部依赖:

  • Zookeeper集群
  • 元数据存储实例:Mysql
  • Deep Storage:HDFS/或者分布式文件系统

参考:
https://help.aliyun.com/document_detail/72987.html?spm=a2c4g.11186623.6.675.5ebb7ba7mPowo3

Segments

Druid 把它的索引存储到一个Segment文件中,Segment文件是通过时间来分割的。

Segment数据结构

对于摄入到Druid的数据的列,主要分三种类型,时间列(timestamp)、维度列(dimensions)和指标列(聚合列metrics)。如下图

  • 时间列 每一条数据都必须有此列,此列会决定数据所在的Segment(后面会介绍),所有的查询也都需要此列,在上面对应的数据列为timestamp,在系统中会存储的字段名为__time;
  • 维度列 Druid会针对这些列建立bitmap的倒排索引,用于筛选数据,结合时间列便能快速找到需要查询的数据。根据假设的分析模型,我们只需要publisher和advertiser,而其他例如gender和country并非我们需要分析的列,便可以在建立模型的时候忽略,需要注意的是Druid只有String型的维度列,如果我们需要对维度列做数字范围的筛选,需要做特殊处理
  • 聚合列(metric column) Druid会根据我们定义的时间粒度,再结合维度列在数据接入的时候对数据进行聚合,并根据聚合类型,用不同的数据结构来保存这一列。这一列算是Druid比较复杂的一个概念,与下面介绍的预聚合有很大关系,稍后我会进一步解释。在假设的分析模型中,我们需要对click列求sum。

于是我们可以建立一个简单的分析模型:

"dataSchema": {"dataSource": "wiki","parser": {"type": "string",       #说明读取的每一行数据都是都是string类型"parseSpec": {"format": "json",     #说明数据的格式需要通过json来解析"timestampSpec": {    #指明时间列字段和格式"column": "timestamp","format": "auto"},"dimensionsSpec": {   #指明需要索引的维度,只有加到此处的维度才可以查询"dimensions": ["publisher", "advertiser"]}}},"metricsSpec": [{"type" : "count",  # 统计行数"name" : "rows"},{"name": "click_sum","fieldName": "click","type": "longSum"    #整数求和}],"granularitySpec" : {     #预聚合相关的配置"segmentGranularity" : "HOUR","queryGranularity" : "HOUR"}}

上面的分析模型是一个简单的使用,Druid本身的配置性非常强,我会在后续章节介绍这些配置的实现原理。

预聚合

上面的样例数据我们可以称之为原始数据,为我们直接采集到的数据,每天这样的数据有千亿甚至万亿。基于这些原始数据,如果我们需要分析点击量,成本是非常高的,因为我们尽管我们可以通过维度筛选出一部分数据,但是数据量还是很巨大,需要扫描这些数据来聚合,如此便会直接导致查询时长加长。为了避免这种情况,Druid采用了预聚合的设计,在数据接入的时候,就开始根据定义的维度,对数据进行聚合。Druid的预聚合分为两个级别,第一级是时间粒度,第二级是维度。

时间粒度

例子中模型的配置有segmentGranularity 和queryGranularity两个关键的参数,可以理解为一个是段的粒度,一个是查询的最小粒度。首先,Druid会根据timestamp和segmentGranularity来决定数据所属的段;然后,再根据timestamp和queryGranularity来决定存储在Druid的__time。

维度

Druid的预聚合过程也是其索引的过程,通常Druid会在内存中使用ConcurrentSkipListMap存储数据,以TimeAndDims(时间和维度值信息)作为key,根据配置的metricsSpec对数据进行聚合,聚合的结果作为value。为了避免OOM,最后会以bitmapIndex的数据格式周期性的落地到磁盘存储。

于是,样例数据最后存储的格式大概为:

Segment sampleData_2011-01-01T01:00:00:00Z_2011-01-01T02:00:00:00Z_v1_0__time    publisher   advertiser  rows    click_sum
2011-01-01T01:00:00Z    ultratrimfast.com   google.com  1   0
2011-01-01T01:00:00Z    bieberfever.com google.com  3   1
Segment sampleData_2011-01-01T02:00:00:00Z_2011-01-01T03:00:00:00Z_v1_0__time   publisher   advertiser  rows    click_sum
2011-01-01T02:00:00Z    ultratrimfast.com   google.com  2   1

segmentGranularity为小时,所以对应的段都是小时级别。queryGranularity为小时,聚合的结果对应的__time也是整点,会将每小时的数据全部聚合到其所属小时的开始时间。 另外说明,倒数第二例为总共的行数,最后一列为点击数。sampleData_2011-01-01T02:00:00:00Z_2011-01-01T03:00:00:00Z_v1_0中的v1通常为Task启动时获取的TaskLock的version,可以理解为Task启动的时间。

对于时间列和指标列处理比较简单,直接用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推到历史节点并修改元数据。


参考:https://www.jianshu.com/p/852bb8cfed6b

采用技术

除了 MPP 架构外,它还运用到了四点重要的技术,分别是:

  • 预聚合
  • 列式存储
  • 字典编码
  • 位图索引

预聚合算是 Druid 的一个非常大的亮点,通过预聚合可以减少数据的存储以及避免查询时很多不必要的计算。

特性

为分析而设计:为OLAP工作流的探索性分析而构建,支持各种过滤、聚合和查询等类;

快速的交互式查询:Druid的低延迟数据摄取架构允许事件在他们创建后毫秒内可被查询到;

高可用性:Druid的数据在系统更新时依然可用,规模的扩大和缩小都不会造成数据丢失;

可扩展:Druid已实现每天能够处理数十亿事件和TB级数据。

使用场景

1、需要交互式聚合和快速探究大量数据时;

2、需要实时查询分析时;

3、具有大量数据时,如每天数亿事件的新增、每天数10T数据的增加;

4、对数据尤其是大数据进行实时分析时;

5、需要一个高可用、高容错、高性能数据库时。

Druid 使用建议

本小节主要想结合实际问题,给大家提供一些 Druid 的使用建议,供大家参考。

①什么样的业务适合用 Druid?

建议如下:

时序化数据:Druid 可以理解为时序数据库,所有的数据必须有时间字段。
实时数据接入可容忍丢数据(tranquility):目前 tranquility 有丢数据的风险,所以建议实时和离线一起用,实时接当天数据,离线第二天把今天的数据全部覆盖,保证数据完备性。
OLAP 查询而不是 OLTP 查询:Druid 查询并发有限,不适合 OLTP 查询。
非精确的去重计算:目前 Druid 的去重都是非精确的。
无 Join 操作:Druid 适合处理星型模型的数据,不支持关联操作。
数据没有 update 更新操作,只对 segment 粒度进行覆盖:由于时序化数据的特点,Druid 不支持数据的更新。

k8s集群部署

参考git:https://github.com/626626cdllp/k8s/tree/master/druid

python 操作druid

参考git:

https://pythonhosted.org/pydruid/

https://druid.apache.org/blog/2014/04/15/intro-to-pydruid.html

hue链接druid

http://gethue.com/quick-task-how-to-query-apache-druid-analytic-database/

Druid 简介,架构,部署,python连接,hue链接druid相关推荐

  1. python连接sqlserver_python 链接sqlserver 写接口实例

    我是使用pymssql完成的sqlserver,首先下载符合版本的pymssql的whl,然后安装,在pycharm的default setting->project Interpreter中确 ...

  2. 数据层优化-jdbc连接池简述、druid简介

    终于回到既定轨道上了,这一篇讲讲数据库连接池的相关知识,线程池以后有机会再结合项目单独写篇文章(自己给自己挖坑,不知道什么时候能填上),从这一篇文章开始到本阶段结束的文章都会围绕数据库和dao层的优化 ...

  3. python后台架构Django教程——连接读写mysql数据库

    全栈工程师开发手册 (作者:栾鹏) python教程全解 本文衔接至python后台架构Django开发全解. 有其他问题请先阅读:http://blog.csdn.net/luanpeng82548 ...

  4. python链接mysql 判断是否成功_【初学python】使用python连接mysql数据查询结果并显示...

    因为测试工作经常需要与后台数据库进行数据比较和统计,所以采用python编写连接数据库脚本方便测试,提高工作效率,脚本如下(python连接mysql需要引入第三方库MySQLdb,百度下载安装) # ...

  5. python链接hbase模块_HBase实战(1):使用Python连接Hbase数据库

    来源于 https://blog.csdn.net/duan_zhihua/java/article/details/80622166 使用Python连接Hbase数据库 1,Hbase下载. 下载 ...

  6. Asible简介及部署

    Asible简介及部署 1.Ansible基本概述 Ansible 能做什么 Ansible 软件特点 Ansible基础架构 2.Ansible服务安装 1)安装ansible(不用手动启动服务) ...

  7. Elasticsearch7从入门到精通(简介、部署、原理、开发、ELK)

    Elasticsearch7从入门到精通(简介.部署.原理.开发.ELK) 第1章.Elasticsearch简介 1-1.Elasticsearch介绍 Elasticsearch官方网站:http ...

  8. 《Python分布式计算》 第5章 云平台部署Python (Distributed Computing with Python)

    序言 第1章 并行和分布式计算介绍 第2章 异步编程 第3章 Python的并行计算 第4章 Celery分布式应用 第5章 云平台部署Python 第6章 超级计算机群使用Python 第7章 测试 ...

  9. Nginx网站服务与LNMP架构部署(详解)

    Nginx网站服务与LNMP架构部署 1.Nginx服务基础 2.编译安装Ngiax 3.Nginx的运行控制 4.配置文件nginx.conf 5.Nginx虚拟主机 6.LNMP架构部署 1.源码 ...

最新文章

  1. eclipse修改文件代码不起作用,输出时还是老的,估计是缓存问题
  2. BZOJ2132 圈地计划
  3. Scala集合实现WordCount单词统计代码
  4. 常见的社会潜规则有哪些?
  5. MSSQL获取指定日期的SQL语句
  6. 火山PC画板打造UI
  7. 双因素认证令牌_安全令牌:防止双因素令牌认证攻击
  8. java真垃圾_JAVA吧真的很垃圾!!!
  9. macOS10.13.6及以下版本不能自动升级到更高版本的解决方案
  10. Spring5基础知识
  11. 手把手教你用深度学习做物体检测(二):数据标注
  12. 互联网的行业都有哪些岗位?
  13. LeetCode50——一题学会快速幂算法
  14. 手把手教你实战开发黑白棋实时对战游戏
  15. 《网络协议》笔记-网络分层
  16. docker安装wnameless/oracle-xe-11g并运行(手写超详细)
  17. 应用检查后台启动权限方法(小米官方给出的)
  18. SAP中输出采购订单GR/IR标识清单
  19. 五笔打字:速成手册---半小时学会五笔打字
  20. 洛谷P1075 质因数分解C语言

热门文章

  1. 方法区jdk1.7,1.8版本的构造变化
  2. 结构体中操作c语言,C语言中结构体的操作
  3. html5期末考试题答案,HTML5期末考试题型
  4. LIRe 源代码分析 6:检索(ImageSearcher)[以颜色布局为例]
  5. DirectShow 在 VS2010 中开发环境的设置
  6. python字符串描述错误的_Python字符串错误字符
  7. spss与python和sql区别_Python/Excel/SPSS/SQL数据处理方法比较之2 - 数据查看
  8. 计算机硬件密码,计算机硬件技术基础综合性实验任务书(08)密码门锁的模拟_C
  9. on() 和 click() 的区别:二者在绑定静态控件时没有区别,但是如果面对动态产生的控件,只有 on() 能成功的绑定到动态控件中。以下实例中原先的 HTML 元素点击其身后的 Dele
  10. Oracle如何使用PL/SQL调试存储过程