druid是什么?
Druid是一个用于大数据实时查询和分析的高容错、高性能开源分布式时序的面向olap的数据库系统,旨在快速处理大规模的数据,并能够实现快速查询和分析。尤其是当发生代码部署、机器故障以及其他产品系统遇到宕机等情况时,Druid仍能够保持100%正常运行。创建Druid的最初意图主要是为了解决查询延迟问题,当时试图使用Hadoop来实现交互式查询分析,但是很难满足实时分析的需要。而Druid提供了以交互方式访问数据的能力,并权衡了查询的灵活性和性能而采取了特殊的存储格式。
存储方式:
Druid是一个列存储,每列被单独存储。Druid查询相当好,是因为只查询所需的列。不同的列还可以采用不同的压缩方式,不同的列也可以有与它们相关的不同的索引。
架构主要的组成部分:
Historical
realtime
Coordinator
Broker
Indexer
druid高性能的原因:
- 类LSM-Tree的架构,保证了Druid的高性能写入
- 通过“查询职责分离模式+不支持更新操作” 保证了组件职责的单一以及数据处理的简单性,保证了查询性能的高效性
- 数据存储结构:DataSource以及Segment的完美设计
Druid中的Datasource可以理解为RDBMS中的表,其包含下面三个重要的概念:
1.时间列(Timestamp):每行数据的时间值,默认使用UTC时间格式,保存到毫秒级别,本列是数据聚合以及范围查询的重要指标
2.维度列(Dimension):标识数据行的列,可以是一列,也可以是多列
3.指标列(Metric):用来做计算或是统计的列,可以是一列,也可以是多列
相对于其他数据库,Druid Datasource最大的特点是在输入存储时,就可以对数据进行聚合操作,该特性不仅可以节省存储的空间,而且可以提高聚合查询的效率。 - Segment为Druid中数据的物理存储格式,Segment通过以下特性来支撑Druid的高性能:
数据的横向切割:横向切割主要只指站在时间范围的角度,将不同时间段的数据存储在不同的Segment文件中(时间范围可以通过segmentGranularity进行设置),查询时只需要更具时间条件遍历对应的Segment文件即可。
数据的纵向切割:面向列进行进行数据压缩
使用BitMap等技术对数据访问进行优化
与其他olap的数据库的对比
Druid作为一个OLAP数据库。OLAP数据库需要支持类似上卷、切块、切片、下钻等操作,但不适合明细查询。对于类似根据某主键ID定位唯一数据的任务,OLAP数据库并不能友好支持。常用的OLAP数据库实现方式以下几种:1)数据检索引擎,比如ES;2)预计算加KV存储实现,比如Kylin;3)SQL on Hadoop 引擎,比如 Presto、SparkSQL。
首先是数据检索引擎的代表ES,ES可以存储结构化和非结构化数据,同时具备明细查询和聚合查询能力,由于其自身是一个数据检索引擎,其索引类型并不是针对聚合分析设计的,所以聚合查询方面开销较大;其次,ES不但要保存所有的原始数据,还需要生成较多的索引,所以存储空间开销会更大,数据的写入效率方面会比Druid差一些。
与ES相比,Druid只能处理结构化数据,因为它必须预定义Schema;其次,Druid会对数据进行预聚合以减少存储空间,同时对数据写入和聚合进行优化。但是,由于进行了预聚合,所以Druid抛弃掉了原始数据,导致其缺少原始明细数据查询能力。如果业务方有需求,可以关闭预聚合,但会丧失Druid的优势。
其次是预计算 + kv存储方式 ,KV存储需要通过预计算实现聚合,可以认为Key涵盖了查询参数,而值就是查询结果,由于直接从KV存储进行查询,所以速度非常快。缺点是因为需要在预计算中处理预设的聚合逻辑,所以损失了查询灵活性,复杂场景下的预计算过程可能会非常耗时,而且面临数据过于膨胀的情况;由于只有前缀拼配一种索引方式,所以在大数据量的复杂过滤条件下,性能下降明显;且缺少聚合下推能力。
与预计算+KV存储方式相比,Druid 是使用Bitmap索引的列式存储,查询速度肯定不如KV存储快; 但是由于使用内存增量索引,增量预聚合的模式,写入即可查,无需等待预计算生成Cube,所以实时性更强;其次,Druid可针对任意维度组合过滤、聚合,查询更加灵活;最后,Scatter & Gather模式支持一定的聚合下推。
最后是SQL on Hadoop, 这类引擎的SQL支持通常很强大,且无冗余数据,不需要预处理。缺点是因为其直接通过计算引擎对Hadoop上的文件进行操作,所以响应速度较慢且QPS相对较低。
与SQL on Hadoop方式相比,Druid的SQL支持有限,但在逐渐完善;必须预定义维度指标。其优势在于可达到亚秒级响应,并发较高。
MPP架构的系统(Presto/Impala/SparkSQL/Drill等)有很好的数据量和灵活性支持,但是对响应时间是没有保证的。当数据量和计算复杂度增加后,响应时间会变慢,从秒级到分钟级,甚至小时级都有可能。
搜索引擎架构的系统(Elasticsearch等)相对比MPP系统,在入库时将数据转换为倒排索引,采用Scatter-Gather计算模型,牺牲了灵活性换取很好的性能,在搜索类查询上能做到亚秒级响应。但是对于扫描聚合为主的查询,随着处理数据量的增加,响应时间也会退化到分钟级。
预计算系统(Druid/Kylin等)则在入库时对数据进行预聚合,进一步牺牲灵活性换取性能,以实现对超大数据集的秒级响应。
没有一个引擎能同时在数据量,灵活性和性能这三个方面做到完美,用户需要基于自己的需求进行取舍和选型。
数据导入方式
通过前面的介绍我们知道在流式处理领域,有两种数据处理模式,一种为Stream Push,另一种为Stream Pull。
Stream Pull 如果Druid以Stream Pull方式自主地从外部数据源拉取数据从而生成Indexing Service Tasks,我们则需要建立Real-Time Node。Real-Time Node主要包含两大“工厂”:一个是连接流式数据源、负责数据接入的Firehose(中文翻译为水管,很形象地描述了该组件的职责);另一个是负责Segment发布与转移的Plumber(中文翻译为搬运工,同样也十分形象地描述了该组件的职责)。在Druid源代码中,这两个组件都是抽象工厂方法,使用者可以根据自己的需求创建不同类型的Firehose或者Plumber。Firehose和Plumber给我的感觉,更类似于Kafka_0.9.0版本后发布的Kafka Connect框架,Firehose类似于Kafka Connect Source,定义了数据的入口,但并不关心接入数据源的类型;而Plumber类似于Kafka Connect Sink,定义了数据的出口,也不关心最终输出到哪里。
Stream Push 如果采用Stream Push策略,我们需要建立一个“copy service”,负责从数据源中拉取数据并生成Indexing Service Tasks,从而将数据“推入”到Druid中,我们在druid_0.9.1版本之前一直使用的是这种模式,不过这种模式需要外部服务Tranquility,Tranquility是一个发送数据流到Druid的http客户端,Tranquility组件可以连接多种流式数据源,比如Spark-Streaming、Storm以及Kafka等,所以也产生了Tranquility-Storm、Tranquility-Kafka等外部组件。
实时数据流摄入方式
Standalone Realtime Node(Streaming pull)
Indexing-service + Tranquility(Streaming push)
KafkaIndex-indexing-service
durid的优点:
1、开源的,分布式的,列存储的,适用于实时数据分析的存储系统,能够快速聚合、灵活过滤、毫秒级查询、和低延迟数据导入
2、Druid在设计时充分考虑到了高可用性,各种节点挂掉都不会使得druid停止工作(但是状态会无法更新);
3、Druid中的各个组成部分之间耦合性低,如果不需要实时数据完全可以忽略实时节点;
4、Druid使用Bitmap indexing加速列存储的查询速度,并使用CONCISE算法来对bitmap indexing进行压缩,使得生成的segments比原始文本文件小很多;
5、查询的工具不好用
什么情况下需要Druid?
当你需要在大数据集上面进行快速的,交互式的查询时
当你需要进行特殊的数据分析,而不只是简单的键值对存储时
当你拥有大量的数据时 (每天新增数百亿的记录、每天新增数十TB的数据)
当你想要分析实时产生的数据时
当你需要一个24x7x365无时无刻不可用的数据存储时