关于数据库:clickhouse聚合之探索聚合内部机制

2次阅读

共计 4495 个字符,预计需要花费 12 分钟才能阅读完成。

大家好,我是奇想派,能够叫我奇奇。

将数据进行聚合是剖析数据库的一项要害性能。咱们无奈从数十亿次的访问量列表数据列表中得出太多论断。然而,咱们能够很容易地了解,本月的均匀访问量从上个月日活 100 到这个有日活 1000。在 ClickHouse 中,咱们将此类汇总称为 聚合,这是从大型数据集中把握意义的根本办法。

在本博客系列中,咱们将探讨聚合在 ClickHouse 中的工作原理、如何掂量其性能以及如何使其更快、更高效。为了便于浏览,咱们将应用简略的示例,但它们显示的准则实用于更简单的查问。

试验前提:

  • ClickHouse 版本:22.1.3.7
  • 操作系统:centos7.8

咱们将应用随机生成的飞机起航的数据集,来演示 ClickHouse 如何在外部解决聚合。上面是一个查问,用于查找航空公司的均匀腾飞延误。它按降序对后果进行排序,并返回均匀延迟时间最长的三家航空公司:

聚合查问外部是如何工作的?

让咱们应用一个简略的示例来演示 ClickHouse 如何在外部解决聚合。上面是一个查问,用于查找美国航空公司的均匀腾飞延误。它按降序对后果进行排序,并返回均匀延迟时间最长的三家航空公司:

SELECT Carrier, avg(DepDelay) AS Delay
FROM ontime
GROUP BY Carrier 
ORDER BY Delay DESC LIMIT 3
. . .
┌─Carrier─┬──────────────Delay─┐
│ B6      │ 12.058290698785067 │
│ EV      │ 12.035012037703922 │
│ NK      │ 10.437692933474269 │
└─────────┴────────────────────┘

在我的单个云主机上,此查问将在大概 0.75 秒后返回。它读取残缺的数据集,即 197M 行来取得这个答案,这十分快。那么,ClickHouse 外部产生了什么?下图阐明了查询处理流。

让咱们重点介绍解决扫描数据时产生的聚合的两个重要特色。

  1. 查询处理是并行化的。ClickHouse 调配了多个线程(在本例中为四个线程),这些线程独立读取其中的不同表局部和数据块。它们读取数据并动静执行初始聚合。
  2. 聚合在哈希表中累积。每个 GROUP BY 值都有一个键。

扫描完结后,最初一步是合并所有聚合并对其进行排序。这就引出了下一个问题。ClickHouse 计算如何并行聚合?下图显示其工作原理。

这就引出了最初一个问题。ClickHouse 如何在合并之前收集和存储局部聚合?正如咱们刚刚提到的,答案是哈希表,其中键对应于值,局部聚合存储为与每个键关联的列表。如下图。

ClickHouse 聚合实际上比上图所示的更简单(也更快)。

首先,ClickHouse 依据密钥的数量和类型抉择不同的聚合办法以及不同的哈希表配置,数据结构和收集办法因数据而异。此专用化可确保聚合办法思考不同数据类型中的性能差别。GROUP BY

其次,ClickHouse 聚合是 动静的。ClickHouse 从单级哈希表开始,对于大量密钥,这些哈希表的速度更快。随着存储密钥数量的减少,ClickHouse 能够主动切换到运行速度更快的两级哈希表。ClickHouse 实际上适应了理论数据。它十分快,没有大量的查问布局开销。

顺便说一句,将聚合排列到按键组织的队列中,而后合并队列内容可能听起来很相熟。你可能据说过 MapReduce。这是 Jeff Dean 和 Sanjay Ghemawat 的一篇驰名论文的主题,为 Hadoop 的倒退做出了奉献。数据仓库实际上在 Hadoop 宽泛遍及之前应用这种技术曾经很多年了。

应用掂量聚合性能的工具

当初咱们曾经钻研了聚合的工作原理,很显著,有一些与聚合相干的乏味问题。咱们须要工具来理解查问速度、应用的内存和其余特色。咱们还须要一种办法来窥视查询处理的掩护,以认真查看聚合详细信息。

应用 system.query_log

好消息是,system.query_log 表恰好蕴含评估聚合性能所需的信息。默认状况下,当您装置 ClickHouse 时,它将处于启用状态。上面是一个简略的查问,用于找出示例 SQL 所需的工夫和内存,该示例在列表中显示为第三位。

SELECT
    event_time, query_duration_ms / 1000 AS secs,
    formatReadableSize(memory_usage) AS memory,
    Settings['max_threads'] AS threads,
    substring(query, 1, 20) AS query
FROM system.query_log AS ql
WHERE (user = 'default') AND (type = 'QueryFinish')
ORDER BY event_time DESC
LIMIT 50

Query id: 94c00711-31cb-4280-8270-717ccf942748

┌──────────event_time─┬──secs─┬─memory───┬─threads─┬─query────────────────┐
│ 2022-03-14 13:30:03 │ 0.007 │ 0.00 B   │ 4       │ select event_time, q │
│ 2022-03-14 13:23:52 │ 0.032 │ 0.00 B   │ 4       │ select event_time, q │
│ 2022-03-14 13:23:40 │ 2.023 │ 0.00 B   │ 4       │ SELECT Carrier, avg( │
│ 2022-03-14 13:23:20 │ 0.002 │ 0.00 B   │ 4       │ SELECT message FROM  │
│ 2022-03-14 13:23:20 │ 0.061 │ 4.24 MiB │ 4       │ SELECT DISTINCT arra │
. . . 

system.query_log 表十分通用,但咱们将重点介绍几个要害列。首先,每个查问记录多个事件。事件名称位于 类型 列中。咱们将重点介绍 QueryFinish 事件类型,因为它显示了查问的残缺统计信息。

其次,query_duration_ms 列和 memory_usage 列别离显示查问的持续时间和内存应用状况。它们很容易解释,但您可能会诧异地发现许多查问仿佛应用 0 字节的 RAM。这显然是不可能的。

正在产生的事件很简略。ClickHouse 会疏忽低于 max_untracked_memory 值(默认值为 4,194,304)的线程内存值。咱们能够通过将值设置为低值(如 1 字节)来找出用于查问的内存。上面是一个示例。

SET max_untracked_memory = 1

SELECT Carrier, avg(DepDelay) AS Delay
FROM ontime
GROUP BY Carrier 
ORDER BY Delay DESC LIMIT 3

SELECT
    event_time, query_duration_ms / 1000 AS secs,
    formatReadableSize(memory_usage) AS memory,
    Settings['max_threads'] AS threads,
    substring(query, 1, 20) AS query
FROM system.query_log
WHERE (user = 'default') AND (type = 'QueryFinish')
ORDER BY event_time DESC
LIMIT 50

┌──────────event_time─┬──secs─┬─memory───┬─threads─┬─query────────────────┐
│ 2022-03-14 13:35:09 │  0.85 │ 8.40 MiB │ 4       │ SELECT Carrier, avg( │
. . .

平均值应用的内存绝对较少,因为聚合很简略。更简单的聚合应用更多的内存,因而在这些状况下,您无需调整 max_untracked_memory 即可理解正在应用的内存。

system.query_log 中还有许多其余有用的列。然而,下面显示的简略查问曾经提供了丰盛的信息,使咱们可能掂量聚合解决中的衡量。在空闲时查看其他人。

为查问启用消息日志

另一个重要的工具是 ClickHouse 服务器消息日志。您能够间接查看消息日志,将它们定向到 system.text_log 表,或者应用 send_message_logs 属性在 clickhouse-client 中启用它们。上面介绍如何启用跟踪音讯以显示查询处理的血腥详细信息,以及几条迷人的音讯以显示您能够学习的内容。

SET send_logs_level = 'trace'

SELECT Carrier, FlightDate, avg(DepDelay) AS Delay
FROM ontime
GROUP BY Carrier, FlightDate
ORDER BY Delay DESC LIMIT 3
[chi-clickhouse101-clickhouse101-0-0-0] 2022.03.16 22:23:39.802817 [485] {287891a8-d444-4d43-a96a-ca7aa5cd6ff5} <Debug> executeQuery: (from xx.xxx.xx.xx:43802, user: default) -- #2
 SELECT Carrier, FlightDate, avg(DepDelay) AS Delay FROM ontime GROUP BY Carrier, FlightDate ORDER BY Delay DESC LIMIT 3 ;
. . . 
[chi-clickhouse101-clickhouse101-0-0-0] 2022.03.16 22:23:39.810727 [282] {287891a8-d444-4d43-a96a-ca7aa5cd6ff5} <Trace> Aggregator: Aggregation method: keys32
. . .
[chi-clickhouse101-clickhouse101-0-0-0] 2022.03.16 22:23:40.530650 [282] {287891a8-d444-4d43-a96a-ca7aa5cd6ff5} <Debug> AggregatingTransform: Aggregated. 50160884 to 42533 rows (from 382.70 MiB) in 0.721708392 sec. (69502980.090 rows/sec., 530.28 MiB/sec.)

调试级别输入足以查看线程上的计时。如果你想看到像抉择的聚合办法这样的货色(例如,如上所示的keys32),trace 就是你的敌人。它为大型查问生成了惊人的输出量。

论断

在本文章中,咱们查看了 ClickHouse 聚合内部结构,也理解了聚合的工作原理。前面还介绍了跟踪聚合行为的两个根本工具:system.query_log 表和 ClickHouse 调试和跟踪日志。

在下一篇文章《clickhouse 聚合之内存不足怎么办?那就晋升聚合性能》,咱们重点说下如何给聚合进步性能。

更多内容请关注微信公众号【编程达人】,分享优质好文章,编程黑科技,助你成为编程达人!

正文完
 0