本文字数:7295;估计阅读时间:19 分钟
作者:ClickHouse Team
本文在公众号【ClickHouseInc】首发
又到新版本发布的时间了!
发布概要
本次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
可刷新物化视图新增 APPEND 功能
贡献者:Michael Kolupaev
可刷新物化视图是一种实验性功能,能够将查询结果存储以便快速读取。在本次发布中,我们新增了 APPEND 功能,使得在更新视图时无需替换整个视图内容,而是直接将新行追加到表末尾。
这个功能的一个典型应用场景是捕获某一时间点的数据快照。假设我们有一个事件表,该表由 Redpanda、Kafka 或其他流数据平台的消息流持续填充。
SELECT *
FROM events
LIMIT 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 count
FROM events
GROUP BY ALL
ORDER BY count DESC
LIMIT 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 = MergeTree
ORDER BY uuid;
接下来,我们可以创建一个可刷新物化视图,将数据填充到这个表中:
SET allow_experimental_refreshable_materialized_view=1;CREATE MATERIALIZED VIEW events_snapshot_mv
REFRESH EVERY 10 SECOND APPEND TO events_snapshot
AS SELECTnow() AS ts,uuid,sum(count) AS count
FROM events
GROUP BY ALL;
然后,我们可以查询 events_snapshot 表,以获取某个特定 uuid 随时间变化的计数记录:
SELECT *
FROM events_snapshot
WHERE uuid = 'fff'
ORDER BY ts ASC
FORMAT 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 │
└─────────────────────┴──────┴─────────┘
Variant 类型的模式推断
贡献者: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) │
└────────────┴──────┴──────────────────────────────┘
用于 JSON 数据分析的聚合函数
贡献者: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']
URL 引擎支持 _headers 列
贡献者:Flynn
现在,查询 url 表函数时,可以通过 _headers 虚拟列访问响应头信息:
SELECT _headers
FROM 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 1
FORMAT 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 的技术研究、项目实践和创新做法等。建议行文风格干货输出&图文并茂。质量合格的文章将会发布在本公众号,优秀者也有机会推荐到 ClickHouse 官网。请将文章稿件的 WORD 版本发邮件至:Tracy.Wang@clickhouse.com