> Photo by Carlos Muza on Unsplash
Apache druid是最流行的在线分析处理(OLAP)开源解决方案之一。 Airbnb和Netflix等许多科技公司都使用它来对每分钟包含数百万个事件的数据流进行查询。 它使公司可以近乎实时地做出决策。
Druid的主要卖点是它可以轻松扩展到百万RPM的写入速度,甚至超过亚秒级的延迟,并且在整个操作过程中都具有很高的可用性。 但是如今,许多数据库都具有高可用性和亚秒级的延迟,那么,什么使druid与众不同?
> From druid's official documentation (http://druid.io/technology)
关系数据库(例如MySQL)擅长处理通常具有行级查询的事务性工作负载。 对于分析,您需要获取某些列的汇总,这需要扫描多个分片上的很多行。 RDBMS不能足够高效地执行实时数据探查。
就NoSQL键值数据库而言,由于您需要查询多个节点上的多个分区,因此聚合计算肯定会效率低下。 您可以通过以某种粒度(例如1分钟)预先计算数据并将其存储在密钥中来解决此问题。 但是,通过遵循这种方法,您将失去在多个窗口上进行浏览的能力。 为所有可能的列组合存储聚集体也是不可行的,因为这会导致存储需求呈指数增长。
Druid旨在解决这些问题,即能够在提供低延迟和高可用性的同时探索实时数据和历史数据。
Druid由多个节点组成,每个节点扮演不同的角色。 这些所有节点相互协调工作(通常使用Apache Zookeeper进行协调)以提供性能。
> Druid Architecture from http://druid.io/docs/latest/design/
让我们更详细地讨论每个节点。 如果您已经熟悉节点及其交互,则可以直接跳到最后一部分。
这些节点负责处理读写的实时数据。 这些文章特别包括四个主要步骤:
这些节点从深度存储加载段,然后在其之上提供查询。 在Druid上运行的大多数分析查询大部分时间将进入这些节点。 因此,这些节点是集群的主要工作者。
节点从Zookeeper获得在深度存储中发布的任何新段的信息。 然后下载并加载该细分受众群以进行投放。 节点还在本地磁盘中缓存了一些段,这使它们可以在发生某些重启时快速为查询提供服务。 节点还提供读取一致性,因为它们仅处理不可变的段。
历史节点也可以分为多个层次,每个层次具有不同的可配置性。 例如 可以将具有较高核心数的节点放在一个层中,以为经常访问的数据提供服务,而为其他数据提供资源较少的节点。
ZooKeeper的可用性阻碍了这些节点加载新段的能力,但是旧段继续提供服务而没有任何停机时间。
> Historical Nodes in action (from druid whitepaper)
所有用户查询都转到代理节点。 然后,这些节点将请求重定向到适当的历史和实时节点,将结果合并并发送回去。 节点还维护内存中的LRU缓存(可以更改为使用Memcached)。 缓存包含每个段的结果。 但是,仅历史节点段的结果是缓存,因为实时数据会经常保留更改。
这些节点还使用Zookeeper发现其他节点。 如果Zookeeper发生故障,它们将以集群状态的最后快照为数据提供服务。
由于"历史"节点很笨,因此协调员有责任告诉他们该怎么做。 具体来说,协调器发出以下命令:
只有一个协调器节点被选为领导者,它负责整个操作,而其余节点仅充当备份者。
协调器从Zookeeper获取最新的群集状态,以及有关应从MySQL服务的段的信息。 MySQL的中断以及Zookeeper的中断阻碍了分配或删除新段的能力,但是旧段仍然是可查询的。
由于每个节点只关注一个主要问题,因此简化了整个系统的复杂性。 所有组件之间的交互最少,集群内通信故障(在读取过程中)几乎不影响可用性。 群集通过Zookeeper保持同步。 即使Zookeeper掉线了,尽管您将无法创建任何新的段,从而影响写入,但读取仍然可能发生。
由于druid是为分析查询而设计的,因此它以列导向的格式存储数据。 面向列的格式可以提供更好的压缩率(因为单个列中的大多数数据都是相似的)和更好的查询性能,因为通常在分析查询中并非所有列都可以访问,因此仅加载实际需要的数据。 对于字符串列,德鲁伊通常执行字典编码,然后应用LZF压缩以减小数据大小。
Druid维护着一个字符串值的倒排索引,这样我们就可以知道在哪个行中可以看到一个特定的值。 这允许仅扫描其中存在值的那些行。
上表的倒排索引看起来像
Foo:[1,0,0,1,0,1]
Bar:[0,1,1,0,1,0]
其中1表示索引中的行中存在特定键。 如果要扫描包含Foo和Bar的所有行,只需对两个索引进行OR。
为了获得准确的基数汇总,例如确定每分钟访问您的站点的唯一用户数,您需要将用户存储在某种数据结构(例如HashSet)中,然后对其中的元素总数进行计数。 但是,这导致大量空间需求。
另一方面,Druid使用HyperLogLog对其进行性能测试,其准确性约为97%。 对于大多数运行分析查询的人来说,这通常很好。 您甚至可以通过在索引过程中在摄取时预先计算HLL来使其更快。
德鲁伊可以在摄取时预聚合数据(称为汇总)。 这减少了存储数据的大小,也使聚合查询快得多。 在这种情况下,您确实会丢失每行信息,这就是为什么可以在摄取期间将其禁用。
Druid在代理上维护每个段的查询缓存,这有助于快速返回结果。 它还在历史和实时服务器中缓存数据,以加快扫描速度。
> Per segment Cache (from druid whitepaper)
协调器以这样的方式分配段:在历史节点之间不偏斜段。 它考虑了数据大小,源和新近度,因此还提供了最佳性能,例如 普通查询涵盖了跨越最近创建的细分的单个数据源,因此明智的做法是以更高的速率复制最近创建的细分,以使这些查询可以由多个节点提供服务。
Druid需要用于数据分发和保留的必填时间戳列。 包含一年中分布的时间戳的数据按天划分更好,而具有一天中分布的时间戳的数据按小时划分更好。 此时间戳还用于在写入Druid时忽略旧事件。 按时间分区还有助于更好地分配和复制段。
如果您想了解有关Druid的更多信息,请参考以下链接:
(本文翻译自Kartik Khare的文章《What Makes Apache Druid Great for Realtime Analytics?》,参考:
https://codeburst.io/what-makes-apache-druid-great-for-realtime-analytics-61f817ee5ff6)