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

博客 / 工程

刚开始使用 ClickHouse?这里有 13 条“致命罪”以及如何避免它们。

author avatar
Dale McDiarmid,Tom Schreiber 和 Geoff Genz
2022年10月26日

getting-started-challenges.png

引言

在 ClickHouse,我们始终在思考如何改善用户入门体验,以及如何帮助用户在最短的时间内从我们的产品中获得价值。虽然大多数用户都能顺利上手,但我们也认识到 ClickHouse 是一款复杂的软件,其中包含了许多新概念。再加上大规模管理 ClickHouse 的挑战,这也是我们开发无服务器 ClickHouse Cloud 解决方案的原因之一,该解决方案可以自动处理许多常见的入门和后续扩展挑战。

然而,有些问题仅仅是由于配置错误或更常见的情况——对 ClickHouse 行为和功能使用方式的误解所导致的。在这篇文章中,我们重点介绍了新用户遇到的 13 个最常见的问题,这些问题通常是由于使用了反模式或未遵循最佳实践导致的:也就是 ClickHouse 入门的 13 宗“致命罪”。所有这些问题都适用于自托管用户,其中一部分问题也适用于 ClickHouse Cloud。对于每个问题,我们都建议相应的解决方案或正确的方法。

1. 部件过多

这是一个经常出现的 ClickHouse 错误,通常表示 ClickHouse 使用不当且未遵循最佳实践。此错误通常在插入数据时出现,并且会出现在 ClickHouse 日志中或 INSERT 请求的响应中。要理解此错误,用户需要对 ClickHouse 中的部件概念有一个基本的了解。

ClickHouse 中的表由按用户指定的 primary key 排序的数据部件组成(默认为创建表时的 ORDER BY 子句,但请参阅 索引设计 获取详细信息)。当数据插入表中时,会创建单独的数据部件,并且每个部件都按 primary key 以字典序排序。例如,如果 primary key 为 (CounterID, Date),则部件中的数据首先按 CounterID 排序,然后在每个 CounterID 值内按 Date 排序。在后台,ClickHouse 会合并数据部件以实现更有效的存储,类似于 日志结构合并树。每个部件都有自己的 primary index,以便高效地扫描和识别值在部件中的位置。当部件合并时,合并后的部件的 primary index 也将合并。

sins-01-parts.png

随着部件数量的增加,查询速度必然会下降,因为需要评估更多的索引和读取更多文件。在部件数量较多的情况下,用户也可能会遇到启动时间变慢的问题。因此,部件过多会导致更多的内部合并以及保持部件数量较低和查询性能较高的“压力”。虽然合并是并发的,但在滥用或配置错误的情况下,部件数量可能会超过内部可配置的限制 [1][2]。虽然可以调整这些限制(以牺牲查询性能为代价),但更多情况下,需要进行调整则表明您的使用模式存在问题。除了导致查询性能下降外,部件数量过多还会给复制配置中的 ClickHouse Keeper 带来更大的压力。

那么,如何才能避免部件过多呢?

分区键选择不当

一个常见的原因是使用基数过高的分区键。在创建表时,用户可以通过指定某个列作为分区键来分离数据。对于每个键值,都会创建一个新的文件系统目录。这通常是一种数据管理技术,允许用户在表中逻辑地清晰地分离数据,例如按日期。诸如 DROP PARTITION 之类的操作随后允许快速删除数据子集。但是,此功能很容易被误用,用户将其视为一种简单的查询优化技术。重要的是,属于不同分区的部件永远不会被合并。如果选择基数过高的键(例如,date_time_ms)作为分区键,那么这些部件将分散在数千个文件夹中,永远不会成为合并候选对象——超过预配置的限制,并在后续 INSERT 操作中导致可怕的“太多非活动部件 (N)。部件清理处理速度明显慢于插入”错误。解决此问题很简单:选择一个合理的基数小于 1000 的分区键。

sins-02-partitioning.png

大量的小批量插入

除了分区键选择不当之外,此问题也可能是由于大量的小批量插入导致的。ClickHouse 中的每次 INSERT 操作都会导致插入块转换为一个部件。为了使部件数量保持在可控范围内,用户应该在客户端缓冲数据并批量插入数据——理想情况下,至少 1000 行,尽管这需要进行调整。如果无法进行客户端缓冲,用户可以通过 异步插入 将此任务延迟到 ClickHouse。在这种情况下,ClickHouse 会在本地磁盘上缓冲插入操作,然后再将它们合并在一起插入到基础表中。

sins-03-async_inserts.png

缓冲表 也是此处的另一种选择,但它们对故障的容错性较差,因为它们会将插入操作保存在内存中,直到刷新发生。与异步插入相比,它们确实有一些优势——主要是数据在缓冲区中时可查询,以及它们作为物化视图目标表的缓冲区的兼容性。

物化视图过多

此错误的其他可能原因是物化视图过多。实际上,物化视图是在数据块插入表时运行的触发器。它们会转换数据(例如,通过 GROUP BY),然后将结果插入到另一个表中。此技术通常用于通过在 INSERT 时预先计算聚合来加速某些查询。用户可以创建这些物化视图,这可能导致许多部件。通常,我们建议用户在创建视图时了解其成本并在可能的情况下进行合并。

sins-04-mvs.png

以上列表并非此错误的详尽原因。例如,变异(如下所述)也会导致合并压力和部件累积。最后,我们应该注意,尽管此错误最常见,但它只是上述错误配置的一种表现形式。例如,用户可能会由于分区键选择不当而遇到其他问题。这些问题包括但不限于“文件系统上没有空闲 inode”、“备份时间过长”以及复制延迟(以及 ClickHouse Keeper 上的高负载)。

2. 过早进行水平扩展

我们经常遇到新的自管理用户要求我们提供关于编排以及如何扩展到数十甚至数百个节点的建议。虽然 Kubernetes 等技术使部署多个无状态应用程序实例变得相对简单,但在几乎所有情况下,ClickHouse 都不需要这种模式。与可能因固有限制(例如 JVM 堆大小)而受限于机器大小的其他数据库不同,ClickHouse 从一开始就被设计为利用机器的全部资源。我们通常会发现,ClickHouse 部署在具有数百个内核、数 TB 内存和数 PB 磁盘空间的服务器上是成功的。大多数分析查询都有排序、过滤和聚合阶段。每个阶段都可以独立地并行化,并且默认情况下会使用尽可能多的线程作为内核数,从而利用机器的全部资源来执行查询。

sins-05-vertical_scale.png

首先进行垂直扩展有很多好处,主要是成本效益、更低的拥有成本(在运营方面)以及由于最大限度地减少网络上的数据以进行 JOIN 等操作而带来的更好的查询性能。当然,用户需要在其基础设施中进行冗余,但对于除最大用例之外的所有用例,两台机器就足够了。

因此,除了更简单的扩展机制外,我们更倾向于在ClickHouse Cloud中首先自动垂直扩展,然后再考虑水平扩展。总之,先垂直扩展,再水平扩展!

3. 变异带来的问题

虽然在 OLAP 使用案例中很少见,但有时无法避免修改数据的需求。为了满足此需求,ClickHouse 提供了变异功能,允许用户通过ALTER 查询修改插入的数据。ClickHouse 在不可变数据上性能最佳,任何需要在插入后更新数据的模式都应仔细审查。

在内部,变异通过重写整个数据部件来工作。此过程依赖于与合并相同的线程池。另请注意,变异需要默认应用于所有副本。因此,变异既是 CPU 密集型又是 IO 密集型,应谨慎计划,并限制管理员才能运行。变异导致的资源压力以多种方式表现出来。通常,正常计划的合并会累积,这反过来会导致我们之前提到的“部件过多”问题。此外,用户可能会遇到复制延迟。system.mutations 表应向管理员指示当前计划的变异。请注意,可以使用KILL MUTATION查询取消变异,但不能回滚。

sins-06-mutations.png

重复数据删除

我们经常看到用户需要计划合并以解决重复数据问题。通常,我们建议用户在插入 ClickHouse 之前解决此问题并进行重复数据删除。如果无法做到这一点,用户有以下几种选择:在查询时进行重复数据删除或使用ReplacingMergeTree

可以在查询时通过对唯一标识行的字段进行分组并使用argMax函数以及日期字段来识别其他字段的最后一个值来实现重复数据删除。ReplacingMergeTree允许在合并时对具有相同排序键(ORDER BY 键)的行进行重复数据删除。请注意,这仅为“尽力而为”:有时部件不会与在非确定性间隔安排的合并过程合并。因此,它不能保证不存在重复项。用户还可以使用FINAL修饰符在SELECT时强制执行此重复数据删除(同样,谨慎使用,因为它会占用大量资源并且可能很慢,尽管最近有所改进)或通过OPTIMIZE FINAL强制在磁盘上合并。

在需要从 ClickHouse 中删除数据(例如,出于合规性或重复数据删除的原因)的情况下,用户还可以使用轻量级删除而不是变异。这些采用DELETE 语句的形式,该语句接受 WHERE 子句以过滤行。这仅将行标记为已删除。这些标记将用于在查询时过滤掉行,并在部件合并时删除。

sins-07-lightweight_deletes.png

注意:此功能为实验性功能,需要设置SET allow_experimental_lightweight_delete = true;。在大多数情况下,它比使用变异更有效,除非您正在执行大规模批量删除。

4. 不必要地使用复杂类型

除了支持常用的基本类型外,ClickHouse 还丰富地支持复杂类型,例如嵌套元组映射,甚至JSON。支持这些类型是有充分理由的——有时,没有其他方法对数据建模,但我们建议尽可能使用基本类型,因为它们提供了最佳的插入和查询时间性能。

例如,我们最近看到用户热衷于利用 ClickHouse 在 22.4 版本中添加的JSON 功能。此强大功能允许从数据中动态推断表模式,避免用户需要指定列类型。谨慎使用此功能,不要将其作为避免显式指定列的替代方案。具体来说,此功能有一些用户应该注意的限制

  • 插入时间成本增加,因为需要动态创建列
  • 次优类型使用,即没有编解码器和不必要地使用 Nullable。
  • 无法在主键中使用 JSON 列

最后两个通常会导致较差的压缩和查询/插入性能。不要对所有行都使用它,而是对某些列(例如 Kubernetes 标签)使用此特定类型,其中数据可能会发生变化。总之,如果您知道您的模式……请指定它!

注意:JSON 对象类型为实验性类型,正在改进中。我们对此功能的建议正在不断发展,因此可能会在以后的版本中发生变化。

此外,我们经常看到用户使用Nullable类型。这允许将 Null 值与类型的默认值区分开来。这可能很有用,但需要一个额外的 Uint8 列来确定哪些值为 null。这会使每个值的存储增加一个字节(尽管它压缩得很好),并增加查询时间开销。仅在确实需要时才使用 Nullable!

5. 在插入时进行重复数据删除

新用户使用ClickHouse Cloud时,常常对 ClickHouse 的重复数据删除策略感到惊讶。这通常发生在相同的插入似乎没有任何效果时。例如,考虑以下情况

CREATE TABLE temp ( `timestamp` DateTime, `value` UInt64 ) ENGINE = MergeTree ORDER BY tuple() INSERT INTO temp VALUES ('2022-10-21', 10), ('2022-10-22', 20), ('2022-10-23', 15), ('2022-10-24', 18) INSERT INTO temp VALUES ('2022-10-21', 10), ('2022-10-22', 20), ('2022-10-23', 15), ('2022-10-24', 18) clickhouse-cloud :) SELECT * FROM temp SELECT * FROM temp ┌───────────timestamp─┬─value─┐ │ 2022-10-21 00:00:00 │ 10 │ │ 2022-10-22 00:00:00 │ 20 │ │ 2022-10-23 00:00:00 │ 15 │ │ 2022-10-24 00:00:00 │ 18 │ └─────────────────────┴───────┘

新用户可能会对这里的结果感到惊讶,尤其是在他们之前使用 ClickHouse 的单个本地实例时。此行为是replicated_deduplication_window设置的结果。

将数据插入 ClickHouse 时,它会创建一个或多个块(部件)。在复制环境(例如ClickHouse Cloud)中,还会在 ClickHouse Keeper 中写入哈希值。后续插入的块会与这些哈希值进行比较,如果存在匹配项则会被忽略。这很有用,因为它允许客户端在 ClickHouse 未确认(例如,由于网络中断)的情况下安全地重试插入。这要求块必须相同,即大小相同,并且行顺序相同。这些哈希值仅存储最近的 100 个块,尽管可以修改。请注意,较高的值会减慢插入速度,因为需要进行更多比较。

sins-08-deduplication.png

可以通过non_replicated_deduplication_window设置对非复制实例启用相同的行为。在这种情况下,哈希值存储在本地磁盘上。

6. 主键选择不当

ClickHouse 的新用户常常难以完全理解其独特的主键概念。与基于B(+)-树的 OLTP 数据库(针对快速定位特定行进行了优化)不同,ClickHouse 使用稀疏索引,该索引设计用于每秒插入数百万行和 PB 级数据集。与 OLTP 数据库相比,此索引依赖于磁盘上的数据按顺序排列,以便快速识别可能匹配查询的行组——这是分析查询中的常见需求。实际上,索引允许在将部分文件流式传输到处理引擎之前快速识别匹配的部分,从而提高效率。有关磁盘上数据布局的更多详细信息,我们强烈推荐本指南

sins-09-primary_index.png

这种方法的有效性(对于查询性能和压缩而言)依赖于用户在创建表时通过ORDER BY子句选择良好的主键列。通常,用户应选择经常用于过滤表的列,很少需要超过 2 到 3 列。这些列的顺序至关重要,并且会影响压缩和对除第一项以外的列进行过滤。为了有效地过滤查询中的辅助键列以及表列文件的压缩率,最佳做法是按基数升序排列主键中的列。可以在此处找到完整的原因解释。

7. 过度使用数据跳过索引

主键是用户在需要加速查询时首先想到的工具。但是,表仅限于一个主键,并且查询访问模式可能导致主键效率低下,例如,对于各种用例,无法有效利用主键的查询是不可避免的。在这些情况下,ClickHouse在应用WHERE子句条件时可能会强制执行每个列的完整表扫描。通常,这仍然足够快,但在某些情况下,用户会求助于数据跳过索引,希望轻松加速这些查询。

这些索引添加了数据结构,使ClickHouse能够跳过读取大量保证没有匹配值的的数据块。更具体地说,它们在块粒度上创建索引(有效标记),如果WHERE子句不满足,则可以跳过这些标记。

sins-10-skipping_index.png

在某些情况下,这些可以加速特定的查询,但通常被过度使用,不直观且需要仔细设计才能有效。因此,我们经常看到它们只是使表设计复杂化并降低插入性能,而很少(如果有的话)提高查询性能。我们始终鼓励用户阅读相关概念和最佳实践

在大多数情况下,只有在其他替代方案都已用尽后才应考虑跳过索引——具体来说,此高级功能应仅在调查其他替代方案(例如修改主键(请参阅创建其他主键的选项)),使用投影或物化视图之后使用。一般而言,仅当主键与目标非主键列/表达式之间存在强相关性时,才考虑跳过索引。如果没有任何实际的相关性,则跳过索引将匹配大多数数据块——导致所有粒度都被读取到内存中并进行评估。在这种情况下,索引成本已产生,但没有带来任何好处,实际上会减慢完整表扫描的速度。

8. LIMIT 并不总是能够短路 + 点查找

我们经常发现刚接触 ClickHouse 的 OLTP 用户会使用 LIMIT 子句来优化查询,以限制返回的结果数量。如果来自 OLTP 数据库,这直观地应该能够优化查询:返回的数据越少,结果越快,当然?是的,也未必。

此技术的有效性取决于查询是否可以完全以流式方式运行。某些查询,例如 SELECT * FROM table LIMIT 10 将只扫描前几个部分的少数几个粒度,直到达到 10 个结果并将结果返回给用户。对于用户按主键字段对 SELECT 进行排序的情况,这也是正确的,因为optimize_in_read_order 设置默认为 1。但是,如果用户运行 SELECT a from table ORDER BY b LIMIT N,其中表按 a 而不是 b 排序,则 ClickHouse 无法避免读取整个表,即无法提前终止查询。

对于聚合,情况稍微复杂一些。除非用户按主键分组并设置optimize_aggregation_in_order=1,否则也需要进行完整表扫描。在这种情况下,一旦获取到足够的结果,就会发送一个传播信号。如果查询的先前步骤能够流式传输数据(例如,过滤),则此机制将起作用,并且查询将提前终止。但是,通常情况下,聚合必须在返回并应用 LIMIT 作为最后阶段之前使用所有表数据。

例如,我们从我们的英国房产价格支付教程创建并加载包含 2755 万行的表。此数据集可在我们的 play.clickhouse.com 环境中获得。

使用 optimize_aggregation_in_order=0,此聚合查询(按主键分组)在应用 LIMIT 1 子句之前执行完整表扫描。

clickhouse-cloud :) SELECT postcode1, postcode2, formatReadableQuantity(avg(price)) AS avg_price FROM uk_price_paid GROUP BY postcode1, postcode2 LIMIT 1; ┌─postcode1─┬─postcode2─┬─avg_price───────┐ │ AL4 │ 0DE │ 335.39 thousand │ └───────────┴───────────┴─────────────────┘ Elapsed: 3.028 sec, read 27.55 million rows, 209.01 MB.

使用 optimize_aggregation_in_order=1,查询能够进行快捷处理,从而处理更少的数据。

clickhouse-cloud :) SELECT postcode1, postcode2, formatReadableQuantity(avg(price)) AS avg_price FROM uk_price_paid GROUP BY postcode1, postcode2 LIMIT 1 SETTINGS optimize_aggregation_in_order = 1; ┌─postcode1─┬─postcode2─┬─avg_price───────┐ │ AL4 │ 0DE │ 335.39 thousand │ └───────────┴───────────┴─────────────────┘ Elapsed: 0.999 sec, read 4.81 million rows, 36.48 MB.

我们还发现,即使是经验丰富的用户也会在表具有多个分片的多节点环境中被不明显的 LIMIT 行为所困扰。分片允许用户将他们的数据拆分或复制到 ClickHouse 的多个实例中。当向分片表(例如,通过分布式表)发送带有 LIMIT N 子句的查询时,此子句将传播到每个分片。然后,每个分片都需要整理前 N 个结果,并将它们返回给协调节点。当用户运行需要完整表扫描的查询时,这可能会特别占用资源。这些通常是“点查找”,其中查询旨在仅识别几行。虽然这可以通过仔细设计 ClickHouse 的索引来实现,但未优化的变体加上 LIMIT 子句可能会非常占用资源。

9. 云端 IP 过滤

在 ClickHouse,我们认为安全是重中之重,并在我们所做的一切中都考虑到了这一点。这体现在用户在首次创建集群时需要指定允许访问的 IP 地址的需求中。默认情况下,我们鼓励用户限制访问并根据需要修改允许列表。不幸的是,当用户尝试连接到外部云服务时(例如,从 Grafana Cloud 连接时),这可能会导致一些混淆。我们将继续优化此体验并在出现这种情况时提供有益的指导,但我们也建议用户在集群创建初期获取任何外部服务的 IP 地址,以避免令人沮丧的连接拒绝错误。

ip-filtering-10.png

10. 只读表

虽然在ClickHouse Cloud中不是问题,但只读表在自管理集群中仍然会引起问题。当节点丢失与 ZooKeeper 的连接时,这种情况发生在复制环境中。这通常几乎总是 ZooKeeper 问题的结果。虽然随着 ClickHouse Keeper 的发布解决了与 ZooKeeper 相关的许多挑战,但此组件的资源不足仍然会导致此问题出现。常见原因是在生产环境中将 keeper 托管在与 ClickHouse 相同的主机上,或者 ZooKeeper JVM 资源调整不当。这通常可以通过确保此组件在专用硬件上分离并提供足够的资源来轻松解决。

11. 查询内存限制超出

作为新用户,ClickHouse 常常让人感觉像魔法一样——即使在最大的数据集和最雄心勃勃的查询上,每个查询都非常快。然而,不可避免地,现实世界的使用测试甚至会测试 ClickHouse 的极限。超过内存限制的查询可能是由多种原因造成的。最常见的是,我们在高基数字段上看到大型联接或聚合。如果性能至关重要,并且需要这些查询,我们通常建议用户简单地进行扩展——ClickHouse Cloud 会自动且毫不费力地执行此操作,以确保您的查询保持响应。但是,我们也理解,在自管理场景中,这有时并非易事,也许最佳性能甚至不是必需的。在这种情况下,用户有一些选择。

聚合

对于内存密集型聚合或排序场景,用户可以使用设置max_bytes_before_external_group_bymax_bytes_before_external_sort。前者在此处进行了广泛讨论。总之,这确保了如果超过内存阈值,任何聚合都可以“溢出”到磁盘。这将不可避免地影响查询性能,但将有助于确保查询不会出现内存不足错误。后者排序设置有助于解决内存密集型排序的类似问题。这在分布式环境中可能尤其重要,在分布式环境中,协调节点会接收来自子分片的排序响应。在这种情况下,可以要求协调服务器对超过其可用内存的数据集进行排序。使用max_bytes_before_external_sort,可以允许排序溢出到磁盘。对于在 GROUP BY 之后带有 LIMITORDER BY 的情况,此设置也很有用,尤其是在查询是分布式的情况下。

联接

对于联接,用户可以选择不同的联接算法,这有助于降低所需的内存。默认情况下,联接使用哈希联接,它在功能方面提供了最大的完整性,并且通常具有最佳性能。此算法将联接的右侧表加载到内存中的哈希表中,然后针对该哈希表评估左侧表。为了最大程度地减少内存,用户应将较小的表放在右侧。但是,此方法在内存受限的情况下仍然存在局限性。在这些情况下,可以通过join_algorithm设置启用 partial_merge 联接。这种排序合并算法的派生算法首先将右侧表排序成块并为其创建最小-最大索引。然后,它按联接键对左侧表的部分进行排序,并在右侧表上进行联接。最小-最大索引用于跳过不需要的右侧表块。这在性能方面以牺牲内存为代价,内存消耗较少。进一步采用此概念,full_sorting_merge 算法允许在右侧非常大且不适合内存且查找不可能的情况下执行联接,例如复杂的子查询。在这种情况下,如果左右两侧都不适合内存,则会将其排序到磁盘上,从而允许联接大型表。

sins-11-joins.png

从 20.3 版本开始,ClickHouse 支持 join_algorithm 设置的 auto 值。这指示 ClickHouse应用自适应联接方法,其中优先使用哈希联接算法,直到违反内存限制,此时尝试使用 partial_merge 算法。最后,关于联接,我们鼓励读者了解分布式联接的行为以及如何最大程度地减少其内存消耗——更多信息请参阅此处

恶意查询

内存问题的其他原因是用户不受限制。在这些情况下,我们看到用户发出没有配额查询复杂度限制的恶意查询。如果将 ClickHouse 实例公开给广泛且多样的用户群体,则这些控制对于提供健壮的服务至关重要。我们自己的play.clickhouse.com环境有效地使用这些功能来限制使用并提供稳定的环境。

ClickHouse 最近还引入了新的内存超额分配功能。在过去,查询会受到max_memory_usage设置(默认 10GB)的限制,该设置提供了一个严格且较为粗略的限制。用户可以提高此限制以满足单个查询的需求,但可能会影响其他用户。内存超额分配允许运行更多内存密集型查询,前提是存在足够的资源。当达到最大服务器内存限制时,ClickHouse 会确定哪些查询超额分配最多,并尝试终止该查询。这可能是也可能不是触发此条件的查询。如果不是,则该查询将等待一段时间,以允许终止高内存查询,然后再继续运行。这使得低内存查询始终能够运行,而更密集的查询可以在服务器空闲且资源可用时运行。此行为可以在服务器和用户级别进行调整。

12. 与物化视图相关的问题

物化视图是 ClickHouse 的一项强大功能。通过允许在插入时重新定向和转换数据,用户可以针对特定查询进行优化。我们经常看到用户在需要超过一个主键索引使用此技术。物化视图存在许多常见问题,可能足以单独撰写一篇博文来介绍。总结一下最常见的问题:

  • 我们经常看到用户误解了物化视图的工作原理。它们不知道源表数据,实际上只是插入操作的触发器——仅能够对插入的数据块运行。它们无法看到合并、分区删除或修改操作。因此,如果用户更改了源表,则也必须更新任何附加的物化视图——没有保持这些视图同步的功能。
  • 用户向单个表添加了过多的物化视图。这些视图不是免费的,必须在每次插入时运行。对于一个表来说,超过 50 个物化视图通常是过多的,并且会减慢插入速度。除了计算开销之外,每个物化视图都会从其运行的块中创建一个新的部分——可能会导致前面讨论的“太多部分”问题。请注意,可以通过设置parallel_view_processing来并行运行视图以提高性能。
  • 状态函数是 ClickHouse 的一项引人注目的功能,它允许使用聚合函数对数据进行汇总以供以后查询使用。具有许多此类函数的物化视图,尤其是计算分位数状态的函数,可能会占用大量 CPU 并导致插入速度变慢。
  • 我们经常看到用户将目标聚合/求和合并树的列与物化视图的列不匹配。目标表的 ORDER BY 子句必须与物化视图的 SELECT 子句中的 GROUP BY 一致。下面显示了正确的示例
CREATE MATERIALIZED VIEW test.basic ENGINE = AggregatingMergeTree() PARTITION BY toYYYYMM(StartDate) ORDER BY (CounterID, StartDate) AS SELECT CounterID, StartDate, sumState(Sign) AS Visits, uniqState(UserID) AS Users FROM test.visits GROUP BY CounterID, StartDate;

CREATE MATERIALIZED VIEW test.summing_basic ENGINE = SummingMergeTree PARTITION BY toYYYYMM(d) ORDER BY (CounterID, StartDate) AS SELECT CounterID, StartDate, count() AS cnt FROM source GROUP BY CounterID, StartDate;

  • 与此类似,物化视图的 SELECT 中的列名必须与目标表的列名匹配——不要依赖于列的顺序。使用别名来确保它们匹配。请注意,目标表可以具有默认值,因此视图的列可以是目标表的子集。下面显示了一个正确的示例——请注意需要将count() 作为counter使用别名。
CREATE MATERIALIZED VIEW test.mv1 (timestamp Date, id Int64, counter Int64) ENGINE = SummingMergeTree ORDER BY (timestamp, id) AS SELECT timestamp, id, count() as counter FROM source GROUP BY timestamp, id;

13. 生产环境中的实验功能

在 ClickHouse 中,我们定期发布新功能。在某些情况下,新功能会被标记为“实验性”,这意味着它们将受益于一段时间的实际使用和社区反馈。最终,这些功能会发展到被认为“生产就绪”的程度,或者如果事实证明它们通常没有用处或有其他方法可以实现最初的目标,则会被弃用。虽然我们鼓励用户尝试实验性功能,但我们建议不要围绕它们构建应用程序的核心功能或在生产环境中依赖它们。因此,我们要求用户在ClickHouse Cloud上请求启用这些功能,并了解注意事项和风险。

我们在文档中将所有功能标记为实验性,任何使用都需要用户设置一个设置以启用特定的实验性功能,例如SET allow_experimental_lightweight_delete = true

结论

如果您阅读到这里,那么您应该已经做好了在生产环境中管理 ClickHouse 集群的准备——或者至少避免了许多常见的陷阱!管理具有 PB 级数据的 ClickHouse 集群必然会带来挑战,即使对于最有经验的操作人员也是如此。为了避免这些挑战并仍然体验 ClickHouse 的速度和强大功能,请尝试ClickHouse Cloud立即开始免费试用

分享此帖子

订阅我们的时事通讯

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