Apache Druid —当代大数据分析的必要条件
术语"大数据"是指具有高容量,高速度和多样化的数据的数字存储。 大数据分析是使用软件来发现那些大型数据存储中的趋势,模式,相关性或其他有用见解的过程。 Apache Druid基本上有能力证明自己是构建大数据分析平台的骨干之一。
根据Wikipedia的介绍,Druid是一种用Java编写的面向列的开源分布式数据存储。 Druid旨在快速提取大量事件数据,并在数据之上提供低延迟查询。
Druid的核心设计结合了数据仓库,时间序列数据库和搜索系统的想法,从而创建了一个统一的系统,可以对广泛的用例进行实时分析。
Druid将这三个系统中的每个系统的关键特征合并到其接收层,存储格式,查询层和核心体系结构中。
Druid的主要特点:
列式存储:
Druid分别存储和压缩每一列,只需要读取特定查询所需的内容即可,这支持快速扫描,排名和按组。
本机搜索索引:
关于Druid的索引实现的一个重要方面是,它使用了倒排索引。 反向索引是一种索引数据结构,用于存储从内容(例如单词或数字)到其在一个文档或一组文档中的位置的映射。 倒排索引的目的是允许快速的全文本搜索,但当将文档添加到数据库时,这样做会增加处理量。
反向索引有两种类型:记录级反向索引包含每个单词的文档引用列表。 单词级倒排索引还包含文档中每个单词的位置。
Druid为字符串值创建倒排索引,以加快搜索和过滤操作。
流式处理和批量摄取:
德鲁伊允许其用户利用Lambda Architecture的工作原理。 Lambda体系结构是一种数据处理体系结构,旨在通过利用批处理和流处理方法来处理大量数据。
Druid提供了到Apache Kafka的连接器,用于通过流和HDFS实时提取数据,提供用于批量提取的AWS S3。 它还支持其他流传输和批处理管道以进行数据提取。
时间优化的分区:
与时间序列数据库类似,Druid按时间对数据进行智能分区,以实现快速的面向时间的查询。
与许多传统系统不同,Druid可以选择在摄取数据时预聚合数据。 此预聚合步骤称为汇总,可以节省大量存储空间。 汇总是对选定的一组列进行的第一级聚合操作,可减少存储数据的大小。 在汇总活动期间,输入行按时间戳和维度列进行分组,并在度量标准列上进行汇总汇总。
启用汇总后,具有相同维度和时间戳的所有行(在基于queryGranularity的截断之后)都可以折叠或汇总为Druid中的单个行。
灵活的列和SQL支持:
Druid可以优雅地处理不断发展的模式和嵌套数据。 在旧版dataSchema中,flattenSpec位于dataSchema→解析器→parseSpec→flattenSpec中,负责弥合潜在嵌套的输入数据(例如JSON,Avro等)与Druid的平面数据模型之间的差距。
除了基于本机JSON的本地语言外,Druid还通过HTTP或JDBC讲SQL。
Druid组件和工作原理:
Druid具有多进程,分布式架构,旨在实现云友好且易于操作。 每种Druid进程类型都可以独立配置和扩展,从而为您的集群提供最大的灵活性。 这种设计还提高了容错能力:一个组件的故障不会立即影响其他组件。
Druid有几种过程类型,下面简要描述:
协调人:
负责管理集群上的数据可用性。 Druid协调员流程主要负责部门管理和分配。 更具体地说,Druid协调器进程与历史进程进行通信,以基于配置加载或删除段。
Druid协调器负责加载新段,删除过时的段,管理段复制以及平衡段负载和段压缩。
Druid协调器维护与Zookeeper集群的连接以获取当前集群信息。
协调器还维护与数据库的连接,该数据库包含有关可用段和规则的信息。 可用的段存储在段表中,并列出应在集群中加载的所有段。 规则存储在规则表中,并指示应如何处理段。
段压缩:
在每次运行期间,Druid协调器通过合并小段或拆分大段来压缩数据段。
当未根据段大小优化段时,这可能会降低查询性能,这很有用。
压缩任务可能由于以下原因而失败。
· 如果压缩任务的输入段在启动之前被删除或遮盖,则该压缩任务将立即失败。
· 如果较高优先级的任务在与压缩任务的时间间隔重叠的时间间隔内获取了时间块锁,则压缩任务将失败。
一旦压缩任务失败,协调器将简单地再次检查失败任务间隔内的段,并在下一次运行中发出另一个压缩任务。
霸王:
负责控制数据提取工作负载的分配。 霸主进程负责接受任务,协调任务分配,围绕任务创建锁以及将状态返回给调用方。
可以将霸王配置为以以下两种模式之一运行:本地或远程(默认为本地)。
在本地模式下,Overlord还负责创建用于执行任务的Peon。
在本地模式下运行霸主时,还必须提供所有MiddleManager和Peon配置。 本地模式通常用于简单的工作流程。
在远程模式下,Overlord和MiddleManager在单独的进程中运行,并且可以在不同的服务器上单独运行。 如果我们打算将索引服务用作所有Druid索引的单个端点,则建议使用此模式。
中层经理:
MiddleManager进程是执行提交任务的工作进程。 中级经理将任务转发给在单独的JVM中运行的Peons。 我们为任务分配单独的JVM的原因是为了资源和日志隔离。
每个Peon一次只能运行一个任务,但是,MiddleManager可能有多个Peon。
索引服务:
Apache Druid索引服务是一种高可用性的分布式服务,可运行与索引相关的任务。
索引任务创建(有时销毁)Druid段。 索引服务具有类似主/从的体系结构。
索引服务由三个主要组件组成:
· 一个可以运行单个任务的Peon组件。
· 管理Peons的中间管理器组件。
· 一个Overlord组件,用于管理向MiddleManager的任务分配。 霸主和MiddleManager可以在同一进程上运行,也可以跨多个进程运行,而MiddleManager和Peons始终在同一进程上运行。
经纪人:
负责处理来自外部客户端的Druid查询。 当我们在分布式集群上运行Druid时,Broker是路由查询的过程。 它了解发布到ZooKeeper的元数据,该元数据涉及哪些段存在于哪些流程中并路由查询,以使它们命中正确的流程。 此过程还将所有单个过程的结果集合并在一起。
查询转发:
Druid Broker确定要转发查询的流程,Broker流程首先根据Zookeeper中的信息构建世界视图。 Zookeeper维护有关"历史记录和流式提取Peon进程"以及它们正在服务的段的信息。
对于Zookeeper中的每个数据源,"代理"流程都会构建一个时间段以及为这些数据段提供服务的流程。 当收到针对特定数据源和时间间隔的查询时,Broker流程将对与查询间隔相关的查询数据源的时间轴进行查询,并检索包含查询数据的流程。 然后,Broker进程将查询向下转发到选定的进程。
快取
代理进程使用具有LRU缓存无效策略的缓存。 Broker缓存存储每个段的结果。 缓存可以是每个Broker进程的本地缓存,也可以使用外部分布式缓存(例如memcached)在多个进程之间共享。
代理程序每次接收到查询时,都会首先将查询映射到一组细分。 这些细分结果的子集可能已经存在于缓存中,并且可以直接从缓存中提取结果。 对于缓存中不存在的任何段结果,代理进程将把查询转发到历史进程。
历史进程返回其结果后,代理会将这些结果存储在缓存中。 实时段永远不会被缓存,因此对实时数据的请求将始终转发到实时进程。 实时数据一直在变化,因此缓存结果将是不可靠的。
历史:
Druid历史进程负责存储可查询的数据。 每个"历史"进程都保持与Zookeeper的恒定连接,并监视一组可配置的Zookeeper路径以获取新的细分信息。 历史过程并不直接与彼此或与协调器过程进行通信,而是依靠Zookeeper进行协调。
如何加载和服务细分:
协调器流程负责为历史流程分配新的细分。 通过在与"历史"进程关联的加载队列路径下创建一个临时的Zookeeper条目来完成分配。
当"历史记录"进程在其加载队列路径中注意到一个新的加载队列条目时,它将首先在本地磁盘目录(缓存)中查找有关段的信息。 如果缓存中不存在有关该段的信息,则"历史"进程将下载有关新段的元数据以从Zookeeper服务。 该元数据包括有关该段在深度存储中的位置以及如何解压缩和处理该段的规范。 历史进程完成对片段的处理后,将在Zookeeper中与该过程关联的服务片段路径下宣布片段。 此时,该段可供查询。
段缓存及其工作方式:
当"历史记录"进程在其加载队列路径中注意到一个新的段条目时,"历史记录"进程首先检查其本地磁盘上的可配置缓存目录,以查看该段之前是否已下载。 如果本地缓存条目已经存在,则Historical(历史)进程将直接从磁盘读取段二进制文件并加载该段。
首次启动历史记录过程时,也会利用段缓存。 启动时,历史进程将搜索其缓存目录,并立即加载并提供找到的所有段。 此功能允许历史进程联机后立即对其进行查询。
Druid的摄取方法
下表列出了Druid最常用的数据提取方法,并进行了比较,以帮助我们选择最适合自己需要的方法。 每种摄取方法都支持其自己的源系统集。
流媒体
最推荐,最流行的流媒体摄取方法是直接从Kafka读取的Kafka Indexing Service。 如果我们更喜欢Amazon Kinesis,则Kinesis索引服务也可以很好地工作。
下表比较了主要可用选项:
批处理
从文件批量加载时,应使用一次性任务,并且有三个选项:index_parallel(本地批处理;并行),index_hadoop(基于Hadoop)或索引(本地批处理;单任务)。
通常,我们建议在满足您需求时进行本机批处理,因为它的设置较为简单(它不依赖于外部Hadoop集群)。 但是,在某些情况下,基于Hadoop的批量提取可能是一个更好的选择,例如,当您已经有一个正在运行的Hadoop群集并且想要使用现有群集的群集资源进行批量提取时。
下表比较了三个可用选项:
Druid的摄入规格:
无论我们采用哪种摄取方式,都将使用一次性任务或正在进行的"监督程序"(随时间运行并监督一组任务)将数据加载到Druid中。 无论如何,任务或管理者定义的一部分是摄入规范。
摄取规范包括三个主要组成部分:
· dataSchema,用于配置数据源名称,主时间戳,维度,指标以及转换和过滤器(如果需要)。
· ioConfig,它告诉Druid如何连接到源系统以及如何解析数据。 有关更多信息,请参阅每种摄取方法的文档。
· tuningConfig,用于控制特定于每种摄取方法的各种调整参数。
我在下面共享了两种Ingestion-Spec,其中我们使用实时Kafka流传输和通过来自AWS S3的文件的批处理将数据提取到同一数据源中。
Ingestion-Spec sample for Realtime Druid Ingestion for Kafka Indexing Service
Ingestion-Spec for Native Batch Ingestion into Druid from a file stored on AWS S3
Druid的数据模型
数据源
德鲁伊数据存储在数据源中,该数据源类似于传统RDBMS中的表。 德鲁伊提供了一个独特的数据建模系统,该系统与关系模型和时间序列模型都有相似之处。
主时间戳
Druid模式必须始终包含主时间戳。 主时间戳用于对数据进行分区和排序。 Druid查询能够快速识别和检索与主要时间戳列的时间范围相对应的数据。 Druid还能够使用主时间戳列进行基于时间的数据管理操作,例如删除时间块,覆盖时间块和基于时间的保留规则。
基于timestampSpec解析主时间戳。 此外,granularitySpec控制基于主时间戳的其他重要操作。
维度
维度是按原样存储并且可以用于任何目的的列。 我们可以在查询时以临时方式对维度进行分组,过滤或应用聚合器。
如果我们在禁用汇总功能的情况下运行,那么维度集将被视为要吸收的一组列,并且其行为与我们从不支持汇总功能的典型数据库中所期望的完全一样。
尺寸是通过DimensionsSpec配置的。
指标
指标是以聚合形式存储的列。 启用汇总时,它们最有用。 指定度量标准后,我们可以为Druid选择一个聚合函数,以在摄取期间将其应用于每一行。
度量是通过metricsSpec配置的。
分区
数据源中段的最佳分区和排序可能会对占用空间和性能产生重大影响。
Druid数据源始终按时间划分为多个时间块,每个时间块包含一个或多个段。 此分区针对所有摄取方法进行,并且基于摄取规范的dataSchema的segmentGranularity参数。
使用根据您选择的摄取类型而变化的选项,还可以进一步细分特定时间块内的细分。 通常,使用特定维度进行此次级分区将提高局部性,这意味着具有相同维度值的行将存储在一起并可以快速访问。
区隔
Apache Druid将其索引存储在段文件中,该段文件按时间进行分区。 在基本设置中,将为每个时间间隔创建一个分段文件,其中该时间间隔可在granularitySpec的segmentGranularity参数中配置。
为了使Druid在繁重的查询负载下正常运行,重要的是,段文件的大小应在建议的300MB-700MB范围内。 如果段文件大于此范围,则应考虑更改时间间隔的粒度或对数据进行分区,并在partitionsSpec中调整targetPartitionSize(此参数的最佳起点是500万行)。
段的核心数据结构:
在这里,我们描述了段文件的内部结构,该结构基本上是柱状的:每列的数据都布置在单独的数据结构中。 通过分别存储每列,Druid可以通过仅扫描查询实际需要的那些列来减少查询延迟。
共有三种基本列类型:时间戳列,维度列和指标列,如下图所示:
timestamp和metric列很简单:在幕后每个都是由LZ4压缩的整数或浮点值的数组。 一旦查询知道需要选择的行,它就简单地解压缩这些行,取出相关的行,然后应用所需的聚合运算符。 与所有列一样,如果查询不需要一列,则该列的数据将被跳过。
Data-Structure of the implementation strategy behind a Druid Segment
维度列有所不同,因为它们支持筛选和分组操作,因此每个维度都需要以下三个数据结构:
· 将值(始终被视为字符串)映射到整数ID的字典,
· 列值的列表,使用1中的字典进行编码,以及
· 对于列中的每个不同值,一个位图指示哪些行包含该值。
为什么要使用这三个数据结构? 字典仅将字符串值映射为整数id,以便可以紧凑地表示(2)和(3)中的值。 (3)中的位图(也称为反向索引)允许进行快速过滤操作(特别是,位图对于快速应用AND和OR运算符非常方便)。 最后,group by和TopN查询需要(2)中的值列表。 换句话说,仅基于过滤器汇总指标的查询不需要触摸存储在(2)中的维度值列表。
Druid集群设置:
Apache Druid旨在作为可伸缩的容错群集进行部署。 Druid进程可以按照我们喜欢的任何方式进行部署,但是为了易于部署,我们建议将其组织为三种服务器类型:主服务器,查询服务器和数据服务器。
· 主机:运行协调器和霸主流程,管理数据可用性和接收。
· 查询:运行代理和可选的路由器进程,处理来自外部客户端的查询。
· 数据:运行Historical和MiddleManager进程,执行提取工作负载并存储所有可查询的数据。
Druid架构:
Druid有基于微服务的架构,可以认为是一个反汇编的数据库。 Druid中的每个核心服务(摄取,查询和协调)都可以单独或联合部署在商用硬件上。
Apache Druid是一个实时分析数据库,旨在对大型数据集进行快速切片和切分分析(" OLAP"查询)。 Druid最常用作数据库,以支持对实时摄取,快速查询性能和高正常运行时间很重要的用例。 因此,Druid通常用于为分析应用程序的GUI供电,或用作需要快速聚合的高并发API的后端。 德鲁伊最适合面向事件的数据。
德鲁伊的常见应用领域包括:
-
点击流分析(网络和移动分析)
-
网络遥测分析(网络性能监控)
-
服务器指标存储
-
供应链分析(制造指标)
-
应用程序性能指标
-
数字营销/广告分析
-
商业智能/ OLAP
Druid最适合的用例:
如果我们的用例适合以下几个描述符,则Druid可能是一个不错的选择:
-
插入率很高,但更新并不常见。
-
我们大多数查询是聚合查询和报告查询("分组依据"查询)。 我们可能还会搜索和扫描查询。
-
我们将查询等待时间定为100毫秒到几秒钟。
-
我们的数据具有时间成分(Druid包括与时间特别相关的优化和设计选择)。
-
我们可能有多个表,但是每个查询仅命中一个大的分布式表。 查询可能会击中多个较小的"查找"表。
-
我们具有高基数数据列(例如URL,用户ID),并且需要对其进行快速计数和排名。
-
我们要从Kafka,HDFS,平面文件或对象存储(如Amazon S3)加载数据。