本文字数:5883;估计阅读时间:15 分钟
作者: ClickHouse官方
本文在公众号【ClickHouseInc】首发
新的一月,又迎来了新版本的发布!
发布概要
本次ClickHouse 24.11 版本包含了9个新功能🎁、15项性能优化🛷、68个bug修复🐛
在本次更新中,并行哈希 Join 成为默认的 Join 策略,WITH FILL 支持 STALENESS 修饰符,可以预热标记缓存。同时,引入 BFloat16 数据类型,显著提升了向量搜索的速度。
新贡献者
正如往常,我们热烈欢迎 24.11 版本中的所有新贡献者!ClickHouse 的持续流行,很大程度上依赖社区的积极贡献。看到社区的不断壮大,令我们倍感荣幸。
以下是本次版本的新贡献者名单:
0xMihalich, Max Vostrikov, Payam Qorbanpour, Plasmaion, Roman Antonov, Romeo58rus, Zoe Steinkamp, kellytoole, ortyomka, qhsong, udiz, yun, Örjan Fors, Андрей
并行哈希 Join 成为默认策略
贡献者:Nikita Taranov
在本次版本中,并行哈希 Join 算法取代了哈希 Join,成为默认的 Join 策略。
并行哈希 Join 是哈希 Join 的一种改进算法,通过将输入数据分片并并发地构建多个哈希表,实现了更快的 Join 操作,但需要更多内存资源。以下是该算法的查询管道示意图:
关于并行哈希 Join 的更多技术细节,请参阅博客文章《ClickHouse Joins Under the Hood - Hash Join, Parallel Hash Join, Grace Hash Join》【https://clickhouse.com/blog/clickhouse-fully-supports-joins-hash-joins-part2】。
除了默认启用外,本次更新还对该算法进行了性能优化。现在,线程间分发的块在并行处理时使用零拷贝技术,避免了每次复制块列所带来的额外开销。
ORDER BY WITH FILL 引入 STALENESS 修饰符
贡献者:Mikhail Artemenko
本次版本为 WITH FILL 引入了 STALENESS 修饰符。以下是结合 MidJourney 数据集的使用示例。假设我们已经下载了 Parquet 文件,可以通过以下查询将数据填充到表中:
CREATE TABLE images (
id String,
timestamp DateTime64,
height Int64,
width Int64,
size Int64
)
ENGINE = MergeTree
ORDER BY (size, height, width);
INSERT INTO images WITH data AS (
SELECT
assumeNotNull(timestamp) AS ts,
assumeNotNull(id) AS id,
assumeNotNull(height) AS height,
assumeNotNull(width) AS width,
assumeNotNull(size) AS size,
parseDateTime64BestEffort(ts) AS ts2
FROM file('data/0000{00..55}.parquet')
)
SELECT id, ts2 AS timestamp, height, width, size
FROM data;
如果我们想统计 2023 年 3 月 24 日某一秒内生成的图像数量,可以通过参数定义起始和结束时间:
SET param_start = '2023-03-24 00:24:02',
param_end = '2023-03-24 00:24:03';
接着,我们可以使用 WITH FILL 子句计算每 100 毫秒的计数,同时为空桶填充值 0:
SELECT
toStartOfInterval(timestamp, toIntervalMillisecond(100)) AS bucket,
count() AS count, 'original' as original
FROM MidJourney.images
WHERE (timestamp >= {start: String}) AND (timestamp <= {end: String})
GROUP BY ALL
ORDER BY bucket ASC
WITH FILL
FROM toDateTime64({start:String}, 3)
TO toDateTime64({end:String}, 3) STEP toIntervalMillisecond(100);
┌──────────────────bucket─┬─count─┬─original─┐
│ 2023-03-24 00:24:02.000 │ 0 │ │
│ 2023-03-24 00:24:02.100 │ 0 │ │
│ 2023-03-24 00:24:02.200 │ 0 │ │
│ 2023-03-24 00:24:02.300 │ 3 │ original │
│ 2023-03-24 00:24:02.400 │ 0 │ │
│ 2023-03-24 00:24:02.500 │ 0 │ │
│ 2023-03-24 00:24:02.600 │ 1 │ original │
│ 2023-03-24 00:24:02.700 │ 1 │ original │
│ 2023-03-24 00:24:02.800 │ 2 │ original │
│ 2023-03-24 00:24:02.900 │ 0 │ │
└─────────────────────────┴───────┴──────────┘
本次更新新增的 STALENESS 子句,其功能定义如下(摘自文档):
当定义了 STALENESS const_numeric_expr 时,查询将生成连续的行,直到前一行与当前行的时间差超过 const_numeric_expr。
在查询中,STALENESS 和 WITH FILL...FROM 子句不能同时使用,因此我们需要移除 WITH FILL...FROM。移除后,查询将如下所示:
SELECT
toStartOfInterval(timestamp, toIntervalMillisecond(100)) AS bucket,
count() AS count, 'original' as original
FROM MidJourney.images
WHERE (timestamp >= {start: String}) AND (timestamp <= {end: String})
GROUP BY ALL
ORDER BY bucket ASC
WITH FILL
TO toDateTime64({end:String}, 3) STEP toIntervalMillisecond(100);
删除 WITH FILL...FROM 子句意味着结果集将从第一个实际存在的值开始,而不是从指定的时间戳回填 0 值。
┌──────────────────bucket─┬─count─┬─original─┐
│ 2023-03-24 00:24:02.300 │ 3 │ original │
│ 2023-03-24 00:24:02.400 │ 0 │ │
│ 2023-03-24 00:24:02.500 │ 0 │ │
│ 2023-03-24 00:24:02.600 │ 1 │ original │
│ 2023-03-24 00:24:02.700 │ 1 │ original │
│ 2023-03-24 00:24:02.800 │ 2 │ original │
│ 2023-03-24 00:24:02.900 │ 0 │ │
└─────────────────────────┴───────┴──────────┘
如果为 STALENESS 设置 200 毫秒的值,查询将仅填充空行,直到前一行与当前行的时间差超过 200 毫秒:
SELECT
toStartOfInterval(timestamp, toIntervalMillisecond(100)) AS bucket,
count() AS count, 'original' as original
FROM MidJourney.images
WHERE (timestamp >= {start: String}) AND (timestamp <= {end: String})
GROUP BY ALL
ORDER BY bucket ASC
WITH FILL
TO toDateTime64({end:String}, 3) STEP toIntervalMillisecond(100)
STALENESS toIntervalMillisecond(200);
┌──────────────────bucket─┬─count─┬─original─┐
│ 2023-03-24 00:24:02.300 │ 3 │ original │
│ 2023-03-24 00:24:02.400 │ 0 │ │
│ 2023-03-24 00:24:02.600 │ 1 │ original │
│ 2023-03-24 00:24:02.700 │ 1 │ original │
│ 2023-03-24 00:24:02.800 │ 2 │ original │
│ 2023-03-24 00:24:02.900 │ 0 │ │
└─────────────────────────┴───────┴──────────┘
此时,以下行将从结果集中被移除:
│ 2023-03-24 00:24:02.500 │ 0 │ │
HTTP 接口错误检测
贡献者:Sema Checherinda
从现在起,HTTP 接口可以在结果流式传输给客户端之后,仍然可靠地检测到错误。在之前的版本中,如果我们在 ClickHouse Server 上运行以下查询:
curl http://localhost:8123/?output_format_parallel_formatting=0 -d "SELECT throwIf(number > 100000) FROM system.numbers FORMAT Values"
我们会先看到值流输出,随后附加如下的错误消息:
Code: 395. DB::Exception: Value passed to 'throwIf' function is non-zero: while executing 'FUNCTION throwIf(greater(number, 100000) :: 2) -> throwIf(greater(number, 100000)) UInt8 : 1'. (FUNCTION_THROW_IF_VALUE_IS_NON_ZERO) (version 24.3.1.465 (official build))
退出代码为 0,这表明查询被认为成功运行。而从 24.11 版本开始,我们将会看到如下输出:
Code: 395. DB::Exception: Value passed to 'throwIf' function is non-zero: while executing 'FUNCTION throwIf(greater(__table1.number, 100000_UInt32) :: 0) -> throwIf(greater(__table1.number, 100000_UInt32)) UInt8 : 1'. (FUNCTION_THROW_IF_VALUE_IS_NON_ZERO) (version 24.11.1.2557 (official build))
curl: (18) transfer closed with outstanding read data remaining
并且返回了非零退出代码 18。
Mark 缓存预热
贡献者:Anton Popov
Mark 文件将主键映射到每列文件的偏移量,是表索引的一部分。每个表的列都对应一个 mark 文件。这些文件可能占用大量内存,并会按需加载到 mark 缓存中。
从 24.11 版本开始,你可以通过 mark_cache_prewarm_ratio 参数预热 mark 缓存,该参数默认值为 95%。
服务器会在每次插入、合并或提取数据部分时主动将 mark 文件加载到内存缓存中,直到缓存几乎填满。
此外,新增了一个系统命令 SYSTEM PREWARM MARK CACHE t,可以立即将所有 mark 文件加载到缓存中。
BFloat16 数据类型
贡献者:Alexey Milovidov
BFloat16 数据类型由 Google Brain 团队开发,专用于表示向量嵌入。顾名思义,它由 16 位组成,其中 1 位为符号位,8 位为指数,7 位为尾数(小数部分)。
这一数据类型的指数范围与 Float32(单精度浮点数)相同,但尾数位数较少(7 位,而非 23 位)。
现在,ClickHouse 已支持 BFloat16 数据类型,非常适合用于 AI 和向量搜索场景。要使用这一新类型,需要进行以下参数配置:
SET allow_experimental_bfloat16_type=1;
我们在 AWS c7a.metal-48xl 实例上进行了一项性能测试,对 28 百万个 384 维向量进行完整扫描的最近邻搜索,结果如下:
clickhouse-benchmark --query "WITH
[-0.02925783360957671,-0.03488947771094666,...,0.032484047621093616]::Array(BFloat16)
AS center SELECT d FROM (SELECT cosineDistance(vector, center) AS d
FROM hackernews_llama_memory ORDER BY d LIMIT 10
) SETTINGS allow_experimental_bfloat16_type = 1"
BFloat16: 0.061 sec, 301 GB/sec.
Float32: 0.146 sec, 276 GB/sec.
征稿启示
面向社区长期正文,文章内容包括但不限于关于 ClickHouse 的技术研究、项目实践和创新做法等。建议行文风格干货输出&图文并茂。质量合格的文章将会发布在本公众号,优秀者也有机会推荐到 ClickHouse 官网。请将文章稿件的 WORD 版本发邮件至:Tracy.Wang@clickhouse.com