首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

震撼!ClickHouse 警报评估,增量合并大揭秘!

在我的开发历程中,Highlight.io 展现出了非凡的价值。其强大的错误跟踪功能令人印象深刻。每当程序出现问题,它总能迅速发出警报,提供的详细错误堆栈跟踪和丰富的上下文信息,使我能够高效地定位问题根源。错误分组功能更是实用,让我可以轻松识别重复出现的问题,从而提前制定应对策略。

在性能监控方面,Highlight.io 同样出色。我可以根据项目需求自定义性能指标,实时掌握程序的性能状况。清晰直观的性能数据和图表,如同为程序性能打造的精准仪表盘,让我能及时发现性能瓶颈。而它给出的性能优化建议,更是为提升程序性能和响应速度提供了有力指导。

近日,Zane Mayberry 发布长篇论文《Alert Evaluations: Incremental Merges in ClickHouse》。

在 Highlight 中,我们依赖于 ClickHouse,这是一个用于处理大量数据集和实时分析的开源列式数据库。它对于我们存储和查询时间序列数据(日志、跟踪、错误等)以及实现快速聚合和过滤至关重要。然而,使用较新的数据库也带来了自己的挑战(我们在这里对此进行了讨论)。下面我们来谈谈 ClickHouse 的一些特定功能如何帮助我们解决性能问题。

千集对文章内容进行了不改变原意的编译:

1面临的挑战

最近,在优化警报系统时遇到了一个问题:基于大时间窗口上的计算来确定是否触发警报变得极其缓慢。实时处理大量数据是不可能的,因为扫描整个数据集将会耗时太长且占用过多内存。以下是我们利用 ClickHouse 的聚合函数进行增量计算——计算并存储较小的中间结果,然后将这些结果合并——来解决这一问题的历程。

2问题所在

考虑一个用户希望在网络请求的 p99(第99百分位)持续时间超过一秒时触发警报。为了提供近实时反馈,我们会每分钟评估这些警报一次。如果采用简单的方法,则需要每分钟重新计算整个一小时窗口内的警报值,这意味着每小时为每个警报扫描相同的数据60次,导致巨大计算开销。

3简单的增量合并

对于像 Count 和 Sum 这样的简单聚合函数,我们意识到可以通过保存先前计算的结果进行优化。无需每次都从头开始重新计算,而是增量地计算并聚合中间结果。

例如,如果设置了一个日志警报,在一个小时内的日志数量超过100条时触发,我们可以每分钟计数并保存这段时间内的日志数量,然后加载并汇总过去60分钟的值以与阈值100进行比较。

下图展示了如何分步计算日志行数量的总和

4 总和聚合

但事情变得更复杂......

然而,对于更复杂的聚合函数,这种方法无效。举例来说,如果我们要计算跨越五个窗口的确切 p50(或中位数)值,则不能简单计算五个中间的 p50 值并通过某种方式“合并”结果;相反,我们必须存储每一个数据点来做计算。

ClickHouse 强大的 -State 和 -Merge 组合器却允许我们以类似“Count”或“Sum”的方式进行中间值计算。-State 函数返回中间状态,而 -Merge 函数则合并这些状态。

下图描述了围绕状态函数(记作“QS”表示分位数状态)与合并函数(记作“QM”表示分位数合并)的工作机制

4 总和聚合

Clickhouse 使用内存高效的近似算法来实现代理其许多复杂聚合函数。Highlight 使用的两个示例函数是 uniq 和 quantile。uniq 返回不同值的大约个数,quantile 用于近似计算 p50、p90 等。这些算法通过不同形式的采样获得代表值集合,并根据底层分布来取得近似值。

uniqState 和 quantileState 返回这些计算的底层状态表达。由于它们是具有有限最大容量的采样算法,因此使用内存量是有限的。在计算并保存这些状态之后,可以稍后加载并使用 uniqMerge 和 quantileMerge 函数。这些函数作为相对的,接收状态作为输入并返回结果值。比如 uniqMerge(uniqState(x)) = uniq(x)。优势在于 uniqState 可以为多块小输入数据运行且保存,随后将这些结果合并在一起。中间状态的大小远小于底层输入数据,因此更高效的做法是先做一次状态计算,然后再多次合并。

5 实施方案

我们的方法是每分钟加载所有新数据,针对这些新数据计算中间状态,然后用这些状态与现有状态合并以获取所需时间段内的聚合值。

我们的模式如下:

(  

    `MetricId` UUID, 

    `Timestamp` DateTime,

    `GroupByKey` String, 

    `MaxBlockNumberState` AggregateFunction(max,UInt64),

    `CountState` AggregateFunction(count, UInt64),  

    `UniqState` AggregateFunction(uniq, String), 

    `MinState` AggregateFunction(min, Float64), 

    `AvgState` AggregateFunction(avg, Float64), 

    `MaxState` AggregateFunction(max, Float64), 

    `SumState` AggregateFunction(sum, Float64), 

    `P50State` AggregateFunction(quantile(0.5), Float64),

    `P90State` AggregateFunction(quantile(0.9), Float64),

    `P95State` AggregateFunction(quantile(0.95), Float64),

    `P99State` AggregateFunction(quantile(0.99), Float64)

)

ENGINE=ReplicatedAggregatingMergeTree('/clickhouse/tables/{uuid}/{shard}', '{replica}')

ORDER BY (MetricId, Timestamp, GroupByKey)

由于每种状态列的类型不同,因此我们有对应支持的聚合函数的单独列。数据以分钟为粒度计算。我们通过检查基础表的所有部分处 max_block_number 大于上次运行的最大 max_block_number 来识别新数据。这也返回任何包含新数据的部分,但其中某些部分可能含有已经读取过的信息。为了避免重复计算这些数据,我们在基础表上启用了 allow_experimental_block_number_column,并在查询中过滤每一行的 _block_number。

6 结论

总体而言,利用 ClickHouse 的 -State 和 -Merge 函数组合大大提高了警报评估过程的表现。在实际工作环境中对日志警报评估进行测试时,我们将速度提升了10倍(从1.24秒提高到0.11秒),并且将内存使用量从7.6GB降低到82MB。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/Ohb2yxFi99xKs_qM9Mftxh5Q0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券