简介
在这篇关于异步插入的后续文章中,我们提供了用于监控异步插入的指南和查询。这有助于验证一切是否根据您的设置正常工作。特别是,识别在 发生在 fire and forget 模式下执行的异步插入的缓冲区刷新期间发生的插入错误。
异步插入机制入门
作为提醒,异步插入通过在创建新 part 之前在服务器端缓冲多个(可能很小的)插入来自动控制 part 创建的频率。下图可视化了这一点: 当 ClickHouse ① 接收到异步插入查询时,查询的数据 ② 会立即写入内存缓冲区。与 ① 异步,并且仅当 ③ 下一次缓冲区刷新发生时,缓冲区的 数据将被排序并作为 part 写入数据库存储。在缓冲区刷新之前,来自同一或其他客户端的其他异步插入查询的数据可以收集到缓冲区中。因此,从缓冲区刷新创建的 part 可能会包含来自多个异步插入查询的数据。请注意,可以从单个缓冲区刷新创建多个 part,并且在任何时候可以有多个缓冲区在运行。
相关的系统表
ClickHouse 提供了其自身的 基于 SQL 的可观察性。每个 ClickHouse 节点都在不断监控自身,并将指标、日志、追踪和其他可观察性数据持续写入 系统表。这允许我们简单地使用 SQL 和 ClickHouse 本身来深入了解 ClickHouse 数据处理机制的内部工作原理,例如异步插入。一般来说,系统表是我们核心和支持工程师的首选故障排除工具。
下图列出了当 ClickHouse 接收和执行异步插入时收集可观察性数据的系统表: ① 当 ClickHouse 接收并执行插入查询时,这会与执行统计信息一起记录到 system.query_log 表中。
② 在 system.metrics 表中,您可以检查 PendingAsyncInsert 指标,以获取当前缓冲并等待刷新的异步插入查询的数量。
③ system.asynchronous_insert_log 表记录所有缓冲区刷新事件。
④ 每当创建表 part 时,此事件都会记录在 system.part_log 表中。
⑤ system.parts 表包含有关当前所有现有表 part 的元信息。
在以下部分中,我们将介绍并描述一些关于这些上述系统表的便捷查询,用于内省异步插入的执行阶段。
请注意,这些查询假定在 具有特定名称的集群上执行,通过利用 clusterAllReplicas 表函数。如果您没有使用 ClickHouse 的集群版本,则可以直接从您的单节点查询表。
用于内省异步插入的查询
为了帮助调试,我们提供了一个简单的 脚本,该脚本创建以下所有查询作为便捷的 参数化视图。
Part 创建
请记住,异步插入的主要目的是通过在创建新 part 之前在 ClickHouse 服务器端缓冲多个(可能很小的)插入来控制 part 创建的频率。
这里 是一个关于 system.part_log 表(与 system.parts 连接以获取附加信息)的查询,您可以使用它来双重检查 part 创建的频率。 请记住,异步插入缓冲区中缓冲的表行可能包含几个不同的 分区键 值,因此,在缓冲区刷新期间,ClickHouse 将为每个表分区创建(至少)一个新的 part。
该查询配置了 4 个 CTE 标识符(db_name
、table_name
、last_x_minutes
和 last_m_parts_per_node_per_partition
)。参数化视图 monitor_parts
具有相同的标识符作为参数。
对于特定的数据库和表,该查询列出了最近 x 分钟内每个集群节点 (n
) 和每个表分区 (ptn
) 的最新 m 个新创建的 part 的创建时间 (write
)、包含的 rows
和压缩大小 on disk
。
为了方便起见,我们还显示了 (prev
) 每个 part,自同一集群节点上同一表分区创建上一个 part 以来经过了多少时间。
这是 SELECT * FROM monitor_parts(db_name='default', table_name='upclick_metrics’, last_x_minutes=10, last_m_parts_per_node_per_partition=4)
视图查询的示例结果: 结果显示了最近 10 分钟内每个集群节点(在三节点 ClickHouse Cloud 服务上)和每个表分区的最新 4 个已创建 part。由于我们的 示例目标表 没有使用分区键,因此
ptn
列为空。我们可以看到,在三个集群节点中的每一个节点上,每 30 秒创建一个新的 part。这正是我们在一个基准 运行 中配置异步插入时的配置,缓冲区刷新时间为 30 秒。请记住,缓冲区每个节点都存在。
缓冲区刷新
如果上一个查询的结果没有显示异步插入预期的 part 创建频率,则第一个调试步骤是检查缓冲区刷新的频率。
可以在这里找到一个查询,该查询连接了 system.asynchronous_insert_log 和 system.query_log 表。 请记住,每个集群节点、每个插入查询形状(插入查询的语法,不包括 values 子句/数据)和每个 设置都有一个单独的缓冲区。
该查询配置了 4 个 CTE 标识符(db_name
、table_name
、last_x_minutes
和 last_m_flushes_per_node_per_shape_per_settings
)。参数化视图 monitor_flushes
具有相同的标识符作为参数。
对于特定的数据库和表,该查询列出了有关最近 x
分钟内每个集群节点 (n
) 每个查询形状 (q
) id 每个查询设置 (s
) id 的最新 m
个缓冲区刷新的信息。对于每个缓冲区刷新,我们列出了 flush
时间,rows
的数量以及刷新到磁盘的未压缩 data
量。为了方便起见,我们还显示了 (prev
) 每个缓冲区刷新,自同一节点上同一查询形状和设置的上一次刷新以来经过了多少时间。此外,我们还显示了每个插入查询形状 id 的一个具体 sample_query
和对应于设置 id 的 sample_settings
。
这是 SELECT * FROM monitor_flushes(db_name='default', table_name='upclick_metrics’, last_x_minutes=10, last_m_flushes_per_node_per_shape_per_settings=4)
视图查询的示例结果: 结果显示了最近 10 分钟内每个集群节点(在三节点 ClickHouse Cloud 服务上)以及每个查询形状和设置的最新 4 个缓冲区刷新。我们可以看到,在三个集群节点中的每一个节点上,每 30 秒发生一次缓冲区刷新。这与我们的一个基准 运行 的 30 秒缓冲区刷新 超时相匹配。刷新时间与从上一个查询返回的 part 创建时间相关。请注意,如上面的示例结果所示,所有插入查询都具有相同的形状和设置。
缓冲区刷新期间的插入错误
插入错误可能在缓冲区刷新期间发生。使用默认异步插入返回行为,查询的发送者将收到详细的错误消息,而不是确认。但是,使用 fire and forget 模式,无论稍后缓冲区刷新期间插入目标表是否实际成功,原始插入到该查询的缓冲区都会成功地向发送者确认。
这里 是一个用于事后找出缓冲区刷新期间插入错误的查询。该查询在 system.asynchronous_insert_log 表上运行,并配置了 3 个 CTE 标识符(db_name
、table_name
和 last_x_minutes
)。参数化视图 monitor_flush_errors
具有相同的标识符作为参数。
该查询列出了每个集群节点 (n
) 以及错误 status
和 exception
消息,在过去 x 分钟内发生异常的最新缓冲区 flush
时间。以及缓冲区刷新期间导致插入错误的查询之一的 query_id
。您可以使用此 id 查询(或连接)system.query_log 表,以获取有关特定查询的更多信息。
这是 SELECT * FROM monitor_flush_errors(db_name='default', table_name='upclick_metrics’, last_x_minutes=10)
视图查询的示例结果: 结果显示,在过去 10 分钟内,每个集群节点(在三节点 ClickHouse Cloud 服务上)的两次缓冲区刷新期间发生了一些解析错误。
待处理的刷新
最后,这里 是一个查询,用于检查 system.metrics 表中 PendingAsyncInsert 指标的当前值,以获取每个集群节点当前缓冲并等待刷新的异步插入查询的数量。
这是一个示例查询结果:
场景
在本节中,我们将使用上一节中的一些查询来识别缓冲区刷新和 part 写入频率的根本原因。
简单直接
当目标表中未使用分区键,并且每个插入查询都具有相同的形状和设置时,我们将看到缓冲区根据配置的刷新阈值设置进行刷新,并且缓冲区刷新和写入磁盘的 part 之间存在 1:1 的关系。这每个节点都会发生。以下两个示例查询结果分别用于缓冲区刷新和part 创建查询,证明了这一点: 在三节点 ClickHouse Cloud 服务上,我们在一个基准 运行 中配置了异步插入,缓冲区刷新时间为 30 秒。我们看到每个节点每 30 秒进行一次缓冲区刷新,导致每个节点每 30 秒写入一个 part 到磁盘。请参阅上面两个查询结果中突出显示的行,其中对应的缓冲区刷新和 part 创建使用相同的颜色。
多个分区键
当在目标表中使用分区键时,缓冲区刷新和写入磁盘的 part 之间通常不再存在(每个节点)1:1 的关系。相反,每次缓冲区刷新可以创建多个 part。接下来的两个示例查询结果显示了这一点: 上面两个查询结果中突出显示的行表明,一个节点上的单个缓冲区刷新导致同一节点上同时写入了三个 part。因为当缓冲区刷新时,缓冲区中的行包含三个不同的分区键值。
多个查询形状
因为存在每个插入查询形状(和每个节点)一个单独的缓冲区,所以每次 配置的 缓冲区刷新阈值在单个节点上可能会发生多次缓冲区刷新。以下两个示例查询结果概述了这一点: 我们配置了异步插入,缓冲区刷新时间为 30 秒。如上面的
缓冲区刷新
查询结果中突出显示的两行所示,在下一个缓冲区刷新超时时,同一节点上发生了两次缓冲区刷新。因为该节点接收了针对同一目标表但具有两种不同语法形状的插入查询,所以在该节点上有两个单独的缓冲区。如 写入磁盘的 Parts
查询结果中突出显示的两行所示,缓冲区刷新和写入磁盘的 part 之间仍然存在 1:1 的关系,因为目标表中未使用分区键。
多个设置
与不同的查询形状类似,每个唯一的插入查询设置集都有一个单独的缓冲区。这对于为同一表的数据启用不同的刷新阈值,以及控制特定数据的资源使用非常有用。我们可以在下面观察到这些混合的缓冲区刷新周期: 上面
缓冲区刷新
查询结果中突出显示的两行显示,同一节点上同一目标表存在两个单独且不同的刷新周期。因为该节点接收了针对同一目标表但具有两个不同设置的插入查询,特别是缓冲区刷新 阈值设置,所以在该节点上有两个具有单独刷新周期的缓冲区。 写入磁盘的 Parts
查询结果中突出显示的两行显示,缓冲区刷新和写入磁盘的 part 之间仍然存在 1:1 的关系,因为目标表中未使用分区键。
组合
可能存在以下场景:分区目标表以及针对该表的异步插入具有不同的查询形状和设置。这导致每个集群节点为每个不同的查询形状/设置集运行一个缓冲区。当其中一个缓冲区刷新时,这将为缓冲区中包含的行中存储的每个不同的分区键值创建(至少)一个 part。我们提供的 缓冲区刷新
和 写入磁盘的 Parts
内省查询将正确指示这一点。
总结
在这篇博文中,我们提供了用于内省异步插入的指南和查询。您可以使用提供的查询来双重检查和排除异步插入配置的故障。