简介
在 ClickHouse,我们不断思考我们的入门体验,以及如何帮助用户在最短的时间内从我们的产品中获得价值。虽然大多数用户都能顺利上手,但我们也意识到 ClickHouse 是一款复杂的软件,它为许多人引入了新的概念。再加上大规模管理 ClickHouse 的挑战,这是促使我们开发无服务器 ClickHouse Cloud 解决方案的原因之一,该解决方案自动处理许多常见的入门和后续扩展挑战。
然而,有些问题仅仅是配置错误,或者更常见的是,对 ClickHouse 行为和适当的功能使用理解不足造成的。在这篇文章中,我们重点介绍了我们看到新用户遇到的 13 个最常见的问题,这些问题是由于以反模式使用 ClickHouse,或者仅仅是不遵守最佳使用实践而导致的:也就是 ClickHouse 入门的 13 个致命错误。所有这些都适用于自管理用户,其中一部分仍然与 ClickHouse Cloud 相关。对于每个问题,我们都建议了解决方案或正确的方法。
1. 过多的 part
一个经常看到的 ClickHouse 错误,这通常指向不正确的 ClickHouse 使用方式以及缺乏对最佳实践的遵守。当插入数据时,通常会遇到此错误,并且该错误将存在于 ClickHouse 日志或 INSERT 请求的响应中。要理解此错误,用户需要对 ClickHouse 中 part 的概念有基本的了解。
ClickHouse 中的表由数据 part 组成,这些 part 按用户指定的主键排序(默认情况下,在表创建时使用 ORDER BY 子句,但有关详细信息,请参阅 索引设计)。当数据插入表中时,会创建单独的数据 part,并且每个 part 都按主键进行字典序排序。例如,如果主键是 (CounterID, Date)
,则 part 中的数据首先按 CounterID
排序,然后在每个 CounterID
值内按 Date
排序。在后台,ClickHouse 合并数据 part 以实现更高效的存储,类似于 日志结构合并树。每个 part 都有自己的主索引,以便有效地扫描和识别值在 part 中的位置。当 part 合并时,合并后的 part 的主索引也会合并。
随着 part 数量的增加,查询不可避免地会变慢,因为需要评估更多的索引并读取更多的文件。在 part 计数较高的情况下,用户还可能遇到启动时间缓慢的问题。因此,创建过多的 part 会导致更多的内部合并,并且为了保持较低的 part 数量和较高的查询性能而产生“压力”。虽然合并是并发的,但在误用或配置错误的情况下,part 的数量可能会超过内部可配置的限制[1][2]。虽然可以调整这些限制,但会牺牲查询性能,因此需要这样做通常会指向您的使用模式存在问题。除了导致查询性能下降外,在高 part 计数的情况下,还可能给复制配置中的 ClickHouse Keeper 带来更大的压力。
那么,如何才能拥有过多的这些 part 呢?
错误选择分区键
一个常见的原因是使用具有过高基数的分区键。在创建表时,用户可以指定一列作为分区键,数据将按该键分隔。将为每个键值创建一个新的文件系统目录。这通常是一种数据管理技术,允许用户在逻辑上清晰地分隔表中的数据,例如,按天。诸如 DROP PARTITION 之类的操作随后允许快速删除数据子集。然而,这个强大的功能很容易被误用,用户将其解释为查询的简单优化技术。重要的是,属于不同分区的 part 永远不会合并。如果选择高基数的键,例如 date_time_ms
作为分区键,那么分布在数千个文件夹中的 part 将永远不会成为合并候选对象 - 超过预配置的限制,并在随后的 INSERT 上导致可怕的“非活动 part 过多 (N)。Part 清理处理速度明显慢于插入”错误。解决此问题很简单:选择一个合理的基数 < 1000 的分区键。
许多小插入
除了错误选择分区键外,此问题还可能表现为许多小插入的结果。每次 INSERT 到 ClickHouse 都会导致插入块转换为 part。为了保持 part 数量可管理,用户应在客户端缓冲数据并将数据作为批次插入 - 理想情况下,至少 1000 行,但这应该进行调整。如果客户端缓冲不可行,用户可以通过 异步插入 将此任务推迟到 ClickHouse。在这种情况下,ClickHouse 将在本地磁盘上缓冲插入,然后再将它们合并在一起以插入到基础表中。
Buffer 表 也是这里的另一种选择,但它们对故障的弹性较差,因为它们将插入保存在内存中,直到发生刷新。它们确实比异步插入有一些优势 - 主要是数据在缓冲区中时可以查询,以及它们作为物化视图目标表的缓冲区的兼容性。
过多的物化视图
此错误的其他可能原因是过多的物化视图。物化视图实际上是一个触发器,当块插入表中时运行。它们在将结果插入到不同的表之前转换数据,例如,通过 GROUP BY。这种技术通常用于通过在 INSERT 时预计算聚合来加速某些查询。用户可以创建这些物化视图,这可能会导致许多 part。通常,我们建议用户在创建视图时意识到成本,并在可能的情况下合并它们。
以上列表并非导致此错误的详尽原因。例如,突变(如下所述)也可能导致合并压力和 part 的累积。最后,我们应该注意到,虽然此错误是最常见的,但它只是上述错误配置的一种表现形式。例如,由于分区键不佳,用户可能会遇到其他问题。这些问题包括但不限于“文件系统上没有空闲 inode”、“备份需要很长时间”以及复制延迟(以及 ClickHouse Keeper 上的高负载)。
2. 过早地进行水平扩展
我们经常有新的自管理用户要求我们提供有关编排以及如何扩展到数十甚至数百个节点的建议。虽然 Kubernetes 等技术使部署多个无状态应用程序实例变得相对简单,但在几乎所有情况下,ClickHouse 都不需要这种模式。与其他可能因固有限制(例如,JVM 堆大小)而受限于机器大小的数据库不同,ClickHouse 从一开始就被设计为利用机器的全部资源。我们通常发现成功的部署将 ClickHouse 部署在具有数百个内核、TB 级 RAM 和 PB 级磁盘空间的服务器上。大多数分析查询都有排序、过滤和聚合阶段。这些阶段中的每一个都可以独立并行化,并且默认情况下,将使用与内核一样多的线程,从而为查询利用机器的全部资源。
首先进行垂直扩展有许多好处,主要是成本效益、较低的拥有成本(相对于运营)以及更好的查询性能,因为操作(例如 JOIN)的网络数据最小化。当然,用户需要其基础设施中的冗余,但除了最大的用例之外,两台机器应该足够了。
因此,除了更简单的扩展机制之外,我们更倾向于在考虑水平扩展之前在 ClickHouse Cloud 中自动垂直扩展。总之,先垂直扩展,再水平扩展!
3. 突变之痛
虽然在 OLAP 用例中很少见,但有时修改数据的需求是不可避免的。为了满足此要求,ClickHouse 提供了 突变 功能,该功能允许用户通过 ALTER 查询 修改插入的数据。ClickHouse 在不可变数据上表现最佳,任何需要插入后更新数据的设计模式都应仔细审查。
在内部,突变通过重写整个数据 part 来工作。此过程依赖于与合并相同的线程池。另请注意,默认情况下,突变需要在所有副本上应用 默认情况下。因此,突变既是 CPU 密集型又是 IO 密集型,应谨慎调度,并且运行权限应仅限于管理员。突变造成的资源压力以多种方式表现出来。通常,正常调度的合并会累积,这反过来会导致我们之前提到的“part 过多”问题。此外,用户可能会遇到复制延迟。system.mutations 表应为管理员提供当前计划突变的指示。请注意,可以使用 KILL MUTATION 查询取消突变,但不能回滚突变。
去重
我们经常看到用户需要调度合并,因为存在重复数据。通常,我们建议用户在上游解决此问题,并在插入 ClickHouse 之前进行去重。如果不可能这样做,用户有多种选择:在查询时去重或使用 ReplacingMergeTree。
在查询时去重可以通过按唯一标识行的字段对数据进行分组,并使用 argMax 函数与日期字段一起使用来识别其他字段的最后一个值来实现。ReplacingMergeTree 允许在合并时对具有相同排序键(ORDER BY 键)的行进行去重。请注意,这只是“尽力而为”:有时 part 不会与在非确定性间隔调度的合并过程合并。因此,它不能保证没有重复项。用户还可以使用 FINAL 修饰符来强制在 SELECT
时进行去重(同样,谨慎使用,因为它资源密集型且可能很慢,尽管 最近进行了改进)或通过 OPTIMIZE FINAL 强制在磁盘上合并。
如果需要从 ClickHouse 中删除数据,例如,出于合规性或去重原因,用户还可以使用轻量级删除而不是突变。这些操作采用 DELETE 语句 的形式,该语句接受 WHERE 子句来过滤行。这仅将行标记为已删除。这些标记将在查询时用于过滤出行,并在合并 part 时删除。
注意:此功能是实验性的,需要设置 SET allow_experimental_lightweight_delete = true;
。在大多数情况下,它比使用突变更有效,但大规模批量删除除外。
4. 不必要地使用复杂类型
除了支持常用的原始类型外,ClickHouse 还丰富地支持复杂类型,例如 Nested、Tuple、Map,甚至 JSON。支持这些类型是有充分理由的 - 有时,没有其他方法可以对数据进行建模,但我们建议尽可能使用原始类型,因为它们提供最佳的插入和查询时间性能。
例如,我们最近看到用户热衷于利用 ClickHouse 22.4 中添加的 JSON 功能。这个强大的功能允许从数据中动态推断表架构,从而避免用户指定列类型。谨慎使用此功能,而不是将其作为避免显式指定列的替代方法。具体而言,此功能具有用户应注意的限制
- 由于需要动态创建列,因此插入时的成本增加
- 次优的类型使用,即,没有编解码器和不必要地使用 Nullable。
- 无法在主键中使用 JSON 列
最后两个问题总是会导致更差的压缩和查询/插入性能。与其将此特定类型用于所有行,不如将其用于选择性列,例如,Kubernetes 标签,其中数据可能会发生更改。总之,如果您知道您的架构...请指定它!
注意:JSON Object 类型是实验性的,并且正在改进。我们对该功能的建议正在不断发展,因此可能会在以后的版本中发生变化。
此外,我们经常看到用户使用 Nullable 类型。这允许将值 Null 与类型的默认值区分开来。这可能很有用,但需要一个额外的 Uint8 列来确定哪些值为空。这相对于存储而言,每个值会产生一个额外的字节(尽管它压缩得很好),并且还会增加查询时间开销。仅在您真正需要时才使用 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 时,它会创建 一个或多个 块(part)。在复制环境中,例如 ClickHouse Cloud,哈希也写入 ClickHouse Keeper。后续插入的块将与这些哈希进行比较,如果存在匹配项,则会被忽略。这很有用,因为它允许客户端在未收到 ClickHouse 的确认时(例如,由于网络中断)安全地重试插入。这要求块是相同的,即大小相同,行相同且顺序相同。这些哈希仅存储最近 100 个块,尽管可以 修改。请注意,由于需要进行更多比较,因此较高的值会减慢插入速度。
可以通过设置 non_replicated_deduplication_window
为非复制实例启用相同的行为。在这种情况下,哈希存储在本地磁盘上。
6. 糟糕的主键选择
ClickHouse 的新用户经常难以完全理解其独特的主键概念。与基于 B(+)-Tree 的 OLTP 数据库(针对快速定位特定行进行了优化)不同,ClickHouse 利用稀疏索引,该索引专为每秒数百万行的插入和 PB 级数据集而设计。与 OLTP 数据库相比,此索引依赖于磁盘上排序的数据,以便快速识别可能与查询匹配的行组 - 这是分析查询中的常见要求。实际上,该索引允许在将 part 文件的匹配部分流式传输到处理引擎之前快速识别它们。有关磁盘上数据布局的更多详细信息,我们强烈 推荐本指南。
对于查询性能和压缩,此方法的有效性取决于用户在创建表时通过 ORDER BY 子句选择良好的主键列。通常,用户应选择他们经常用于过滤表的列,很少需要超过 2 到 3 列。这些列的顺序至关重要,并且会影响按主键列以外的列进行压缩和过滤。为了有效地过滤查询中的二级键列以及表的列文件的压缩率,最佳做法是将主键中的列按其基数升序排列。有关推理的完整说明,请参见 此处。
7. 过度使用数据跳过索引
当需要加速查询时,主键理所当然地成为用户首先使用的工具。但是,表仅限于单个主键,并且查询访问模式可能会使主键失效,即,对于不同的用例,不可避免地会出现无法有效利用主键的查询。在这些情况下,当应用 WHERE 子句条件时,ClickHouse 可能会被迫对每个列执行全表扫描。通常,这仍然足够快,但在某些情况下,用户会使用 数据跳过索引,希望轻松加速这些查询。
这些索引添加了数据结构,使 ClickHouse 可以跳过读取保证没有匹配值的大量数据块。更具体地说,它们在 块粒度(实际上是标记)上创建索引,如果 WHERE 子句不满足条件,则允许跳过这些粒度。
在某些情况下,这些索引可以加速特定查询,但通常被过度使用,不直观,并且 需要仔细设计才能有效。因此,我们经常看到它们只是使表设计复杂化并减慢插入性能 ,同时很少(甚至从不)提高查询性能。我们始终鼓励用户阅读概念和 最佳实践。
在大多数情况下,只有在其他替代方案都已用尽后才应考虑跳过索引 - 特别是,只有在研究了其他替代方案后,例如修改主键(请参阅 创建其他主键索引的选项),使用投影或物化视图,才应使用此高级功能。通常,只有在主键与目标非主键列/表达式之间存在强相关性时,才应考虑跳过索引。在没有任何实际相关性的情况下,跳过索引将匹配大多数块 - 导致所有粒度都被读取到内存并进行评估。在这种情况下,索引成本已经产生,但没有任何好处,实际上减慢了全表扫描。
8. LIMIT 并不总是短路 + 点查找
我们经常发现刚接触 ClickHouse 的 OLTP 用户使用 LIMIT 子句来优化查询,方法是限制返回的结果数量。如果来自 OLTP 数据库,这应该直观地优化查询:返回的数据越少 = 结果越快,不是吗?是也不是。
此技术的有效性取决于查询是否可以完全以 流式方式 运行。某些查询,例如 SELECT * FROM table LIMIT 10
将仅扫描前几个 part 的几个 粒度,然后在达到 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,以避免令人沮丧的连接被拒绝错误。
10. 只读表
虽然在 ClickHouse Cloud 中不是问题,但只读表在自管理集群中仍然会抬头。当节点失去与 ZooKeeper 的连接时,就会发生这种情况。这通常几乎总是 ZooKeeper 问题的结果。虽然与 ZooKeeper 相关的许多挑战已通过 ClickHouse Keeper 的发布得到解决,但此组件的资源不足仍然可能导致此问题显现出来。常见的原因包括在生产环境中将 Keeper 托管在与 ClickHouse 相同的主机上,或者 ZooKeeper JVM 资源调整不佳。这通常很容易通过确保此组件在专用硬件上分离并提供足够的资源来解决。
11. 查询超出内存限制
对于新用户来说,ClickHouse 通常看起来像魔法一样 - 即使在最大的数据集和最雄心勃勃的查询上,每个查询都超级快。但实际上,真实世界的使用情况会测试 ClickHouse 的极限。查询超出内存可能是多种原因造成的。最常见的是,我们看到对高基数字段进行大型连接或聚合。如果性能至关重要,并且需要这些查询,我们通常建议用户简单地向上扩展 - ClickHouse Cloud 可以自动且毫不费力地做到这一点,以确保您的查询保持响应速度。但是,我们理解,在自管理场景中,这有时并非易事,甚至可能不需要最佳性能。在这种情况下,用户有几个选择。
聚合
对于内存密集型聚合或排序场景,用户可以使用设置 max_bytes_before_external_group_by
和 max_bytes_before_external_sort
。前者已在 此处 进行了广泛讨论。总而言之,这确保了如果超过内存阈值,任何聚合都可以“溢出”到磁盘。这将不可避免地影响查询性能,但将有助于确保查询不会 OOM。后者的排序设置有助于解决内存密集型排序的类似问题。这在分布式环境中尤其重要,在分布式环境中,协调节点接收来自子分片的排序响应。在这种情况下,可能会要求协调服务器对大于其可用内存的数据集进行排序。使用 max_bytes_before_external_sort
,可以允许排序溢出到磁盘。此设置对于用户在带有 LIMIT
的 GROUP BY
之后具有 ORDER BY
的情况也很有用,尤其是在查询是分布式的情况下。
JOIN
对于连接,用户可以选择不同的 JOIN 算法,这可以帮助降低所需的内存。默认情况下,连接使用哈希连接,哈希连接在功能完整性方面提供了最大的完整性,并且通常具有最佳性能。此算法将 JOIN 的右侧表加载到内存中的哈希表中,然后针对该哈希表评估左侧表。为了最大限度地减少内存,用户应将较小的表放在右侧。但是,这种方法在内存受限的情况下仍然存在局限性。在这些情况下,可以通过 join_algorithm
设置启用 partial_merge
连接。这种 排序-合并算法 的变体,首先将右表排序成块,并为其创建 min-max 索引。然后,它按连接键对左表的部分进行排序,并在右表上连接它们。min-max 索引用于跳过不需要的右表块。这以性能为代价降低了内存密集度。更进一步,当右侧非常大且不适合内存并且无法查找时(例如,复杂的子查询),full_sorting_merge
算法允许执行 JOIN。在这种情况下,如果左右两侧都不适合内存,则在磁盘上对它们进行排序,从而允许连接大表。
自 20.3 版本以来,ClickHouse 支持 join_algorithm
设置的 auto
值。这指示 ClickHouse 应用自适应连接方法,其中哈希连接算法是首选,直到违反内存限制,此时尝试 partial_merge 算法。最后,关于连接,我们鼓励读者注意分布式连接的行为以及如何最大限度地减少其内存消耗 - 更多信息请参见 此处。
恶意查询
内存问题的其他原因是没有限制的用户。在这些情况下,我们看到用户发出恶意查询,没有 配额 或 查询复杂性限制。如果将 ClickHouse 实例暴露给广泛且多样化的用户,这些控制对于提供强大的服务至关重要。我们自己的 play.clickhouse.com 环境有效地使用了这些来限制使用并提供稳定的环境。
ClickHouse 最近还引入了新的 内存过载提交功能。从历史上看,查询将受到 max_memory_usage
设置(默认为 10GB)的限制,这提供了一个硬性且相当粗糙的限制。用户可以提高此限制,但以牺牲单个查询为代价,这可能会影响其他用户。内存过载提交允许运行更多内存密集型查询,前提是有足够的资源。当 达到最大服务器内存限制时,ClickHouse 将确定哪些查询过度提交最多,并尝试终止该查询。这可能是也可能不是触发此条件的查询。如果不是,则查询将等待一段时间,以允许终止高内存查询,然后再继续运行。这允许低内存查询始终运行,而更密集的查询可以在服务器空闲且资源可用时运行。可以在 服务器和用户 级别调整此行为。
12. 与物化视图相关的问题
物化视图是 ClickHouse 的一项强大功能。通过允许在插入时重新定向和转换数据,用户可以针对特定查询进行优化。我们经常看到用户在需要多个 使用此技术,当需要超过一个 主索引 时。物化视图存在许多常见问题,可能足以单独写一篇博客文章。总结最常见的
- 我们经常看到用户误解物化视图的工作方式。他们不了解源表数据,并且实际上只是插入时的触发器 - 只能在插入的数据块上运行。它们看不到合并、分区删除或突变。因此,如果用户更改源表,他们还必须更新任何附加的物化视图 - 没有功能可以使这些视图保持同步。
- 用户向单个表添加了过多的物化视图。这些视图不是免费的,必须在每次插入时运行。对于一个表来说,超过 50 个物化视图通常是过度的,并且会减慢插入速度。除了计算开销之外,每个物化视图都将从其运行的块创建一个新部分 - 可能会导致前面讨论的“Too Many Parts”问题。请注意,可以通过设置
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 并立即开始免费试用。