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

博客 / 工程

ClickHouse 23.8 版本

author avatar
ClickHouse 团队
2023 年 9 月 15 日

版本发布列车持续前进。

我们非常激动地宣布 23.8 版本中的大量精彩功能。

此外,23.9 版本的发布日期已经确定,请立即注册参加 9 月 28 日的社区电话会议,时间为太平洋时间上午 9:00 / 中欧时间下午 6:00。

版本概述

29 个新功能。19 项性能优化。63 个错误修复。

以下是部分亮点功能……此版本还涵盖了对向量的算术运算、元组的连接、集群/集群所有副本的默认参数、从元数据中计数(Parquet 速度提升 5 倍)、文件中的数据跳过(对 Parquet 的巨大影响)、从 S3 流式消费,以及……更多内容。

新贡献者

热烈欢迎所有 23.6 版本的新贡献者!ClickHouse 的流行在很大程度上归功于社区的努力。看到社区不断壮大,我们总是感到欣慰。

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

Al Korgun、Zamazan4ik (Alexander Zaitsev)、Andy Fiddaman、Artur Malchanau、Ash Vardanian、Austin Kothig、Bhavna Jindal、Bin Xie、Dani Pozo、Daniel Pozo Escalona、Daniël van Eeden、Davit Vardanyan、Filipp Ozinov、Hendrik M、Jai Jhala、Jianfei Hu、Jiang Yuqing、Jiyoung Yoo、Joe Lynch、Kenji Noguchi、Krisztián Szűcs、Lucas Fernando Cardoso Nunes、Maximilian Roos、Nikita Keba、Pengyuan Bian、Ruslan Mardugalliamov、Selfuppen、Serge Klochkov、Sergey Katkovskiy、Tanay Tummalapalli、VanDarkholme7、Yury Bogomolov、cfanbo、copperybean、daviddhc20120601、ekrasikov、gyfis、hendrik-m、irenjj、jiyoungyoooo、jsc0218、justindeguzman、kothiga、nikitakeba、selfuppen、xbthink、xiebin、Илья Коргун、王智博

文件、文件、更多文件

更快地读取文件 (Michael Kolupaev/Pavel Kruglov)

我们在最近的几个版本中重点介绍了我们改进 Parquet 性能的努力 [1][2]。虽然改进性能是一个永无止境的旅程,但 23.8 版本中的更改意义重大,因为它们使我们达到了对 Parquet 文件的当前读取性能感到更加满意的程度。更棒的是,其中一些改进也适用于其他文件类型,例如 JSON 和 CSV。

总而言之,ClickHouse 现在可以

  • 利用 Parquet 文件中的元数据跳过读取行组,该元数据捕获了列的数值范围。这个期待已久的功能带来了巨大的提速,正如我们将要展示的。
  • 利用元数据对大多数文件格式提供快速计数,包括 Parquet,方法是避免不必要的读取。
  • 允许在过滤子句中使用文件名元数据来避免读取文件。当应用于对象存储(如 S3)上的大型文件列表时,这可以避免大量的 I/O,并将查询从几秒钟缩短到几毫秒。此改进适用于所有文件类型。

为了说明这些新增功能带来的优势,我们将使用 PyPI 的数据集:Python 包索引。此数据集中的每一行都表示使用 pip 等工具下载 Python 包。我们提供了一个包含一天内所有包的下载量的 Parquet 文件。这大约包括 125.69GiB 的 Parquet 文件,分布在 1657 个文件,总共有 9 亿行。

所有以下测试都是在具有 64GiB 内存和 16 个内核的 GCE e2-highmem-8 实例上进行的,该实例位于 us-central-1 中的 GCS 存储桶本地。

在 23.8 版本之前,对这些文件进行计数可能很昂贵。将从每个文件下载、读取一个任意列,并计算总数。在本地 Macbook 上运行它

--23.7
SELECT count()
FROM s3('https://storage.googleapis.com/clickhouse_public_datasets/pypi/file_downloads/2023-07-31/*.parquet')

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

1 row in set. Elapsed: 26.488 sec. Processed 900.79 million rows, 134.94 GB (34.01 million rows/s., 5.09 GB/s.)

--23.8

SELECT count()
FROM s3('https://storage.googleapis.com/clickhouse_public_datasets/pypi/file_downloads/2023-07-31/*.parquet')

1 row in set. Elapsed: 11.431 sec. Processed 900.79 million rows, 56.22 KB (78.80 million rows/s., 4.92 KB/s.)
Peak memory usage: 44.43 MiB.

不错,性能提高了一倍多! 此优化也适用于其他文件类型,因此优势应该超越您的 Parquet 文件。

这些相同的文件具有一个时间戳列,用于表示下载发生的时间。使用 ParquetMetadata 格式,我们可以看到此列具有描述最小值和最大值的元数据。

SELECT tupleElement(row_groups[1], 'columns')[1] AS timestamp
FROM s3('https://storage.googleapis.com/clickhouse_public_datasets/pypi/file_downloads/2023-07-31/000000000000.parquet', ParquetMetadata)
FORMAT PrettyJSONEachRow
{
	"timestamp": {
    	"name": "timestamp",
    	"path": "timestamp",
    	"total_compressed_size": "681",
    	"total_uncompressed_size": "681",
    	"have_statistics": 1,
    	"statistics": {
        	"num_values": "406198",
        	"null_count": "0",
        	"distinct_count": null,
        	"min": "1690761600000000",
        	"max": "1690761650000000"
    	}
	}
}

从 23.8 版本开始,我们可以利用查询时的元数据来加速查询。假设我们希望确定选定日期的 30 分钟内的下载量。

--23.7
SELECT
	project,
	count() AS c
FROM s3('https://storage.googleapis.com/clickhouse_public_datasets/pypi/file_downloads/2023-07-31/*.parquet')
WHERE (timestamp >= '2023-07-31 15:30:00') AND (timestamp <= '2023-08-31 16:00:00')
GROUP BY project
ORDER BY c DESC
LIMIT 5

┌─project────────────┬───────c─┐
│ boto3          	│ 9378319 │
│ urllib3        	│ 5343716 │
│ requests       	│ 4749436 │
│ botocore       	│ 4618614 │
│ setuptools     	│ 4128870 │
└────────────────────┴─────────┘

5 rows in set. Elapsed: 83.644 sec. Processed 900.79 million rows, 134.94 GB (10.77 million rows/s., 1.61 GB/s.)

--23.8

SELECT
	project,
	count() AS c
FROM s3('https://storage.googleapis.com/clickhouse_public_datasets/pypi/file_downloads/2023-07-31/*.parquet')
WHERE (timestamp >= '2023-07-31 15:30:00') AND (timestamp <= '2023-08-31 16:00:00')
GROUP BY project
ORDER BY c DESC
LIMIT 5

5 rows in set. Elapsed: 34.993 sec. Processed 338.86 million rows, 51.17 GB (9.68 million rows/s., 1.46 GB/s.)
Peak memory usage: 95.61 MiB.

性能再次提高了一倍!

此特定改进也 立即影响了我们的 ClickBench 结果,如下所示。

clickbench_parquet.png

最后,Clickhouse 长期支持文件路径中的 glob 模式。虽然这对于定位文件子集非常完美,但它限制了用户对 glob 的表达能力。因此,ClickHouse 公开了 _file 虚拟列。此隐藏的元数据列包含行来源的文件名,并公开给用户。这可以在 SQL 查询中的任何地方使用,例如,计算 uniq 文件名的数量,或将读取的文件限制为子集。但是,在 23.7 及更早版本中,如果在查询的 SELECT 部分使用此列,则需要读取整个文件。如果用户只是想确定子集,则可能意味着读取所有文件。

这里性能改进的潜力可能很大,具体取决于子集相对于文件总数的大小。考虑以下示例,其中我们计算 10% 样本的唯一文件数量。

--23.7

SELECT uniq(_file)
FROM s3('https://storage.googleapis.com/clickhouse_public_datasets/pypi/file_downloads/2023-07-31/*.parquet')
WHERE (toUInt32(splitByChar('.', _file)[1]) % 10) = 0

┌─uniq(_file)─┐
│     	166 │
└─────────────┘

1 row in set. Elapsed: 4.572 sec. Processed 89.46 million rows, 13.41 GB (19.57 million rows/s., 2.93 GB/s.)

--23.8
SELECT uniq(_file)
FROM s3('https://storage.googleapis.com/clickhouse_public_datasets/pypi/file_downloads/2023-07-31/*.parquet')
WHERE (toUInt32(splitByChar('.', _file)[1]) % 10) = 0

┌─uniq(_file)─┐
│     	166 │
└─────────────┘

1 row in set. Elapsed: 0.632 sec. Processed 89.46 million rows, 0.00 B (141.65 million rows/s., 0.00 B/s.)
Peak memory usage: 1.71 MiB.

速度提高了 7 倍! 请记住,这种特定的优化适用于所有文件类型,而不仅仅是 Parquet。

如果需要完整位置,ClickHouse 还公开了一个 _path 虚拟列。

从 S3 流式消费 (Sergei Katkovskiy、Kseniia Sumarokova)

从 S3 读取数据的能力是 ClickHouse 的一项基本功能,用于执行临时数据分析,并且可能是将数据迁移到 MergeTree 表的最常用方式。历史上,后者的编排依赖于用户。虽然可以使用带有 s3 函数的 INSERT INTO SELECT 执行初始数据加载,但后续增量加载通常依赖于用户构建额外的流程。这些流程可以是数据管道,使用 Kafka 等技术,或者更简单地,使用 cron 作业安排 INSERT SELECT。后者非常普遍,但要确保其健壮性可能很微妙。

在 23.8 版本中,我们开始使用 S3Queue 表引擎,大幅简化来自 S3 的增量加载。这个新的表引擎允许从 S3 流式消费数据。当文件被添加到存储桶时,ClickHouse 会自动处理这些文件并将它们插入到指定的表中。有了这项功能,用户可以设置简单的增量管道,无需任何额外的代码。

请考虑以下简单示例。下面,我们有一个包含 PyPI 数据集的 Parquet 文件的 GCS 存储桶,其中每个小时由一个文件表示。

CREATE TABLE pypi_queue
ENGINE = S3Queue('https://storage.googleapis.com/clickhouse_public_datasets/pypi/file_downloads/2023/*.parquet')
SETTINGS mode = 'unordered'

CREATE TABLE pypi
ENGINE = MergeTree
ORDER BY (project, timestamp)
SETTINGS allow_nullable_key = 1 EMPTY AS
SELECT *
FROM pypi_queue

CREATE MATERIALIZED VIEW pypi_mv TO pypi AS
SELECT *
FROM pypi_queue

SELECT count()
FROM pypi

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

1 row in set. Elapsed: 0.003 sec.

熟悉 Kafka 表引擎的用户会认出下面提到的结构。我们在这里使用物化视图订阅 S3Queue 并将转换后的结果插入目标 pypi 表。

如果我们在存储桶中添加一个新文件,表就会更新。

S3Queue.gif

我们使用了一个 GCS 存储桶,它是一个与 S3 兼容的服务。此功能将在所有与 S3 兼容的服务上运行。

上面提到的 S3Queue 引擎通过定期轮询存储桶并跟踪存在的文件来工作,并将状态存储在 ClickHouse Keeper 节点中。默认情况下,每次轮询检索到的文件列表将与存储的列表进行比较,以识别新文件。下图显示了如何处理新文件(并将其添加到 Keeper)并将其插入到队列表中。虽然这不会存储数据本身,但物化视图可以订阅并转换行,然后再将它们插入到表中以进行查询。

s3_queue_image.png

这表示对默认的 unordered 行为的简化,该行为可以进行调整。例如,用户可以配置引擎在导入文件后将其删除。这样做的好处是,它可以将状态从 ClickHouse Keeper 中删除,这有助于确保跟踪的状态不会无限制地增长。为了防止状态的无限制存储,该引擎还会(默认情况下)仅跟踪有限数量的文件,同时也会设置 TTL。在文件 TTL 过期或文件数量超过当前限制后,文件将被重新导入。用户可以调整这些限制,但鼓励他们要么使用删除功能,要么使用 S3 功能手动使存储桶中的文件过期。

或者,可以配置引擎使用 ordered 模式使用文件的命名。在这种模式下,仅在 Keeper 中存储成功消费文件的最大名称。在每次轮询时,仅导入具有更高名称的文件。这需要在文件名中强制执行排序,但避免了上述许多复杂性。

我们期待在未来几个月内改进此功能,并希望我们能稍微简化您的架构。

直接从档案导入(Nikita Keba、Antonio Andelic、Pavel Kruglov)

作为我们最后的功能亮点,并且与文件主题保持一致,我们很高兴地宣布对档案的支持。ClickHouse 已经支持使用 zstdlz4snappygzxzbz2 等格式的压缩文件。在 23.8 之前,这些压缩文件只能包含一个文件。在 23.8 版本中,我们添加了对 zip、tar 和 7zip 的支持——它们都可以包含多个文件。

为了举例说明,我们 提供了一个 1.5GB 的 zip 档案,其中包含 24 个 CSV 文件(解压缩后为 45GB),表示一天的 PyPI 数据,包含 9 亿行。这些文件中的每一个都包含来自 PyPI 数据集的示例列:项目、版本和时间戳,并表示一个小时的数据。下面的查询使用虚拟列 _file 统计 zip 中每个文件的行数。请注意,我们使用 :: * 语法查询档案中的所有文件。

目前,仅通过 file 函数支持档案。因此,以下操作使用 clickhouse-local 执行。对于 S3 等函数的支持,敬请期待!

SELECT count(), _file
FROM file('2023-08-01.zip :: *.csv')
GROUP BY _file

┌──count()─┬─_file────────────────┐
│ 47251829 │ file_download_15.csv │
│ 43946206 │ file_download_17.csv │
│ 39082124 │ file_download_0.csv  │
│ 38928391 │ file_download_21.csv │
│ 34467371 │ file_download_1.csv  │
│ 44163805 │ file_download_12.csv │
│ 43229010 │ file_download_18.csv │
│ 41974421 │ file_download_10.csv │
│ 33003822 │ file_download_4.csv  │
│ 33331289 │ file_download_23.csv │
│ 34430684 │ file_download_5.csv  │
│ 40843622 │ file_download_11.csv │
│ 41122874 │ file_download_19.csv │
│ 37279028 │ file_download_6.csv  │
│ 36118825 │ file_download_22.csv │
│ 40800076 │ file_download_7.csv  │
│ 31962590 │ file_download_2.csv  │
│ 42055283 │ file_download_20.csv │
│ 30887864 │ file_download_3.csv  │
│ 45910953 │ file_download_13.csv │
│ 43467095 │ file_download_9.csv  │
│ 46705311 │ file_download_16.csv │
│ 42704388 │ file_download_8.csv  │
│ 48248862 │ file_download_14.csv │
└──────────┴──────────────────────┘

24 rows in set. Elapsed: 97.703 sec. Processed 961.10 million rows, 48.57 GB (9.84 million rows/s., 497.11 MB/s.)
Peak memory usage: 4.04 MiB.

我们可以使用相同的 :: 表示法来定位特定文件。

SELECT toStartOfMinute(timestamp) AS minute, count() AS c
FROM file('2023-08-01.zip :: file_download_9.csv')
WHERE project = 'requests'
GROUP BY project, minute
ORDER BY minute ASC

┌──────────────minute─┬─────c─┐
│ 2023-08-01 09:00:0010944 │
│ 2023-08-01 09:01:0011076 │
│ 2023-08-01 09:02:0013705 │
│ 2023-08-01 09:03:0012460 │
│ 2023-08-01 09:04:0011379 │
│ 2023-08-01 09:05:0013363 │
│ 2023-08-01 09:06:0011438 │
…
│ 2023-08-01 09:54:007972 │
│ 2023-08-01 09:55:008641 │
│ 2023-08-01 09:56:009696 │
│ 2023-08-01 09:57:008710 │
│ 2023-08-01 09:58:007495 │
│ 2023-08-01 09:59:007692 │
└─────────────────────┴───────┘

60 rows in set. Elapsed: 7.374 sec. Processed 42.97 million rows, 2.20 GB (5.83 million rows/s., 298.59 MB/s.)
Peak memory usage: 66.99 MiB.

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

分享这篇文章

订阅我们的时事通讯

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