DoubleCloud 即将停止服务。迁移到 ClickHouse,享受限时免费迁移服务。立即联系我们 ->->

博客 / 工程

ClickHouse 23.11 版本发布

author avatar
ClickHouse 团队
2023 年 12 月 21 日

我们非常高兴地分享 23.11 版本中的一系列令人惊叹的功能

版本摘要

25 个新功能。24 项性能优化。70 个错误修复。

下面是一些突出功能的示例……但此版本涵盖了使用任意类型进行连接的功能、fileCluster 函数、Keeper 改进、异步加载表、system.numbers 上的索引、并发控制机制、对 S3 请求的积极重试以及比以往更小的二进制文件大小!等等……更多

如果您想抢先了解本月即将推出的几款特别“礼物”,请加入即将于 12 月 28 日举行的12 月社区发布电话会议

新贡献者

一如既往,我们向 23.11 版本的所有新贡献者表示热烈的欢迎!ClickHouse 的流行在很大程度上归功于社区的贡献。看到社区不断壮大,我们总是感到谦卑。

如果您在这里看到您的名字,请与我们联系……但我们也会在 Twitter 等平台上找到您。

Andrej Hoos、Arvind Pj、Chuan-Zheng Lee、James Seymour、Kevin Mingtarja、Oleg V. Kozlyuk、Philip Hallstrom、Sergey Kviatkevich、Shri Bodas、abakhmetev、edef、joelynch、johnnymatthews、konruvikt、melvynator、pppeace、rondo_1895、ruslandoga、slu、takakawa、tomtana、xleoken、袁焊忠

S3Queue 已准备好投入生产

由 Sergei Katkovskiy 和 Kseniia Sumarokova 贡献

在 23.8 版本中,我们宣布了S3Queue 表引擎的实验性版本,以大幅简化来自 S3 的增量加载。此新表引擎允许从 S3 流式传输数据。当文件添加到存储桶时,ClickHouse 将自动处理这些文件并将它们插入到指定的表中。通过此功能,用户可以设置简单的增量管道,无需任何其他代码。

我们很高兴地宣布,自实验性版本发布以来,此功能已得到显著改进,并且现在已准备好投入生产!为了庆祝这一时刻,我们的YouTube 名人马克制作了一段视频

PREWHERE 的列统计信息

由韩飞贡献

列统计信息是一项新的实验性功能,可以更好地优化 ClickHouse 中的查询。使用此功能,您可以让 ClickHouse 为使用 MergeTree 系列引擎的表中的列创建(并自动更新)统计信息。这些统计信息存储在表的分区内的一个小的单个statistics_(column_name).stat文件中,该文件是每个列的不同类型统计信息的通用容器文件,这些列启用了统计信息。这确保了对列统计信息的轻量级访问。截至今天,唯一支持的列统计信息类型是t-digest。其他类型已计划,不过。

列统计信息可以更好地进行优化的第一个示例是多阶段PREWHERE过滤中的列处理顺序。我们用一个图来描述这一点

column_stats.png

图的左上角的查询有一个WHERE子句,该子句由多个用AND连接的过滤器条件组成。ClickHouse 有一种优化方法,可以尝试评估扫描数据量最少的过滤器。这种优化称为多阶段 PREWHERE,它基于这样一个想法,即我们可以按顺序读取过滤器列,即逐列读取,并且在每次迭代中,仅检查包含至少一行“存活”(=匹配)先前过滤器的块。每个过滤器要评估的块数单调递减。

毫不奇怪,当首先评估产生最少存活块的过滤器时,这种优化效果最佳 - 在这种情况下,ClickHouse 只需要扫描几个块来评估剩余的过滤器。当然,不可能知道每个过滤器有多少个包含匹配行的块存活下来,因此 ClickHouse 需要进行猜测以确定执行过滤器的最佳顺序。借助列统计信息,ClickHouse 能够更精确地估计匹配行/存活块的数量,因此,多阶段 PREWHERE 作为一种优化变得更加有效。

在这个例子中,ClickHouse 利用列统计信息自动确定列c2上的过滤器条件是最有选择性的,即它删除了最多的块。因此,处理从c2开始。扫描来自列c2的所有块,并对每一行评估过滤器谓词。接下来,对列c3执行过滤器评估,但仅对包含在c2的相应块中至少有一次匹配c2过滤器的行的块执行。因为列c1上的过滤器条件选择性最低,所以此列的块最后处理。同样,仅扫描(并对每一行评估过滤器谓词)那些来自c2c3的相应块都具有谓词匹配的块。对于查询运行需要扫描和处理的所有其他列,ClickHouse 只需要从磁盘扫描那些所有相应的 PREWHERE 列都具有谓词匹配的块。

让我们用一个具体的例子来演示这一点。

我们创建一个示例表并插入 1000 万行

CREATE OR REPLACE TABLE example
(
    `a` Float64,
    `b` Int64,
    `c` Decimal64(4),
    `pk` String
)
ENGINE = MergeTree
ORDER BY pk;

INSERT INTO example SELECT
    number,
    number,
    number,
    generateUUIDv4()
FROM system.numbers
LIMIT 10_000_000

接下来,我们运行一个查询,该查询在WHERE子句中包含多个用 AND 连接的过滤器条件。请注意,我们禁用了PREWHERE优化

SELECT count()
FROM example
WHERE b < 10 AND a < 10 AND c < 10
SETTINGS optimize_move_to_prewhere = 0

┌─count()─┐
│      10 │
└─────────┘

1 row in set. Elapsed: 0.057 sec. Processed 10.00 million rows, 240.00 MB (176.00 million rows/s., 4.22 GB/s.)
Peak memory usage: 162.42 KiB.

我们可以看到该查询处理了240 MB的列数据。

现在,我们启用(多阶段)PREWHERE 并运行相同的查询

SELECT count()
FROM example
WHERE b < 10 AND a < 10 AND c < 10

┌─count()─┐
│      10 │
└─────────┘

1 row in set. Elapsed: 0.032 sec. Processed 10.00 million rows, 160.42 MB (308.66 million rows/s., 4.95 GB/s.)
Peak memory usage: 171.74 KiB.

这次,该查询处理了160 MB的列数据。

接下来,我们启用列统计信息功能,并为我们表的三个列启用并物化基于 t-digest 的统计信息

SET allow_experimental_statistic = 1;
ALTER TABLE example ADD STATISTIC a, b, c TYPE tdigest;
ALTER TABLE example MATERIALIZE STATISTIC a, b, c TYPE tdigest;

运行启用了列统计信息优化的示例查询

SELECT count()
FROM example
WHERE b < 10 AND a < 10 AND c < 10
SETTINGS allow_statistic_optimize = 1

┌─count()─┐
│      10 │
└─────────┘

1 row in set. Elapsed: 0.012 sec. Processed 10.00 million rows, 80.85 MB (848.47 million rows/s., 6.86 GB/s.)
Peak memory usage: 160.25 KiB.

该查询处理了80 MB的列数据。

但这仅仅是一个开始。列统计信息还将用于其他有影响力的优化,例如连接重新排序或使低基数数据类型成为自动决策。

敬请期待!

并行窗口函数

由 Dmitriy Novik 贡献

任何使用 SQL 进行过严肃数据分析的人都会理解窗口函数的价值。窗口函数自 21.5 版本起在 ClickHouse 中可用。PostgreSQL 的文档很好地总结了这种 SQL 功能

窗口函数对一组与当前行相关的表行执行计算。这类似于聚合函数可以执行的计算类型。但与常规聚合函数不同,使用窗口函数不会导致行被分组为单个输出行——行保留其单独的身份。窗口函数能够访问查询结果的不仅仅是当前行。

虽然窗口函数可以应用于一些非常复杂的问题,但大多数用户会在需要执行简单操作时遇到它们,例如移动平均线(需要考虑多行)或累积和。由于这些特定的查询通常在 Grafana 等流行工具中可视化,因此我们总是很高兴宣布其性能得到显著提升。在 23.11 版本中,ClickHouse 在其窗口函数的实现上取得了巨大进步,确保其执行可以并行化。

这种并行化是通过利用窗口函数固有的分桶功能:分区来实现的。当用户指定窗口函数应按某个列进行分区时,实际上会为每个分区创建一个单独的逻辑窗口,即如果该列包含 N 个不同的值,则需要创建 N 个窗口。在 23.11 版本中,这些分区可以有效地并行构建和评估。

例如,考虑以下使用 NOAA 天气数据集的查询。

CREATE TABLE noaa
(
	`station_id` LowCardinality(String),
	`date` Date32,
	`tempAvg` Int32 COMMENT 'Average temperature (tenths of a degrees C)',
	`tempMax` Int32 COMMENT 'Maximum temperature (tenths of degrees C)',
	`tempMin` Int32 COMMENT 'Minimum temperature (tenths of degrees C)',
	`precipitation` UInt32 COMMENT 'Precipitation (tenths of mm)',
	`snowfall` UInt32 COMMENT 'Snowfall (mm)',
	`snowDepth` UInt32 COMMENT 'Snow depth (mm)',
	`percentDailySun` UInt8 COMMENT 'Daily percent of possible sunshine (percent)',
	`averageWindSpeed` UInt32 COMMENT 'Average daily wind speed (tenths of meters per second)',
	`maxWindSpeed` UInt32 COMMENT 'Peak gust wind speed (tenths of meters per second)',
	`weatherType` Enum8('Normal' = 0, 'Fog' = 1, 'Heavy Fog' = 2, 'Thunder' = 3, 'Small Hail' = 4, 'Hail' = 5, 'Glaze' = 6, 'Dust/Ash' = 7, 'Smoke/Haze' = 8, 'Blowing/Drifting Snow' = 9, 'Tornado' = 10, 'High Winds' = 11, 'Blowing Spray' = 12, 'Mist' = 13, 'Drizzle' = 14, 'Freezing Drizzle' = 15, 'Rain' = 16, 'Freezing Rain' = 17, 'Snow' = 18, 'Unknown Precipitation' = 19, 'Ground Fog' = 21, 'Freezing Fog' = 22),
	`location` Point,
	`elevation` Float32,
	`name` LowCardinality(String),
	`country` LowCardinality(String)
)
ENGINE = MergeTree
ORDER BY (country, date)


INSERT INTO noaa SELECT *
FROM s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/noaa/noaa_with_country.parquet')

这里可以使用一个简单的窗口函数来计算每天和每个国家/地区的温度移动平均值。这要求我们**按国家/地区进行分区**(数据集中有 214 个国家/地区)并按日期排序。在计算移动平均值时,我们考虑最后 5 个数据点。

SELECT
	country,
	day,
	max(tempAvg) AS temperature,
	avg(temperature) OVER (PARTITION BY country ORDER BY day ASC ROWS BETWEEN 5 PRECEDING AND CURRENT ROW) AS moving_avg_temp
FROM noaa
WHERE country != ''
GROUP BY
	country,
	date AS day
ORDER BY
	country ASC,
	day ASC

此简单函数的意图最好通过简单的可视化来解释。

window_function.png

在 23.11 版本之前,ClickHouse 基本上会并行执行此函数——窗口函数除外。在查询不受其他因素(例如 I/O)限制的情况下,这可能会限制性能。

在 23.10 版本中,在一台拥有 12 个内核和 96GiB 内存的机器上,此查询大约需要 8.8 秒才能处理完 10 亿行数据。

SELECT
	country,
	day,
	max(tempAvg) AS avg_temp,
	avg(avg_temp) OVER (PARTITION BY country ORDER BY day ASC ROWS BETWEEN 5 PRECEDING AND CURRENT ROW) AS moving_avg_temp
FROM noaa
WHERE country != ''
GROUP BY
	country,
	date AS day
ORDER BY
	country ASC,
	day ASC
LIMIT 10

┌─country─────┬────────day─┬─avg_temp─┬─────moving_avg_temp─┐
│ Afghanistan │ 1900-01-01-81-81 │
│ Afghanistan │ 1900-01-02-145-113 │
│ Afghanistan │ 1900-01-03-139-121.66666666666667 │
│ Afghanistan │ 1900-01-04-107-118 │
│ Afghanistan │ 1900-01-05-44-103.2 │
│ Afghanistan │ 1900-01-060-86 │
│ Afghanistan │ 1900-01-07-71-84.33333333333333 │
│ Afghanistan │ 1900-01-08-85-74.33333333333333 │
│ Afghanistan │ 1900-01-09-114-70.16666666666667 │
│ Afghanistan │ 1900-01-10-71-64.16666666666667 │
└─────────────┴────────────┴──────────┴─────────────────────┘

10 rows in set. Elapsed: 8.515 sec. Processed 1.05 billion rows, 7.02 GB (123.61 million rows/s., 824.61 MB/s.)
Peak memory usage: 1.13 GiB.

从 23.11 版本开始,通过并行执行每个分区来提高性能。同样,这最好用简单的插图来描述。

parallell_window_function.png

您在此处获得的实际收益取决于许多因素——最重要的是要有足够的分区和每个分区的处理量,以便并行化能够提供显著的改进。它还假设查询不受其他因素的限制。在下面的示例中,我们获得了超过 10% 的改进,而无需做任何事情!

SELECT
	country,
	day,
	max(tempAvg) AS avg_temp,
	avg(avg_temp) OVER (PARTITION BY country ORDER BY day ASC ROWS BETWEEN 5 PRECEDING AND CURRENT ROW) AS moving_avg_temp
FROM noaa
WHERE country != ''
GROUP BY
	country,
	date AS day
ORDER BY
	country ASC,
	day ASC
LIMIT 10

┌─country─────┬────────day─┬─avg_temp─┬─────moving_avg_temp─┐
│ Afghanistan │ 1900-01-01-81-81 │
│ Afghanistan │ 1900-01-02-145-113 │
│ Afghanistan │ 1900-01-03-139-121.66666666666667 │
│ Afghanistan │ 1900-01-04-107-118 │
│ Afghanistan │ 1900-01-05-44-103.2 │
│ Afghanistan │ 1900-01-060-86 │
│ Afghanistan │ 1900-01-07-71-84.33333333333333 │
│ Afghanistan │ 1900-01-08-85-74.33333333333333 │
│ Afghanistan │ 1900-01-09-114-70.16666666666667 │
│ Afghanistan │ 1900-01-10-71-64.16666666666667 │
└─────────────┴────────────┴──────────┴─────────────────────┘

10 rows in set. Elapsed: 7.571 sec. Processed 1.05 billion rows, 7.02 GB (139.03 million rows/s., 927.47 MB/s.)
Peak memory usage: 1.13 GiB.

立即开始使用 ClickHouse Cloud 并获得 300 美元的信用额度。在 30 天试用期结束时,您可以继续使用按需付费计划,或者联系我们 了解有关我们基于用量的折扣的更多信息。请访问我们的定价页面 获取详细信息。

分享此文章

订阅我们的时事通讯

随时了解功能发布、产品路线图、支持和云产品信息!
加载表单...
关注我们
Twitter imageSlack imageGitHub image
Telegram imageMeetup imageRss image