实时或准实时的海量数据分析对于许多应用来说是困难的。一方面是数据量太大;另一方面,分析条件是可变的。本文是Cloudflare在处理DNS查询分析时的技术选择和踩过的洞。
日前,Cloudflare公布了所有DNS分析工具。由于Cloudflare规模庞大,Cloudflare必须创造性地解决这个问题。在本文中,我们将介绍DNS分析工具的组件,这些工具每月帮助处理数万亿条日志。

Cloudflare已经有一个用于HTTP日志的数据管道。我们希望DNS分析工具可以利用这个功能。每当边缘服务处理HTTP请求时,它都会生成Cap'n Proto格式的结构化日志消息,并将其发送到本地多路复用服务。考虑到数据量,我们只记录部分DNS报文数据,只包括我们感兴趣的数据,比如响应代码、大小或者查询名称,这使得我们平均每条报文只保留150字节左右的数据。然后将其与元数据处理融合。在边缘合并数据和元数据的好处是,我们可以将计算成本分摊到数以千计的边缘服务器上,只记录我们需要的信息。
复用服务运行在每个边缘节点上,将来自多个服务的日志消息组装起来,通过TLS安全通道传输到我们的仓库进行处理。运行在仓库中的相应服务接收日志并将其分解成几个Apache Kafka集群。Apache Kafka作为生产者和下游消费者之间的缓冲,防止消费者出现故障或需要维护时数据丢失。从版本0.10开始,Kafka允许通过机架感知分发副本,从而提高对机架或站点故障的恢复能力,并为我们提供容错的未处理消息存储。
拥有结构化的日志队列使我们能够在不访问生产节点的情况下跟踪问题。在项目前期,我们会跳过队列,找到我们需要的大致时间段的偏移量,然后将数据以Parquet格式提取到HDFS中进行离线分析。
关于聚合
HTTP分析服务是围绕生成聚合的流处理器构建的,因此我们计划使用Apache Spark将日志自动传输到HDFS。Parquet本身不支持索引以避免全表扫描,因此通过API进行在线分析或报告是不切实际的。虽然有像parquet-index这样的扩展可以在数据上创建索引,但是它们不能实时运行。有鉴于此,当初的设计是只给客户看总结报告,保留原始数据供内部排查。
汇总的问题是它们只能处理基数低的列。通过聚合,给定时间范围内的每一列都将展开为大量的行,因此可以聚合响应代码。如果域名是热门的,假设每分钟查询1000次,可以预计通过聚合每分钟可以减少1000次数据,但实际上并非如此。
由于DNS缓存的存在,解析器在TTL期间不会进行DNS查询。TTL往往超过一分钟。因此,服务器多次看到相同的请求,而我们的数据偏向于不可缓存的查询,如拼写错误或随机前缀子域攻击。在实践中,当域名用于聚合时,我们可以看到行数最多可以减少到原来的1/60,多分辨率存储的聚合几乎可以抵消行数的减少。聚合也可以通过使用多个分辨率和组合键来完成,因此聚合甚至可以在高基数列上产生比原始数据更多的行。
由于这些原因,我们首先在区域级别汇总日志,这对于趋势分析来说足够了,但是对于具体原因分析来说太粗略了。例如,我们正在调查一个数据中心的短暂流量爆发。拥有未聚合的数据使我们能够将问题缩小到特定的DNS查询,然后将该查询与错误配置的防火墙规则相关联。在这种情况下,只有摘要日志是有问题的,因为它只聚集了少量的请求。
所以我们开始研究几个OLAP系统。我们研究的第一个系统是德鲁伊。我们对前端分割数据的能力印象深刻,这使我们能够生成任意维度的报告。Druid已经部署在类似的环境中,每天有超过1000亿个事件,因此我们有信心它可以工作,但在测试了采样数据后,我们无法证明数百个节点的硬件成本。几乎在同一时间,Yandex开放了他们的OLAP系统ClickHouse。
点击之家

ClickHouse的系统设计更简单,集群中的所有节点功能相同,使用ZooKeeper进行协调。我们构建了一个由几个节点组成的集群,发现性能相当可观,于是继续构建概念验证。我们遇到的第一个障碍是缺乏工具和社区规模小,所以我们深入研究了ClickHouse设计,以了解它是如何工作的。
ClickHouse并不直接支持Kafka,因为它只是一个数据库,所以我们用Go写了一个适配器服务。它从Kafka读取Cap'n Proto编码的消息,转换成TSV,通过HTTP接口批量插入ClickHouse。后来我们谈到用GO SQL驱动替换ClickHouse的HTTP接口来提高性能。从那以后,我们开始为这个项目提供性能改进。我们在性能评估过程中学到的一点是,ClickHouse写性能很大程度上取决于批处理大小,即一次插入的行数。要了解原因,我们需要了解更多关于ClickHouse如何存储数据的信息。
ClickHouse最常用的存储表引擎是MergeTree系列。它在概念上类似于Google的BigTable或Apache Cassandra中使用的LSM算法,但它避开了中间内存表,直接写入磁盘。这使得写入吞吐量非常出色,因为每个插入的批处理只能按“主键”排序,压缩并写入磁盘以形成一个段。无内存表也意味着只追加数据,不支持数据修改或删除。目前,删除数据的唯一方法是按日历月删除数据,因为该段不会与月边界重叠。ClickHouse团队正在积极努力使这一功能可配置。另一方面,这使得写入和段合并没有冲突,因此吞吐量与并行插入的数量成线性比例,直到I/O满负荷为止。但是,这也意味着它不适合小批量生产,这也是我们依赖Kafka和inserter服务进行缓冲的原因。ClickHouse在后台不断合并,所以很多段会合并写很多遍。太多未合并的段将触发写电流限制,直到合并完成。我们发现每秒钟插入每个表一次效果最好。
表读取性能的关键是索引和数据在磁盘上的排列。无论处理速度有多快,当引擎需要从磁盘上扫描太多数据时,都需要花费大量的时间。ClickHouse是列存储,所以每个段都包含每一列的文件,每一行都有排序值。这样,可以跳过查询中不存在的列,然后通过向量化并行处理多个单元。为了避免完全扫描,每个段还有一个稀疏索引文件。由于所有列都是按“主键”排序的,所以索引文件只包含每第n行的标记,这样即使对于非常大的表,它也可以保存在内存中。例如,默认设置是每隔8192行标记一次。这种方法只需要122,070个标记来索引一个有1万亿行的表。在这里,您可以查看ClickHouse中的主键,深入了解它的工作原理。
使用主键列查询时,索引返回所考虑的行的大致范围。理想情况下,范围应该是宽且连续的。例如,当典型的用法是为单个区域生成报告时,将区域放在主键的第一个位置将导致按每个区域排序,这样磁盘将连续读取单个区域,但如果按主时间戳排序,则无法保证生成报告的连续性。行只能以一种方式排序,因此必须仔细选择主键,并考虑典型的查询负载。在我们的示例中,我们优化了单个区域的读取查询,并为探索性查询提供了一个包含采样数据的独立表。由此得到的教训是,我们不再为了各种目的而优化索引,而是分解差异并添加更多的表。
这种专有设计的结果是一个在区域中聚合的表。因为没有办法过滤数据,所以扫描所有查询行的成本要高得多。这使得分析师在很长一段时间内无法计算基本的聚合,因此我们决定使用物化视图来增量计算预定义的聚合,如计数器、唯一键和分位数。物化视图使用批量插入的排序阶段来执行生产性工作——计算聚合。所以新插入的段排序后,也会生成一个表,表中的行代表维度,列代表聚合函数状态。聚合状态和最终结果之间的区别在于,我们可以使用任何时间分辨率来生成报告,而无需实际存储多个分辨率的预先计算的数据。在某些情况下,状态和结果可能是相同的——例如,基本计数器,每小时的计数可以通过累积分钟计数来生成,但对唯一访问者求和或延迟分位数是没有意义的。这是聚合状态更有用的时候,因为它允许更复杂的状态,如超对数位图,有意义地合并,以便每小时聚合生成每小时独立的访问者估计。缺点是存储状态的开销可能比存储最终值的开销大得多——上面的HLL状态压缩后大约是20-100字节/行,而计数器只有8个字节。使用这些表格可以快速可视化整个区域或站点的整体趋势,我们的API服务也使用它们进行简单的查询。通过在同一位置同时使用增量聚合和不聚合的数据,我们可以通过流处理完全简化架构。
基础设施和数据集成
我们为RAID-10使用了12个6TB磁盘,但在一次磁盘故障后重新评估了它们。在第二次迭代中,我们迁移到RAID-0有两个原因。首先,故障磁盘无法热插拔,其次,阵列重建需要几十个小时,这降低了I/O性能。与等待阵列完成重建相比,更换故障节点并使用内部复制通过网络填充数据要快得多。为了弥补节点故障的高可能性,我们切换到三向复制,将每个切片的副本分发到不同的机架,并开始计划复制到单独的数据仓库。
另一个磁盘故障突出了我们正在使用的文件系统的问题。起初,我们使用XFS,但它在复制期间同时从2个对等方开始锁定,因此在复制完成之前中断了段复制。这个问题表现为大量的I/O活动。随着损坏的部分被删除,磁盘使用量增加很少,所以我们逐渐迁移到ext4,这个文件系统不存在这个问题。

数据可视化
当时我们只是依靠熊猫和ClickHouse的HTTP接口进行临时分析,但我们想让分析和监控变得更容易。因为我们知道Caravel,所以我们将它与ClickHouse整合在一起。
超集是一个直观的数据可视化平台,它允许分析师以交互方式对数据进行切片和分段,而无需编写任何SQL语句。它最初是由AirBnB为Druid构建并开源的,但随着时间的推移,它已经通过使用SQLAlchemy为几十种不同的数据库方言提供了基于SQL的后端支持。因此,我们编写并开源了一个ClickHouse方言,并将其集成到超集中。
超集为我们提供了特殊的可视化服务,但对于我们的监控用例来说,它仍然不是完美的。在Cloudflare,我们广泛使用Grafana来可视化所有指标,因此我们将其与Grafana集成并开源。
它使我们能够利用新的分析数据无缝扩展现有的监控仪表板。我们很喜欢这个功能,所以希望给用户提供同样的查看分析数据的能力。因此,我们构建了一个Grafana应用程序来可视化来自Cloudflare DNS Analytics的数据。最后,我们会在您的Cloudflare控制面板分析中提供它。随着时间的推移,我们将添加新的数据源、维度和其他有用的方法来显示Cloudflare中的数据。


