博客 / 工程

ClickHouse Release 23.9

author avatar
ClickHouse 团队
2023 年 10 月 24 日 - 16 分钟阅读

我们非常激动地分享 23.9 版本中大量令人惊叹的功能

而且,我们已经确定了 23.10 版本的发布日期,请立即注册参加 11 月 2 日上午 9:00(PDT)/下午 6:00(CET)的社区电话会议。

版本摘要

20 个新功能。

19 项性能优化。

55 个错误修复。

下面是突出显示的功能的一小部分...但该版本涵盖了仅在表为空时才删除表、JSON 格式的自动检测、对长列名的支持、改进数值到日期时间的转换、非常量时区、改进备份日志记录、更多 MYSQL 兼容性、生成临时凭证的能力、并行读取 INFILE 子句的文件、对 Tableau Online 的支持等等。

新贡献者

与往常一样,我们特别欢迎 23.9 版本的所有新贡献者!ClickHouse 的普及很大程度上归功于社区的贡献。看到社区的成长总是令人感到谦卑。

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

Alexander van Olst, Christian Clauss, CuiShuoGuo, Fern, George Gamezardashvili, Julia Kartseva, LaurieLY, Leonardo Maciel, Max Kainov, Petr Vasilev, Roman G, Tiakon, Tim Windelschmidt, Tomas Barton, Yinzheng-Sun, bakam412, priera, seshWCS, slvrtrn, wangtao.2077, xuzifu666, yur3k, Александр Нам.

JSON 的类型推断

由 Pavel Kruglov 贡献

ClickHouse 中的 JSON 何时才能投入生产使用?

是的,我们经常听到这个问题。没有这个问题,社区电话会议就不一样了!虽然我们继续致力于此功能,并在内部优先考虑使 ClickHouse 中的 JSON 达到生产状态,但我们也相信用户通常不需要它将提供的所有功能和灵活性。本着提供能够解决大多数需求的功能的精神,我们很高兴推出 JSON 的类型推断。

此功能明确针对具有结构良好且可预测的 JSON 的用户。它允许从结构化数据推断嵌套模式,从而使用户不必手动定义它。虽然这带来了一些限制,但它加快了入门体验。

例如,考虑以下 PyPI 数据。此数据源自 BigQuery,它作为公共数据集托管在那里,其中包含全球范围内 Python 软件包每次下载的行(我们在之前的文章中使用过它)。如下所示,此处的模式具有多个级别

pypi_schema.png

我们已将此数据的样本导出到 GCS 存储桶。在 23.9 版本之前,ClickHouse 用户需要定义模式才能查询此数据。如下所示,这将非常繁琐

SELECT
	file.version,
	count() AS c
FROM s3('https://storage.googleapis.com/clickhouse_public_datasets/pypi/sample/*.json.gz', 'NOSIGN', 'JSONEachRow', 'timestamp DateTime64(9), country_code String, url String, project String, file Tuple(filename String, project String, type String, version String), details\tTuple(cpu String, distro Tuple(id String, libc Tuple(lib String, version String), name String, version String), implementation Tuple(name String, version String), installer Tuple(name String, version String), openssl_version String, python String, rustc_version String, setuptools_version String, system Tuple(name String, release String)), tls_protocol String, tls_cipher String')
WHERE project = 'requests'
GROUP BY file.version
ORDER BY c DESC
LIMIT 5

┌─file.version─┬──────c─┐
│ 2.31.0268665 │
│ 2.27.129931 │
│ 2.26.011244 │
│ 2.25.110081 │
│ 2.28.28686 │
└──────────────┴────────┘

5 rows in set. Elapsed: 21.876 sec. Processed 26.42 million rows, 295.28 MB (1.21 million rows/s., 13.50 MB/s.)
Peak memory usage: 98.80 MiB.

此外,不可能从此数据的样本创建表并依赖模式推断。相反,用户需要在导入行之前手动定义表模式。

对于计划保留数据并构建生产服务的用户来说,这种开销是可以接受的。对于新用户或希望执行临时分析的用户来说,这代表了使用的障碍并增加了不必要的摩擦。从 23.9 版本开始,体验得到了简化,ClickHouse 可以推断模式

DESCRIBE TABLE s3('https://storage.googleapis.com/clickhouse_public_datasets/pypi/sample/*.json.gz')
FORMAT TSV

timestamp    Nullable(DateTime64(9))
country_code    Nullable(String)
url    Nullable(String)
project    Nullable(String)
file    Tuple(filename Nullable(String), project Nullable(String), type Nullable(String), version Nullable(String))
details    Tuple(cpu Nullable(String), distro Tuple(id Nullable(String), libc Tuple(lib Nullable(String), version Nullable(String)), name Nullable(String), version Nullable(String)), implementation Tuple(name Nullable(String), version Nullable(String)), installer Tuple(name Nullable(String), version Nullable(String)), openssl_version Nullable(String), python Nullable(String), rustc_version Nullable(String), setuptools_version Nullable(String), system Tuple(name Nullable(String), release Nullable(String)))
tls_protocol    Nullable(String)
tls_cipher    Nullable(String)

8 rows in set. Elapsed: 0.220 sec.

如果我们对模式感到满意,那么我们可以运行以下查询来查找最受欢迎的 requests 库版本

SELECT
	file.version,
	count() AS c
FROM s3('https://storage.googleapis.com/clickhouse_public_datasets/pypi/sample/*.json.gz')
WHERE project = 'requests'
GROUP BY file.version
ORDER BY c DESC
LIMIT 5

┌─file.version─┬──────c─┐
│ 2.31.0268665 │
│ 2.27.129931 │
│ 2.26.011244 │
│ 2.25.110081 │
│ 2.28.28686 │
└──────────────┴────────┘

5 rows in set. Elapsed: 4.306 sec. Processed 26.46 million rows, 295.80 MB (6.14 million rows/s., 68.69 MB/s.)
Peak memory usage: 487.79 MiB.

这也可以用于定义表

CREATE TABLE pypi
ENGINE = MergeTree
ORDER BY (project, timestamp) EMPTY AS
SELECT *
FROM s3('https://storage.googleapis.com/clickhouse_public_datasets/pypi/sample/*.json.gz') SETTINGS schema_inference_make_columns_nullable = 0

SHOW CREATE TABLE pypi FORMAT Vertical

CREATE TABLE default.pypi
(
	`timestamp` String,
	`country_code` String,
	`url` String,
	`project` String,
	`file` Tuple(filename String, project String, type String, version String),
	`details` Tuple(cpu String, distro Tuple(id String, libc Tuple(lib String, version String), name String, version String), implementation Tuple(name String, version String), installer Tuple(name String, version String), openssl_version String, python String, rustc_version String, setuptools_version String, system Tuple(name String, release String)),
	`tls_protocol` String,
	`tls_cipher` String
)
ENGINE = MergeTree
ORDER BY (project, timestamp)
SETTINGS index_granularity = 8192

请注意结构是如何自动推断为嵌套元组的。此模式推断不会生成优化的模式。我们建议用户仍然手动定义模式以优化类型和编解码器以获得最佳性能,并将推断的模式用作第一步或仅用于临时分析。

那么,与 JSON 类型相比,这种方法的局限性是什么?

首先,以上要求在用于模式推断的数据样本中指定所有列。默认情况下,ClickHouse 读取前 25k 行32MB(以较小者为准)数据以建立这些列。在此推断步骤中,结构不需要一致,并且行不需要包含所有列。例如,假设我们有以下消息

{"a" : 1, "obj" : {"x" : 1}}
{"b" : 2, "obj" : {"y" : 2}}

如果我们要求 ClickHouse 描述潜在的表结构,它会将 a 和 b 都添加为潜在列。

DESCRIBE TABLE format(JSONEachRow, '{"a" : 1, "obj" : {"x" : 1}}, {"b" : 2, "obj" : {"y" : 2}}')
FORMAT TSV
a    Nullable(Int64)
obj    Tuple(x Nullable(Int64), y Nullable(Int64))
b    Nullable(Int64)

但是,在此样本之后出现的新列将在后续导入时被忽略,即,模式不会更新。查询也不能引用样本中未出现的列。

其次,列的类型必须一致。换句话说,不支持同一 JSON 路径的不同类型。例如,以下是无效的

{"a" : 42}
{"a" : [1,2,3]}

我们理解某些用户拥有高度动态的数据,并且无法解决这些限制。因此,JSON 类型...

GCD 编解码器 - 更好的压缩

由 Alexander Nam 贡献

在 23.9 版本中,我们添加了一个新的编解码器 GCD。此编解码器基于最大公约数算法,可以显着提高存储在配置精度远高于要求的列中的十进制值的压缩率。当列中的数字很大(例如 1201000000)并且也以很大的增量变化(例如从 1201000000 变为 1203000000)时,此编解码器也有帮助。具有相似大小和分布的整数也可以从 GCD 中受益,例如具有纳秒精度和相对“不频繁”的日志消息的时间戳(例如 UInt64),例如每 100 毫秒。

此编解码器背后的想法很简单。在块级别,我们计算列值的 GCD(GCD 也是持久的),使用它来除以它们。通过减小值的比例,我们增加了其他编解码器(例如 Delta)的机会。即使是像 LZ4 和 ZSTD 这样的通用算法也可以从这种范围的缩小中受益。在查询时,使用存储的 GCD 值,我们可以通过简单的乘法来恢复原始值。

gcd_codec.png

例如,以上图中第一行为例。初始值为 1,201,000,000,它存储为 1,201,GCD 为 1,000,000。在查询时,值 1,201 将乘以 1,000,000 以返回到 1,201,000,000。

减小值的比例还具有额外的优势,即增加了其他编解码器(例如 Delta)进一步压缩数据的机会。即使是像 LZ4 和 ZSTD 这样的通用算法也可以从这种范围的缩小中受益。

例如,为了展示 GCD 编解码器的潜在优势,我们使用了下面超过 110 亿行的 Forex 数据集。此数据集包含两个 Decimal 列,bidask,我们评估了 GCD 编解码器对以下表配置的压缩的影响

  • forex_v1 - Decimal(76, 38) CODEC(ZSTD) - 此处的精度和比例远大于所需,导致 bidask 以大于所需的整数表示形式存储。应用 ZSTD 压缩。
  • forex_v2 - Decimal(76, 38) CODEC(GCD, ZSTD) - 与上述相同,但在 ZSTD 压缩之前应用 GCD 编解码器。
  • forex_v3 - Decimal(11, 5) CODEC(ZSTD) - 值的最佳(最小)精度和比例。应用 ZSTD 压缩。
  • forex_v4 - Decimal(11, 5) CODEC(GCD, ZSTD) - 值的最佳精度和比例,带有 GCD 编解码器和 ZSTD。

*所有情况下均为 ZSTD(1)。

注意:在内部,十进制数字存储为普通的有符号整数,精度决定所需的位数

示例表模式和数据加载如下所示

CREATE TABLE forex
(
	`datetime` DateTime64(3),
	`bid` Decimal(11, 5) CODEC(ZSTD(1)),
	`ask` Decimal(11, 5) CODEC(ZSTD(1)),
	`base` LowCardinality(String),
	`quote` LowCardinality(String)
)
ENGINE = MergeTree
ORDER BY (base, quote, datetime)

INSERT INTO forex
SELECT *
FROM s3Cluster('default', 'https://datasets-documentation.s3.eu-west-3.amazonaws.com/forex/csv/year_month/*.csv.zst', 'CSVWithNames')
SETTINGS min_insert_block_size_rows = 10000000, min_insert_block_size_bytes = 0, parts_to_throw_insert = 50000, max_insert_threads = 30, parallel_distributed_insert_select = 2

我们可以使用以下查询检查每个表配置的 bidask 列的压缩率

SELECT
	table,
	name,
	any(compression_codec) AS codec,
	any(type) AS type,
	formatReadableSize(sum(data_compressed_bytes)) AS compressed_size,
	formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed_size,
	round(sum(data_uncompressed_bytes) / sum(data_compressed_bytes), 2) AS ratio
FROM system.columns
WHERE (table LIKE 'forex%') AND (name IN ['bid', 'ask'])
GROUP BY
	table,
	name
ORDER BY
	table ASC,
	name DESC

┌─table────┬─name─┬─codec───────────────┬─type───────────┬─compressed_size─┬─uncompressed_size─┬─ratio─┐
│ forex_v1 │ bid  │ CODEC(ZSTD(1))  	│ DECIMAL(76, 38)│ 23.56 GiB   	   │ 345.16 GiB    	   │ 14.65 │
│ forex_v1 │ ask  │ CODEC(ZSTD(1))  	│ DECIMAL(76, 38)│ 23.61 GiB   	   │ 345.16 GiB    	   │ 14.62 │
│ forex_v2 │ bid  │ CODEC(GCD, ZSTD(1)) │ DECIMAL(76, 38)│ 14.47 GiB   	   │ 345.16 GiB    	   │ 23.86 │
│ forex_v2 │ ask  │ CODEC(GCD, ZSTD(1)) │ DECIMAL(76, 38)│ 14.47 GiB   	   │ 345.16 GiB    	   │ 23.85 │
│ forex_v3 │ bid  │ CODEC(ZSTD(1))  	│ DECIMAL(11, 5) │ 11.99 GiB   	   │ 86.29 GiB     	   │   7.2 │
│ forex_v3 │ ask  │ CODEC(ZSTD(1))  	│ DECIMAL(11, 5) │ 12.00 GiB   	   │ 86.29 GiB     	   │  7.19 │
│ forex_v4 │ bid  │ CODEC(GCD, ZSTD(1)) │ DECIMAL(11, 5) │ 9.77 GiB    	   │ 86.29 GiB     	   │  8.83 │
│ forex_v4 │ ask  │ CODEC(GCD, ZSTD(1)) │ DECIMAL(11, 5) │ 9.78 GiB    	   │ 86.29 GiB     	   │  8.83 │
└──────────┴──────┴─────────────────────┴────────────────┴─────────────────┴───────────────────┴───────┘

显然,定义具有不必要的高精度和比例的列对压缩和未压缩大小具有重大影响,forex_v1 占用的空间几乎是最近的其他配置的两倍,为 23.56 GiB。虽然 GCD 不会影响未压缩大小,但它确实将压缩大小减少了 38%,降至 14.47GiB。因此,在使用的精度高于必要精度的情况下,GCD 非常有用。

这些结果还表明,指定正确的精度和比例可以显着改进,forex_v3 仅消耗 12 Gib。未压缩大小的减小仅为大小的 1/4,由于每个值使用的位数较少(“64 对 256”),因此更可预测。

最后,即使使用优化的精度和比例,GCD 编解码器也在此处提供了显着的压缩改进。我们将列的压缩大小减少了近 20%,降至 9.8GiB。

希望这能显示 GCD 编解码器的潜力。如果您觉得它有用并且节省了成本,请告诉我们!

使用 SSH 密钥的简单身份验证

由 George Gamezardashvili 贡献

数据工程师和数据库管理员每次使用不同密码登录多个 ClickHouse 集群,希望他们会喜欢此功能。ClickHouse 现在支持通过 SSH 密钥进行身份验证的功能。这只需要用户将其公钥添加到 ClickHouse 配置文件即可。

$ cat users.d/alexey.yaml

users:
  alexey:
	ssh_keys:
  	ssh_key:
    	type: ssh-rsa
    	# cat ~/.ssh/id_rsa.pub
    	base64_key: 'AAAAB3NzaC1yc2EAAAABIwAAAQEAoZiwf7tVzIXGW26cuqnu...'

或通过 DDL

CREATE USER alexey IDENTIFIED WITH ssh_key BY KEY 'AAAAB3NzaC1yc2EAAAABIwAAAQEAoZiwf7tVzIXGW26cuqnu...' TYPE 'ssh-rsa'

连接到 ClickHouse 服务器时,用户无需提供密码,而是指定其私钥的路径。

$ clickhouse-client --ssh-key-file ~/.ssh/id_rsa --user alexey

每次都需要提供 SSH 密钥的路径对于某些用户来说可能会很麻烦,尤其是在连接到多台服务器时。不要忘记,您还可以通过配置配置客户端设置。此文件位于您的主目录中,即 ~/.clickhouse-client/config.xml。以上设置可以配置如下

<?xml version="1.0" ?>
<config>
    <secure>1</secure>
    <host>default_host</host>
    <openSSL>
   	 <client>
   		 <loadDefaultCAFile>true</loadDefaultCAFile>
   		 <cacheSessions>true</cacheSessions>
   		 <disableProtocols>sslv2,sslv3</disableProtocols>
   		 <preferServerCiphers>true</preferServerCiphers>
   		 <invalidCertificateHandler>
   			 <name>RejectCertificateHandler</name>
   		 </invalidCertificateHandler>
   	 </client>
    </openSSL>
    <prompt_by_server_display_name>
   	 <default>{display_name} :) </default>
    </prompt_by_server_display_name>
	<!--Specify private SSH key-->
	<user>alexey</user>
	<ssh-key-file>~/.ssh/id_rsa</ssh-key-file>
</config>

如果您的公钥已分发到我们的 ClickHouse 实例配置,则您无需指定公钥的位置即可连接。

clickhouse-client --host <optional_host_if_not_default>

注意:使用 SSH 密钥时,将提示用户输入密码。这可以在响应中输入,也可以通过参数 --ssh-key-passphrase 配置。

对于我们的云用户,我们正在努力尽快使其可用。

工作负载调度 - 更大事物的基础

由 Sergei Trifonov 贡献

ClickHouse 最受期待的功能之一是隔离查询工作负载的能力。更具体地说,用户通常需要定义一组查询的资源限制,目的是最大限度地减少其影响。此处的目的是确保这些查询不会影响其他业务关键型查询。

例如,ClickHouse 管理员可能需要运行一个大型查询,预计该查询将消耗大量资源并且需要几分钟甚至几小时才能完成。在此查询执行期间,ClickHouse 必须继续为来自业务关键型应用程序的快速查询提供服务。理想情况下,长运行查询的执行方式应使较小的关键快速查询不受影响。

虽然这可以通过 ClickHouse 中的内存配额CPU 限制部分实现,但我们承认目前它不像应该的那样容易实现。也没有限制共享资源(如磁盘 I/O)使用的方法。

因此,我们很高兴地宣布工作负载调度的基础。

虽然此功能的初始实现侧重于能够调度远程磁盘 IO,但它包括可以添加其他资源的框架和基础。

创建工作负载后,反过来可以使用 workload SETTING 安排查询,例如

SELECT count() FROM my_table WHERE value = 42 SETTINGS workload = 'long_running_limited'
SELECT count() FROM my_table WHERE value = 42 SETTINGS workload = 'priority'

有关如何配置工作负载计划的完整详细信息,我们建议查阅文档

分享此文章

订阅我们的新闻通讯

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