DoubleCloud 即将停止运营。使用限时免费迁移服务迁移到 ClickHouse。立即联系我们 ->->

博客 / 工程

ClickHouse 23.9 版本发布

author avatar
ClickHouse 团队
2023 年 10 月 24 日

我们非常高兴与大家分享 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

JSON 何时将在 ClickHouse 中投入生产使用?

是的,我们经常听到这个问题。如果没有这个问题,社区电话会议就不完整!虽然我们一直在努力开发这项功能,并将内部优先级排序,使其在 ClickHouse 中达到生产状态,但我们也认为用户通常不需要它提供的全部功能和灵活性。为了提供一种能够满足大部分需求的功能,我们很高兴地介绍 JSON 类型推断功能。

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

例如,请考虑以下 PyPI 数据。此数据源自 BigQuery,在 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读取数据中的前 25000 行32 MB(以较小者为准)来确定这些列。在此推断步骤期间,结构不需要一致,行也不需要包含所有列。例如,假设我们有以下消息

{"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 中获益,例如具有纳秒精度的 timestamps(例如 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 亿行外汇数据集。该数据集包含两个 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)。

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

示例表格架构和数据加载如下所示

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。由于每个值使用的位数较低,64 vs 256,未压缩大小的减少(仅为 1/4 大小)更加可预测。

最后,即使在优化了精度和比例的情况下,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)的使用。

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

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

创建工作负载后,查询就可以使用 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'

有关如何配置工作负载调度的完整详细信息,我们建议您阅读 文档

分享此帖子

订阅我们的时事通讯

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