
本文字数:7295;估计阅读时间:19 分钟

又到新版本发布的时间了!
发布概要
本次ClickHouse 24.9 版本包含了23个新功能🎁、14项性能优化🛷、76个bug修复🐛
本次更新亮点包括:可刷新物化视图新增 APPEND 子句、JSON 数据类型支持更多新函数、自动模式推断现在还支持返回 Variant 类型。

一如既往地欢迎 24.9 版本中的新贡献者!ClickHouse 的受欢迎离不开社区的持续努力,看到这个社区不断壮大,总是让人倍感欣慰。
以下是新加入的贡献者名单:
1on, Alexey Olshanskiy, Alexis Arnaud, Austin Bruch, Denis Hananein, Dergousov, Gabriel Mendes, Konstantin Smirnov, Kruglov Kirill, Marco Vilas Boas, Matt Woenker, Maxim Dergousov, Michal Tabaszewski, NikBarykin, Oleksandr, Pedro Ferreira, Rodrigo Garcia, Samuel Warfield, Sergey (Finn) Gnezdilov, Tuan Pham Anh, Zhigao Hong, baolin.hbl, gao chuan, haozelong, imddba, kruglov, leonkozlowski, m4xxx1m, marco-vb, megao, mmav, neoman36, okunev, siyuan

贡献者:Michael Kolupaev
可刷新物化视图是一种实验性功能,能够将查询结果存储以便快速读取。在本次发布中,我们新增了 APPEND 功能,使得在更新视图时无需替换整个视图内容,而是直接将新行追加到表末尾。
这个功能的一个典型应用场景是捕获某一时间点的数据快照。假设我们有一个事件表,该表由 Redpanda、Kafka 或其他流数据平台的消息流持续填充。
SELECT *FROM eventsLIMIT 10Query id: 7662bc39-aaf9-42bd-b6c7-bc94f2881036┌──────────────────ts─┬─uuid─┬─count─┐│ 2008-08-06 17:07:19 │ 0eb │ 547 ││ 2008-08-06 17:07:19 │ 60b │ 148 ││ 2008-08-06 17:07:19 │ 106 │ 750 ││ 2008-08-06 17:07:19 │ 398 │ 875 ││ 2008-08-06 17:07:19 │ ca0 │ 318 ││ 2008-08-06 17:07:19 │ 6ba │ 105 ││ 2008-08-06 17:07:19 │ df9 │ 422 ││ 2008-08-06 17:07:19 │ a71 │ 991 ││ 2008-08-06 17:07:19 │ 3a2 │ 495 ││ 2008-08-06 17:07:19 │ 598 │ 238 │└─────────────────────┴──────┴───────┘
在这个数据集中,uuid 列包含 4096 个值。我们可以通过以下查询来找到总计数最高的那些值:
SELECTuuid,sum(count) AS countFROM eventsGROUP BY ALLORDER BY count DESCLIMIT 10┌─uuid─┬───count─┐│ c6f │ 5676468 ││ 951 │ 5669731 ││ 6a6 │ 5664552 ││ b06 │ 5662036 ││ 0ca │ 5658580 ││ 2cd │ 5657182 ││ 32a │ 5656475 ││ ffe │ 5653952 ││ f33 │ 5653783 ││ c5b │ 5649936 │└──────┴─────────┘
例如,如果我们希望每 10 秒记录一次每个 uuid 的计数,并将这些计数存储在一个新表 events_snapshot 中,events_snapshot 的表结构可以是这样的:
CREATE TABLE events_snapshot (ts DateTime32,uuid String,count UInt64)ENGINE = MergeTreeORDER BY uuid;
接下来,我们可以创建一个可刷新物化视图,将数据填充到这个表中:
SET allow_experimental_refreshable_materialized_view=1;CREATE MATERIALIZED VIEW events_snapshot_mvREFRESH EVERY 10 SECOND APPEND TO events_snapshotAS SELECTnow() AS ts,uuid,sum(count) AS countFROM eventsGROUP BY ALL;
然后,我们可以查询 events_snapshot 表,以获取某个特定 uuid 随时间变化的计数记录:
SELECT *FROM events_snapshotWHERE uuid = 'fff'ORDER BY ts ASCFORMAT PrettyCompactMonoBlock┌──────────────────ts─┬─uuid─┬───count─┐│ 2024-10-01 16:12:56 │ fff │ 5424711 ││ 2024-10-01 16:13:00 │ fff │ 5424711 ││ 2024-10-01 16:13:10 │ fff │ 5424711 ││ 2024-10-01 16:13:20 │ fff │ 5424711 ││ 2024-10-01 16:13:30 │ fff │ 5674669 ││ 2024-10-01 16:13:40 │ fff │ 5947912 ││ 2024-10-01 16:13:50 │ fff │ 6203361 ││ 2024-10-01 16:14:00 │ fff │ 6501695 │└─────────────────────┴──────┴─────────┘

贡献者:Shaun Struwig
ClickHouse 现在支持在模式推断中自动使用 Variant 数据类型。这一功能默认关闭,可以通过设置 ``input_format_try_infer_variants`` 来启用。
让我们来看一个示例,了解该功能如何运作。假设我们读取以下文件:
data1.json
{"id": [1], "name": "Mark"}{"id": "agerty", "name": "Dale"}
文件中,id 字段在第一行是一个整数数组,在第二行是一个字符串。我们可以查询该文件以查看 id 列的类型:
select *, toTypeName(id)FROM file('data1.json')SETTINGS input_format_try_infer_variants=1;┌─id─────┬─name─┬─toTypeName(id)──────────────────────────┐│ [1] │ Mark │ Variant(Array(Nullable(Int64)), String) ││ agerty │ Dale │ Variant(Array(Nullable(Int64)), String) │└────────┴──────┴─────────────────────────────────────────┘
如果没有启用 input_format_try_infer_variants=1,则会收到以下错误消息:
Received exception:Code: 636. DB::Exception: The table structure cannot be extracted from a JSON format file. Error:Code: 53. DB::Exception: Automatically defined type String for column 'id' in row 1 differs from type defined by previous rows: Array(Int64). You can specify the type for this column using setting schema_inference_hints. (TYPE_MISMATCH) (version 24.9.1.3278 (official build)).You can specify the structure manually: (in file/path/to/24.9/data1.json). (CANNOT_EXTRACT_TABLE_STRUCTURE)
需要注意的是,Variant 类型并不总是会在您预期的地方被推断。例如,如果 id 字段中的值可以被转换为 String 类型,即使 Variant 类型也是可推断的,推断结果仍然会是 String 类型。以下文件就是这种情况:
data2.json
{"id": 1, "name": "Mark"}{"id": "agerty", "name": "Dale"}{"id": "2021-01-04", "name": "Tom"}
当运行以下查询时:
select *, toTypeName(id)FROM file('data2.json')SETTINGS input_format_try_infer_variants=1;┌─id─────────┬─name─┬─toTypeName(id)───┐│ 1 │ Mark │ Nullable(String) ││ agerty │ Dale │ Nullable(String) ││ 2021-01-04 │ Tom │ Nullable(String) │└────────────┴──────┴──────────────────┘
id 列会被推断为 Nullable(String),因为所有值都可以被转换为字符串。不过,您仍然可以通过提供提示,将 id 列推断为 Variant 类型:
SET allow_experimental_variant_type=1;SELECT *, toTypeName(id)FROM file('data2.json')SETTINGS schema_inference_hints='id Variant(String, Int64, Date)';┌─id─────────┬─name─┬─toTypeName(id)───────────────┐│ 1 │ Mark │ Variant(Date, Int64, String) ││ agerty │ Dale │ Variant(Date, Int64, String) ││ 2021-01-04 │ Tom │ Variant(Date, Int64, String) │└────────────┴──────┴──────────────────────────────┘

贡献者:Pavel Kruglov
在 24.8 版本中,我们介绍了新的 JSON 数据类型。这次更新中,我们为 JSON 和 Dynamic 数据类型新增了更多实用函数。以下是一个示例数据集:
data3.json
{"id": 1, "name": "Mark"}{"id": "agerty", "name": "Dale"}{"id": "2021-01-04", "name": "Tom"}{"id": ["1", 2, "3"], "name": "Alexey", "location": "Netherlands"}
首先,distinctJSONPaths 函数可以用来返回唯一的 JSON 路径。
SELECT distinctJSONPaths(json)FROM file('data3.json', JSONAsObject)FORMAT Vertical;Row 1:──────distinctJSONPaths(json): ['id','location','location.city','location.country','name']
如果您还想查看类型,可以使用 distinctJSONPathsAndTypes 函数:
SELECT distinctJSONPathsAndTypes(json)FROM file('data3.json', JSONAsObject)FORMAT Vertical;Row 1:──────distinctJSONPathsAndTypes(json): {'id':['Array(Nullable(String))','Int64','String'],'location':['String'],'location.city':['String'],'location.country':['String'],'name':['String']}
最后,distinctDynamicTypes 函数可以返回 Dynamic 列中的唯一数据类型。
SELECT distinctDynamicTypes(json.id)FROM file('data3.json', JSONAsObject)FORMAT VerticalRow 1:──────distinctDynamicTypes(json.id): ['Array(Nullable(String))','Int64','String']

贡献者:Flynn
现在,查询 url 表函数时,可以通过 _headers 虚拟列访问响应头信息:
SELECT _headersFROM url('https://en.wikipedia.org/w/api.php?action=query&list=recentchanges&rcprop=title%7Cids%7Csizes%7Cflags%7Cuser%7Cuserid%7Ctimestamp&format=json&rcdir=newer')LIMIT 1FORMAT Vertical;Row 1:──────_headers: {'accept-ranges':'bytes','age':'0','cache-control':'private, must-revalidate, max-age=0','content-disposition':'inline; filename=api-result.json','content-type':'application/json; charset=utf-8','date':'Tue, 01 Oct 2024 15:32:59 GMT','nel':'{ "report_to": "wm_nel", "max_age": 604800, "failure_fraction": 0.05, "success_fraction": 0.0}','report-to':'{ "group": "wm_nel", "max_age": 604800, "endpoints": [{ "url": "https://intake-logging.wikimedia.org/v1/events?stream=w3c.reportingapi.network_error&schema_uri=/w3c/reportingapi/network_error/1.0.0" }] }','server':'mw-api-ext.codfw.main-54d5bc66d9-98km5','server-timing':'cache;desc="pass", host;desc="cp3067"','set-cookie':'WMF-Last-Access=01-Oct-2024;Path=/;HttpOnly;secure;Expires=Sat, 02 Nov 2024 12:00:00 GMT','strict-transport-security':'max-age=106384710; includeSubDomains; preload','transfer-encoding':'chunked','vary':'Accept-Encoding,Treat-as-Untrusted,X-Forwarded-Proto,Cookie,Authorization','x-cache':'cp3067 miss, cp3067 pass','x-cache-status':'pass','x-client-ip':'82.35.72.115','x-content-type-options':'nosniff','x-frame-options':'DENY'}
overlay 函数
如果您需要将字符串片段替换为另一个字符串,overlay 函数让这一过程更为简单。只需提供初始字符串、替换字符串、替换的起始位置以及替换字符的数量即可。
我们可以利用这个函数来表达 chDB 【https://clickhouse.com/docs/en/chdb】的酷炫之处!
SELECT overlay('ClickHouse is cool', 'and chDB are', 12, 2) AS res┌─res──────────────────────────┐│ ClickHouse and chDB are cool │└──────────────────────────────┘
试用阿里云 ClickHouse企业版
轻松节省30%云资源成本?阿里云数据库ClickHouse架构全新升级,推出和原厂独家合作的ClickHouse企业版,在存储和计算成本上带来双重优势,现诚邀您参与100元指定规格测一个月的活动,了解详情:https://t.aliyun.com/Kz5Z0q9G


征稿启示
面向社区长期正文,文章内容包括但不限于关于 ClickHouse 的技术研究、项目实践和创新做法等。建议行文风格干货输出&图文并茂。质量合格的文章将会发布在本公众号,优秀者也有机会推荐到 ClickHouse 官网。请将文章稿件的 WORD 版本发邮件至:Tracy.Wang@clickhouse.com






