博客 / 工程

ClickHouse 22.12 版本发布

author avatar
ClickHouse 团队
2022年12月19日 - 11 分钟阅读

这是一个节日惊喜。

团队交付了 11 个月的常规版本还不够。ClickHouse Cloud 的 Early Access、Beta 和 GA 也不够。说到这,如果您想以最简单的方式在生产环境(或开发环境)中运行 ClickHouse,请立即开始 ClickHouse Cloud 的免费试用。

作为节日礼物,我们很高兴推出 22.12 版本。

版本摘要

17 项新功能。8 项性能优化。39 个错误修复。

如果这还不足以引起您尝试的兴趣。请查看一些重点项目

  • grace_hash JOIN
  • 密码复杂度规则
  • BSON 支持
  • GROUP BY ALL 支持
  • 为 ClickHouse Keeper 添加 Prometheus 端点

当然,还有许多性能改进。

GraceHopper哈希连接 (Sergei Skvortsov + Vladimir Cherkasov)

历史上,在 ClickHouse 中,用户在连接方面有几种选择:要么使用 hash 方法,这种方法速度快但受内存限制,要么恢复使用 partial_merge 算法。后者依赖于对数据进行排序并将其转储到磁盘,通常以牺牲性能为代价来克服内存限制。虽然这至少允许用户执行大型连接,但它通常会受到性能缓慢的影响。在此版本中,我们为连接算法引入了一个令人兴奋的非内存限制的补充,它克服了 partial merge 的一些性能挑战:Grace Hash。

Grace Hash 算法 利用两阶段方法来连接数据。我们的实现与 经典算法描述略有不同,以适应我们的查询管道。

我们的第一阶段读取右表,并根据键列的哈希值将其拆分为 N 个桶(最初,N 为 grace_hash_join_initial_buckets)。这样做是为了确保每个桶都可以独立处理。来自第一个桶的行被添加到内存哈希表中,而其他行则保存到磁盘。如果哈希表增长超过内存限制(例如,由 max_bytes_in_join 设置),我们将增加桶的数量并重新计算每行分配的桶。任何不属于当前桶的行都会被刷新并重新分配。

right-side-grace.png

然后读取左表。与第一个桶对应的行被连接(因为哈希表在内存中),其他行被刷新到它们相应的基于磁盘的桶中。这两个步骤的关键在于哈希函数将始终如一地将值分配给同一个桶,从而有效地对数据进行分区,并通过分解解决问题。

left-side-grace.png

在我们完成读取左表后,我们必须处理磁盘上剩余的桶。这些桶是按顺序处理的。我们从右表数据中为每个桶构建哈希表。同样,如果我们耗尽内存,我们必须增加桶的数量。一旦从右桶构建了哈希表,我们就会流式传输左桶并完成此对的连接。请注意,在此步骤中,我们可能会获得一些属于当前桶以外的其他桶的行,因为它们是在桶的数量增加之前保存的。在这种情况下,我们将它们保存到新的实际桶中并进一步处理它们。对于所有剩余的桶,此过程都会重复进行。

grace-final-step.png

这种分区数据的方法确保了我们既可以限制内存,又可以限制我们需要扫描每个表的次数。表的两侧都被扫描两次 - 一次用于分区数据,另一次用于连接阶段。虽然不如哈希连接快,但与 partial_merge 相比,性能优势可能非常明显。

现在让我们来看一个实际的例子。

在我们的 play.clickhouse.com 中,我们在 hits 表中维护一个匿名化的 Web 分析数据集。此表中的每一行都代表一次网站点击,记录了诸如浏览器代理和 URL 之类的详细信息。数据集还包括一个 Referer 列(也是一个 URL),指示访问者到达每个 URL 的先前位置。假设我们识别出在 URL 之前访问过的先前两个页面,而不仅仅是直接引荐来源网址。我们可以通过按时间排序来解决这个问题,但让我们故意使其更棘手,并使用图分析来解决它。这要求我们有效地执行深度为 2 的图导航,如下所示

url-self-join.png

这可以在 SQL 中实现为自连接,我们在其中通过查找共享共同 URL 和 Referer 值的行对,将 hits 表自身连接起来。

SELECT UserID, h1.Referer, h1.URL, h2.URL FROM hits AS h1 INNER JOIN hits AS h2 ON h1.UserID = h2.UserID AND h1.URL = h2.Referer WHERE h1.URL != '' AND h2.URL != '' AND h1.Referer != '' AND h2.Referer != '' ORDER BY UserID ASC LIMIT 10

虽然此查询的结果并非特别有趣,但我们注意到以下 hashparallel_hashpartial_mergegrace_join 算法的性能和内存开销。

hash-algorithms.png hash-join-results.png

注意:parallel_hash 是哈希连接的一种变体,它将数据拆分为桶并同时构建多个哈希表,而不是一个,以加速连接,但代价是更高的内存开销。

此处的结果非常清楚。与 partial_merge 相比,Grace 提供了更高的性能,但代价是更高的内存开销。虽然 hash,尤其是 parallel_hash,速度明显更快,但您需要付出开销并依赖于拥有足够的内存。这里的选择取决于用户。请注意,这些结果会因您的数据和查询而异。

请注意,此处的 Grace Hash 测试是在设置 grace_hash_join_initial_buckets=128 的情况下运行的。为 grace_hash_join_initial_buckets 选择“正确”值的主要好处是它可以避免我们之前描述的重新分桶 - 从而可能加快 JOIN 速度。目前,这留给用户手动调整。

强制密码复杂度规则 (Nikolay Degterinsky)

ClickHouse 认为安全性是头等大事。在 22.12 之前,用户可以创建密码,而无需强制执行复杂度。虽然我们相信我们的用户会负责任,但错误和疏忽是会发生的,并且可能会创建弱密码。我们需要为我们自己的 ClickHouse Cloud 需求解决这个问题,这也是我们社区需要的。

可以通过在服务器配置中添加 password_complexity 配置键来设置密码强制执行。以下是强制执行强标准的 4 条规则的示例

$ cat /etc/clickhouse-server/config.d/rules.yaml password_complexity: - rule: pattern: '.{12}' message: 'be at least 12 characters long' - rule: pattern: '\p{N}' message: contain at least 1 numeric character - rule: pattern: '\p{Lu}' message: contain at least 1 uppercase character - rule: pattern: '[^\p{L}\p{N}]' message: contain at least 1 special character

现在,尝试违反此策略会导致错误,例如,

clickhouse-cloud:) CREATE USER vasyan IDENTIFIED WITH sha256_password BY 'qwerty123' DB::Exception: Invalid password. The password should: be at least 12 characters long, contain at least 1 uppercase character, contain at least 1 special character.

我们始终建议用户使用 clickhouse-client 创建用户和设置密码。除了确认密码在客户端满足规则外,它还可以确保纯文本密码永远不会传输到服务器。

BSON 支持 (Pavel Kruglov, Mark Polokhov)

BSON(二进制 Javascript 对象表示法)是一种二进制编码的 Javascript 对象表示法 (JSON) 格式,用于 MongoDB 中的数据存储和网络传输。虽然基于 JSON,但它有一些优点:具体来说,它支持日期和二进制数据等其他类型,并且由于长度和数组索引被编码到格式中,因此构建和扫描速度更快。我们希望大多数用户在与 mongodump 工具创建的转储交互时会发现这很有用。

Mongo 示例数据集提供了一些有用的示例来测试此功能。假设您已将这些数据集 加载到您的 Mongo 或 Atlas 实例中,则使用 ClickHouse 导出和查询将非常简单。下面我们使用 clickhouse-local 并查询 movies 数据集中的 comments。

mongodump --uri="/sample_mflix" --username=default --db=sample_mflix --collection=comments --out=comments clickhouse-local SELECT count() FROM file('comments/sample_mflix/comments.bson', BSONEachRow) ┌─count()─┐ │ 41079 │ └─────────┘ 1 row in set. Elapsed: 0.236 sec. Processed 36.86 thousand rows, 21.18 MB (156.37 thousand rows/s., 89.84 MB/s.) SELECT name, text FROM file('comments/sample_mflix/comments.bson', BSONEachRow) LIMIT 1 FORMAT Vertical Query id: 7d522673-f124-4598-bdff-86b12f2905d1 Row 1: ────── name: Mercedes Tyler text: Eius veritatis vero facilis quaerat fuga temporibus. Praesentium expedita sequi repellat id. Corporis minima enim ex. Provident fugit nisi dignissimos nulla nam ipsum aliquam. 1 row in set. Elapsed: 0.048 sec.

ClickHouse Keeper - Prometheus 端点 (Antonio Andelic)

我们认为 ClickHouse Keeper 在一段时间内已准备好用于生产环境,并鼓励所有用户在可能的情况下从 Zookeeper 迁移。然而,对于某些用户来说,使用与其旧版 Zookeeper 实例相同的方法在其部署中监视 ClickHouse Keeper 的能力是迁移的障碍。除了在此版本中提高了高请求速率下的写入性能外,我们还为 ClickHouse Keeper 添加了一个 Prometheus 端点,以允许监视 ClickHouse 集群中这个关键的软件组件。希望这可以消除一些迁移障碍,并且更多用户可以从更稳定的负载下集群协调中受益。

$ cat /etc/clickhouse-keeper/config.d/prometheus.yaml prometheus: port: 9369 endpoint: /metrics

GROUP BY ALL (TaoFengLiu)

来自 Postgres 等 OLTP 数据库的 ClickHouse 新用户很快发现 ClickHouse 在某些方面与 ANSI SQL 不同。这通常是故意的,因为我们认为这些差异使分析查询更简单、更简洁。然而,在少数情况下,我们只存在一些我们渴望弥合的功能差距。其中之一是能够在 GROUP BY 中使用 ALL 子句。这个简单的功能意味着用户无需重复 SELECT 子句中的列(这些列不是聚合函数),从而使查询更短、更快地编写。由于我们热爱速度,因此您现在可以在 22.12 中利用此功能,这要归功于 社区的贡献

SELECT county, town, district, street, median(price) AS med_price, count() AS c FROM uk_price_paid WHERE toYear(date) = 2022 GROUP BY county, town, district, street ORDER BY count() DESC LIMIT 10 // and even simpler with ALL SELECT county, town, district, street, median(price) AS med_price, count() AS c FROM uk_price_paid WHERE toYear(date) = 2022 GROUP BY ALL ORDER BY count() DESC LIMIT 10

分享此文章

订阅我们的新闻通讯

随时了解功能发布、产品路线图、支持和云产品信息!
正在加载表单...
关注我们
X imageSlack imageGitHub image
Telegram imageMeetup imageRss image
©2025ClickHouse, Inc. 总部位于美国加州湾区和荷兰阿姆斯特丹。