许多从事数据流工作的工程师都不是 SQL 专家。所以你可能会问自己:什么是 CTE?更重要的是,什么是 CTE 查询,它们有什么用,以及它们如何帮助你处理 Debezium?
在本文中,我们将回答这些问题,探讨 Debezium Oracle 连接器如何利用 CTE 查询,并讨论涉及的优点和权衡。
什么是通用表表达式 (CTE)?
通用表表达式 (CTE) 是 SQL 标准中引入的、已有 20 多年历史的 SQL 功能。它允许你在查询中定义一个临时的、命名的结果集——这个结果集仅存在于查询的生命周期内,并且不会存储在数据库中。
CTE 通过将复杂的查询分解为逻辑清晰、可读性强的构建块,帮助你编写更简洁、更易于维护的 SQL 代码——这就像用简单、设计良好的组件来构建一个复杂的结构一样。
实际上,CTE 可以作为视图的替代方案,有时还可以通过避免重复计算来提高性能,尤其是在涉及大型连接或嵌套逻辑的查询中。
CTE 查询在 Debezium 中解决的问题
Debezium 必须跟踪每个事务的详细元数据:事务何时开始、谁发起的、何时结束。数据库使用诸如 START 或 BEGIN(事务开始)和 COMMIT 或 ROLLBACK(事务结束)之类的标记来表示这些边界。
对于 Debezium Oracle 连接器来说,这些标记至关重要。该连接器以未提交模式从 Oracle 事务日志中消耗更改,这对数据库负载来说效率很高,但这意味着 Debezium 会接收所有更改——包括那些后来被回滚的事务中的更改。为了处理这种情况,该连接器使用事务缓冲区来存储进行中的事务,直到遇到 COMMIT 或 ROLLBACK。
目前,用户可以将 log.mining.query.filter.mode 设置为将表包含/排除过滤器推送到 Oracle LogMiner。这通常可以提高性能,因为它减少了通过网络发送到 Debezium 的数据量。然而,此过滤器仅适用于 DML(例如,插入、更新、删除)和 DDL(模式更改)——而不适用于事务标记。
在低活动数据库中,少量事务标记(START、COMMIT、ROLLBACK)通常不是问题。但在高流量环境中——想象一下每天数百万次更改,而只捕获少量表——事务标记可能会主导数据流,造成不必要的处理和网络开销。
这就是 CTE 查询发挥作用的地方。通过在数据库级别执行预过滤传递,CTE 查询可以将事务标记限制在仅包含与你包含/排除列表中的相关 DML 或 DDL 事件的事务。
要求和启用 CTE 支持
要启用 Oracle 连接器的 CTE 查询功能,请在你的连接器配置中设置以下内容:
-
internal.log.mining.use.cte.query— 启用该功能(目前处于实验阶段)。 -
log.mining.query.filter.mode— 必需,以便将包含/排除谓词推送到数据库查询。
| 如果 |
优点
启用 CTE 查询功能后,Oracle LogMiner 查询会对事务日志执行一次有针对性的预传递,以生成一个内存中的 CTE,其中仅包含涉及包含/排除列表中的表的事务标识符 (XID)。然后,此临时表用于过滤主查询,从而使 Debezium **只处理重要的事务**。
这种方法提供了几个关键优势:
- 降低网络负载
-
没有 CTE 过滤,Debezium 会接收所有事务标记(
START、COMMIT、ROLLBACK),即使这些事务与你捕获的表无关。在高流量数据库中,这些标记可能占通过网络发送的事件的大部分。通过在源头过滤它们,你只传输相关的标记,从而减少网络流量。 - 降低 Debezium CPU 使用率
-
连接器不再浪费 CPU 周期来解析、缓冲和丢弃不包含任何相关更改的事务的事务标记。当你的 Oracle 实例有大量短暂事务或针对未捕获表的事务时,这一点尤其重要。
- 提高内存效率
-
在未提交模式下,Debezium 会缓冲所有进行中的事务,直到遇到
COMMIT或ROLLBACK。尽管包含/排除过滤器可以防止缓冲非捕获表的事件,但事务元数据仍会被捕获。使用 CTE 查询,消耗的事务标记更少,这意味着事务元数据的内存占用更小,从而减轻了许多短暂事务的垃圾回收压力。 - 更高的吞吐量和更低的延迟
-
由于需要处理的事件更少,连接器在高峰负载下可以更容易地保持同步,从而改善了端到端事件到 Kafka 和下游消费者的交付时间。
- 最小的配置复杂度
-
该功能建立在现有的
log.mining.query.filter.mode包含/排除规则之上。如果你已经在连接器配置中设置了此项,启用 CTE 支持只需要一项额外的配置更改。 - 数据库端优化
-
通过将过滤工作卸载到数据库引擎,你可以利用 Oracle 本身针对基于集合的操作进行的优化执行路径。数据库在数据离开服务器之前高效地扫描和过滤数据,这通常比在 Debezium 的 JVM 进程中处理这些操作更有效。
在实际工作负载中——特别是那些每天有数百万个事务且捕获范围相对较小的场景——这些综合优势可以显著减轻基础设施负载,提高在突发流量下的稳定性,并允许 CDC 管道进行更可预测的扩展。
缺点
与大多数优化一样,CTE 查询功能也伴随着权衡,这些权衡应与你的环境仔细进行评估。
当启用该功能时,CTE 表仅包含一个唯一的事务标识符列表(一个 RAW(8) 列)。由于此表未直接连接到主查询的结果集中,Oracle 必须对每个传递进行两次事务日志扫描
- 第一次传递
-
构建仅涉及已捕获表的事务 ID 的 CTE。
- 第二次传递
-
使用生成的事务 ID 列表来过滤并返回相关的事务事件。
这种双重传递方法会增加数据库服务器的 I/O 负载。在日志挖掘 I/O 已接近极限的系统上——尤其是在具有大量重做日志的大型 Oracle 实例上——这种额外的读取开销可能会影响挖掘吞吐量或整体系统性能。
其他注意事项包括:
- 数据库服务器上可能更高的 CPU 使用率
-
额外的过滤和 CTE 物化需要额外的 CPU 周期,这在资源受限的环境中可能会变得明显。
- 在低流量或全捕获环境中获益较少
-
如果你已经捕获了大多数表或事务流量很小,那么过滤带来的好处可能微不足道,但仍然会产生额外的 I/O 成本。
- 日志挖掘查询过滤器要求
-
该功能要求显式使用
log.mining.query.filter.mode设置为in。如果你当前使用的是regex,则此功能目前不受支持,无法使用。
在大多数高流量、针对性捕获的部署中,在网络和 Debezium 端处理方面降低的性能优势大于数据库上的 I/O 成本。但是,在启用该功能后,衡量和监控你的环境以确保权衡对你的工作负载有利非常重要。
关于 Debezium
Debezium 是一个开源的分布式平台,可以将现有数据库转变为事件流,使应用程序能够几乎即时地看到并响应数据库中已提交的每个行级更改。Debezium 构建在 Kafka 之上,并提供了 Kafka Connect 兼容的连接器,用于监控特定的数据库管理系统。Debezium 将数据更改的历史记录在 Kafka 日志中,这样您的应用程序可以随时停止和重新启动,并可以轻松地消费在未运行时错过的所有事件,确保所有事件都被正确且完整地处理。Debezium 在 Apache 许可证 2.0 下是 开源 的。