这篇博文是三部分系列文章的最后一部分,旨在探讨如何使用 Debezium 通过 Oracle LogMiner 从 Oracle 数据库摄取更改。如果您错过了,该系列的第一部分可以在 这里 找到,第二部分可以在 这里 找到。
在这第三也是最后一部分中,我们将在前两篇文章的基础上,重点关注以下领域:
开始之前要知道什么
本期将重点介绍一些高度技术的 Oracle 数据库功能,这些功能通常属于 Oracle 数据库管理员的专业领域。根据您在环境中拥有的权限和角色,您可能无法访问此处讨论的所有工具和命令,因此在非本地环境中工作时,您可能需要与数据库管理员协调。
此外,本博文接续我们上次在 第二部分 的内容。我们之前已部署了一个完整的 Kafka 生态系统,包括 Zookeeper、Kafka Broker 和 Kafka Connect 环境。这些并不是本系列文章的必要要求,但如果您想手动跟进,我鼓励您按照本系列早期部分的步骤操作,以便快速上手。
什么是 Oracle?为何它如此复杂?
我在 第一部分 中简要提及过这一点,但我认为有必要更深入地解答,以便为我们今天将要讨论的一些技术概念提供支持。
新接触者或不熟悉 Oracle 的用户常常会问:“为什么 Oracle 的所有东西总是那么复杂?”大多数情况下,这些用户要么 Oracle 数据库经验很少,要么接触过其他开源数据库解决方案,而这些解决方案(从广义上讲)更易于使用,尤其是在开箱即用的情况下。那么,这是为什么呢?
Oracle 于 40 多年前(1979 年)首次公开发布,使其成为世界上最古老、最受欢迎的数据库。在前五大最受欢迎的数据库中,Microsoft SQL Server 是次古老的,已有三十年历史,于 1989 年发布,而其他数据库的年龄都只有 Oracle 的一半或更短。
驱动 Oracle 市场份额增长的因素在于其快速创新的能力、保留与现有数据库平台用户的兼容性,以及足够灵活地提供您未来可能需要的现有功能。这使得 Oracle 能够像其竞争对手一样蓬勃发展,但我们都知道,灵活性往往要以牺牲其他东西为代价;而传统上,代价就是易用性。Oracle 拥有大量工具可供您使用,但这些工具通常紧密耦合,导致安装和配置复杂,但其灵活性之外的优势在于,它在其擅长的领域是最好的,所以权衡通常是值得的。
当我们继续阅读本系列的后续部分时,我希望这些背景能为您提供新的视角。虽然与竞争对手相比,Oracle 的许多怪异之处可能被视为痛点,但实际上,它们是使 Oracle 在大数据时代的关键领域始终处于领先地位的优势。
性能
在选择使用变更数据捕获 (CDC) 管道时,低延迟通常是驱动因素。无论您是使用 CDC 来为微服务之间提供事件驱动的通信,还是进行复杂的数据复制,事件的尽早到达都非常重要,因此吞吐量和性能通常是评估解决方案价值的首要因素,通常紧随可靠性之后。
在本节中,我们将介绍 Debezium Oracle 连接器的几个配置属性,这些属性可以让您根据环境和需求优化连接器的性能。
磁盘 IO
Debezium Oracle 连接器实现的一个基本方面是,它使用 Oracle API 与 LogMiner 进程协同工作,以读取重做日志和归档日志。这些日志由两个 Oracle 进程管理:Oracle LogWriter (LGWR) 和 Oracle Archiver (ARCH)。这两个进程的详细信息对于讨论不如它们对 Oracle 数据库当前重做日志的管理以及包含对 Oracle 所做历史更改的归档日志的创建负责的事实重要。
Debezium Oracle 连接器使用 LogMiner API 从磁盘读取这些重做日志和归档日志,并生成变更事件。不幸的是,重做日志和归档日志不能无限期地保留在数据库服务器上。通常,日志可能高达几 GB,再加上补充日志配置,Oracle 数据库可以在短时间内生成大量日志,因此磁盘空间会很快被消耗。当这些重做日志或归档日志位于网络驱动器或高延迟设备上时,这对 LogMiner 快速读取和向连接器提供变更事件数据产生了直接影响。
一种可能的提高性能的方法是与您的 Oracle 数据库管理员协调,看看是否可以更长时间地保留更多归档日志,即使它们位于数据库机器本地但另一个挂载的磁盘上。Oracle 提供了定义所谓 日志归档目标 的功能,并且最多支持 31 个不同的路径,归档进程可以将归档日志写入这些路径。
您的数据库管理员可能已经为 GoldenGate、DataGuard 等其他进程配置了多个日志归档目标,这并不罕见。如果已经定义了这些路径,并且它们具有与摄取速率一致的日志保留策略,那么如果其中一个已存在,您就可以安全地将 Debezium 与这些目标一起使用。如果没有定义路径,您可以按照上述 Oracle 文档链接创建一个新路径。
要使 Debezium 使用特定的日志归档目标,必须提供 log.mining.archive.destination.name 连接器属性。
{
"log.mining.archive.destination.name": "LOG_ARCHIVE_DEST_5",
...
} | 日志的物理路径将直接从 Oracle 获取,您只需要指定数据库管理员配置的目标名称。 |
| 此配置仅适用于 Oracle LogMiner 适配器,在使用 Oracle XStream 摄取更改时没有影响。 |
重做日志大小
Oracle 的重做日志主要用于恢复和实例故障。当实例首次创建时,管理员会提供重做日志的起始大小。如果重做日志的大小太小甚至太大,这都会对实例的性能产生直接影响。
例如,重做日志的大小直接影响 Oracle Archiver (ARCH) 进程转换为归档日志的频率,这被称为日志切换。通常,Oracle 建议管理员尽量减少在短时间内发生日志切换的次数,但这可能因音量或日志记录配置等多种因素而异。
日志切换是一个相当昂贵的操作,因为它是 Archiver 进程将重做日志复制到归档日志并分配新重做日志的时刻。如果 Archiver 进程出现落后,并且所有重做日志都已填满,Oracle 数据库的性能可能会下降甚至停止,如果无法发生检查点,因为所有当前重做日志都已填满并等待归档。
如果您使用的是基于 Oracle Docker Images 的 Oracle 镜像,您会注意到默认创建的重做日志非常小,每个只有几兆字节。对于开发目的,这开箱即用是可以的,但当将这样的实例用于任何类型的严肃集成(如 Debezium)时;这根本不起作用,尤其是在我们将在下一节中更详细讨论的默认挖掘策略下。
然而,小重做日志大小并非唯一问题。如果重做日志文件的大小过大,这会不利地影响从磁盘读取时间,使连接器等待更改的间隔更长,因为需要由于文件较大而执行更多的磁盘 IO。
| 调整 Oracle 重做日志的大小需要对数据库服务器路径以及安全存储这些文件的位置有现有知识;因此,由于这些信息是环境相关的,我们在这里不直接介绍如何执行此操作。Oracle 提供了出色的 文档 来介绍如何执行此任务。 |
不幸的是,对于应该使用什么大小,没有简单的答案。这需要一些技巧、科学和启发式方法来衡量环境的最佳选择,但这可以在必要时进行调整的武器库中。
日志挖掘策略
在 第二部分 中,我们介绍了 Debezium Oracle 连接器的两种 日志挖掘策略。这些策略控制连接器如何与 Oracle LogMiner 交互,以及如何为模式和表更改摄取重做日志中的特定条目。
重做日志存储重做条目,并非所有重做条目都明确存储了重建发生的更改所需的所有数据。例如,DML 操作(INSERT、UPDATE、DELETE)不引用表或列名,而是对象标识符。这些对象标识符和版本详细信息在数据字典中随着列或表修改(DDL 更改)的发生而变化。这意味着同一表在模式更改之前和之后的重做条目的标识符和/或其版本将不同。
日志挖掘策略精确控制 Oracle LogMiner 如何解释重做条目,主要通过将数据字典写入重做日志末尾或省略此步骤。使用任一策略都有其优点,我们将深入探讨这些优点以及您为什么可能选择一种策略而非另一种。
- 默认挖掘策略
-
默认挖掘策略是最安全的选择,但也是最昂贵的。此策略将在观察到日志切换时将数据字典的副本附加到重做日志。
此策略的主要优点是模式和数据更改会被 Oracle LogMiner 无缝摄取。换句话说,如果一个INSERT操作后跟一个ALTER TABLE操作,然后再跟一个UPDATE操作,Oracle LogMiner 将能安全地从旧的和新的对象 ID 和版本推断出正确的表名和列名。这意味着 Debezium 将能够安全地摄取该变更事件,正如人们所期望的那样。
此策略的缺点是,每次日志切换都是一个昂贵的操作。
首先,它要求连接器定期将数据字典的副本附加到重做日志,并且 Oracle 在写入字典后执行完整的日志切换(所有日志组都执行切换)。这意味着与使用我们稍后将讨论的在线目录策略相比,将生成更多的归档日志。
其次,它还要求当 LogMiner 进程开始挖掘重做日志时,它必须首先读取并准备 SGA 的一个部分,其中包含所有字典元数据,以便正确解析表名和列名。根据重做日志的大小,以及更恰当地说,日志的字典段的大小,这可能需要几分钟才能准备好。所以您可能可以猜到,当您将此策略与大小不当的重做日志结合时,很容易造成性能瓶颈。
| **不建议**在生产环境中部署多个使用此策略的 Oracle 连接器,而应使用单个 Oracle 连接器。 |
- 在线目录策略
-
当使用
online_catalog值指定log.mining.strategy连接器配置属性时,将使用在线目录挖掘策略。与默认挖掘策略不同,此策略不会将任何额外数据写入重做日志,而是依赖当前数据字典来解析表名和列名。
此策略的优点在于,由于我们不将任何字典详细信息写入重做日志,重做日志仅根据现有的数据库活动转换为归档日志。简而言之,Debezium 不会超出所需的额外补充日志配置来影响此频率,从而更容易管理创建的归档日志的卷。其次,由于没有将字典详细信息写入日志,并且日志切换次数与现有行为保持不变,因此挖掘会话几乎瞬时启动,并且 LogMiner 不需要准备任何字典元数据,因为现有数据字典已满足该要求。
不幸的是,此策略有一个限制,那就是模式更改不会被无缝观察到。换句话说,如果重做条目引用了一个与在线数据字典中的对象 ID/版本不匹配的对象 ID/版本,Oracle LogMiner 将无法重构该操作的 SQL。
但是,可以使用此策略处理模式更改,但这需要以锁定步骤的方式进行模式更改。换句话说,您将停止对表的更改,等待 Debezium 捕获完该表的最后一个更改,应用模式更改,等待 Debezium 发出模式更改,最后恢复对表中数据的更改。
| 此策略为 Oracle 和连接器都提供了最佳性能提升。 唯一的要求是,如果表的模式不是静态的,并且您可能会对其进行周期性更改,如果您能按照上述描述以锁定步骤执行模式更改,那么您就可以安全地使用此策略进行模式更改;否则,应避免在正在捕获的表上进行模式更改。 最后,如果您在同一个 Oracle 数据库上部署了多个 Oracle 连接器,则应使用此策略。 |
总之,所选择的挖掘策略可能对数据库性能以及 Debezium Oracle 连接器的摄取速率产生重大影响。根据您的环境所允许的情况,权衡此选择的优点和缺点非常重要。
| 目前正在努力弥合这两种策略,以提供一种解决方案,该解决方案兼具在线目录策略的所有性能优势以及默认挖掘策略提供的无缝模式管理。有关此工作的进展,请参见 DBZ-3401。 |
批次大小
Debezium Oracle 连接器使用自适应批次大小算法来确定每次数据库调用要获取的行数。该算法由以下配置属性控制:
log.mining.batch.size.default-
指定每次数据库调用要获取的默认行数。
log.mining.batch.size.min-
指定每次数据库调用要获取的最小数据库行数。
log.mining.batch.size.max-
指定每次数据库调用要获取的最大数据库行数。
这些设置使连接器能够读取更多数据并降低网络延迟,尤其是在连接器滞后或在日志中遇到大事务时,代价是暂时消耗更多 SGA 和 JVM 内存,而在连接器接近实时更改时消耗更少的 SGA 和 JVM 内存。
连接器的默认值是很好的起点,但根据您的变更事件量,明智的做法是根据您的环境增加或减少这些设置,以提高性能。
查询过滤器模式
任何从事过 SQL 应用程序开发的开发人员都会告诉你,仅仅因为一个查询在某个环境中或某个时间点表现良好,并不意味着在另一个环境或未来随着数据集的变化,同一个查询会同样高效。这就是为什么在 Debezium 2.3 中,我们添加了一个名为 log.mining.query.filter.mode 的新功能。通过与拥有各种安装、数据量和集成的 Oracle 社区用户进行各种讨论,我们得出结论,Oracle 连接器使用的 LogMiner 查询根本无法成为“一刀切”的解决方案。
为了实现最高效率,我们需要为用户社区提供一种方法,让他们可以调整 LogMiner 查询,以最好地满足他们的配置和环境。目前有三种构建 LogMiner 查询的方式,每种都会影响 WHERE 子句的生成。
none-
指定不对 LogMiner 查询添加额外的谓词。
相反,所有过滤主要委托给 Oracle 连接器的 Java 运行时,而不是数据库查询。这具有所有选项中最高的网络带宽使用量,并且根据重做条目的数量和数据集,吞吐量最高。对于较低数量的安装,这可以轻松地最快执行,但当重做条目的数量增加或感兴趣的数据集小于数据集总计时,则扩展性不佳。 in-
指定使用 SQL IN 子句应用模式和表包含/排除过滤器。
默认情况下,包含/排除配置选项支持逗号分隔的正则表达式列表;但是,如果您选择避免使用正则表达式,则可以使用此查询过滤器模式更有效地应用数据库级别的过滤器。与使用析取或 Oracle 的REGEXP_LIKE运算符(我们将在下一个选项中讨论)相比,IN 子句效率更高。如果您在配置中定义了大量模式或表包含/排除列表选项,这也表现得非常出色。最后,由于此选择执行数据库级别的过滤,因此可以减少网络延迟,并且只将必要的行返回给连接器。 regex-
指定使用 SQL 运算符
REGEXP_LIKE应用模式和表包含/排除过滤器。
由于包含/排除配置选项支持逗号分隔的正则表达式列表,因此在使用正则表达式时必须使用REGEXP_LIKE而不是IN。虽然此选项执行数据库级别的过滤,就像 IN 子句一样,但随着连接器配置中指定更多包含/排除选项,正则表达式的性能会下降。因此,为了最大化性能,在使用正则表达式时,通常最好编写尽可能少的表达式来匹配最多的表或模式,以减少添加到查询的谓词数量。
| 从 Debezium 2.3 开始,默认值为 |
调试
尽管我希望软件工程是美好的,但这与事实相去甚远,管理运行软件的环境也无例外。当问题发生时,具备自助诊断并尽快恢复运行状态的知识非常重要。因此,我们将深入探讨,讨论一些我们常见的错误、如何调试这些错误以及潜在的修复方法。
- 日志文件中不包含偏移 SCN,需要重新快照
-
我相当确定,至少有一些 Oracle 连接器用户在 PoC 设计或测试期间(但希望不是在生产环境中)会遇到此错误。错误消息本身相对清晰,但通常不清楚的是“为什么会发生这种情况”。
对于其他数据库,它们的事务日志只包含已提交的更改,然后由 Debezium 消耗。不幸的是,Oracle 不会这样做,而是将每一次更改都写入事务日志,即使该更改后来因约束冲突或用户/系统显式回滚而被撤销。这意味着从 Oracle 重做日志和归档日志中读取更改并不像从位置 X 读取到文件末尾,然后以下一个日志为序列重复那样简单。相反,连接器必须维护我们称之为低水位标记 SCN 和高水位标记 SCN,或者如果您熟悉连接器的偏移量,这些表示为scn和commit_scn。
低水位标记或scn代表重做日志中的安全恢复点。通常,这指向日志中最旧的正在进行的事务开始的位置。高水位标记或commit_scn代表我们上次为给定重做线程发出事务批次的位置。这意味着这两个值之间的更改是未提交的更改、已提交的更改或已回滚的更改的混合。
当连接器启动时,从偏移量读取的低水位标记或scn与 Oracle 中最早可用的归档日志进行比较。如果归档日志以晚于此scn值的系统更改号开头,则将发生此错误。
长时间运行的事务直接影响低水位标记或scn位置。如果事务保持活动状态的时间超过了您的归档日志保留策略,并且由于重新平衡或故障导致连接器重新启动,则可能发生此错误。如果您怀疑存在长时间运行的事务,可以配置log.mining.transaction.retention.ms属性,以便丢弃持续时间超过指定值的事务。虽然这会导致数据丢失,因为该事务的更改将被丢弃,但它可以让低水位标记在出现长时间运行的事务时安全地向前推进。您应将事务保留期设置为小于您的归档日志保留期。
可能导致此错误的另一个用例是,如果您正在从低变更量的 Oracle 数据库捕获更改。特别地,如果您使用的是较旧版本的 Debezium,其中 LogMiner 查询应用了数据库级别的过滤器,或者您已将新的查询过滤器模式配置为应用数据库级别的过滤器,那么连接器可能会在很长一段时间内未观察到任何单个更改事件。由于偏移量数据仅在连接器将事件发送到 Kafka 时同步,因此在很长的时间窗口内低变更量可能意味着 Kafka 偏移量会过时,如果发生重启,则可能发生此错误。在此用例中,配置heartbeat.internval.ms和heartbeat.action.query连接器属性是确保有某些活动流向 Kafka 以防止这些偏移量过时的绝佳方法。 - ORA-01555:快照太旧
-
此特定错误在连接器的初始快照期间最常观察到。Oracle 连接器在初始快照阶段依赖于所谓的 flashback 查询。
flashback 查询是一个标准的 SELECT 语句,它使用系统更改号来生成基于数据库某个特定时间点状态的结果集。这可能很有用,原因有很多,包括能够恢复对象而无需介质恢复,因为 Oracle 能够将该先前状态保留一段时间。这些查询返回的数据使用自动撤销管理 (AUM) 子系统,并依赖于记录和保留给定时间段的撤销数据区域,该时间段可根据数据库参数UNDO_RETENTION进行配置。
如果用于 flashback 查询的 SCN 过旧,并且撤销保留不再维护该系统更改号的历史数据,Oracle 将报告 ORA-01555 错误,表示快照太旧。当在初始快照期间发生这种情况时,需要从头开始重新拍摄快照,并且除非您重新配置 Oracle 的撤销保留期以允许更长的保留时间,否则在相同数据集上重新运行快照将导致相同的结果。
因此,a) 让您的 DBA 暂时增加UNDO_RETENTION数据库参数,或 b) 使用仅架构快照,然后依赖增量快照从您现有的表数据生成初始数据集。 - 流式更改需要几分钟才能出现
-
有时用户会注意到连接器启动时或连接器生命周期中的特定时期会出现延迟。识别正在发生情况的最佳方法之一是与您的 DBA 协调,并仔细查看数据库的警报日志,该日志记录了 Debezium 与数据库进行的所有 LogMiner 和 XStream 交互。但最常见的是,对于使用默认日志挖掘策略的用户来说,这种延迟是相当普遍的。
我们前面介绍的默认挖掘策略执行所谓的“数据字典构建”步骤,并且根据您的数据库,可能需要一些时间才能写入重做日志,然后由 LogMiner 进程解析。此过程通常需要 30 秒到几分钟才能完成,并且在使用默认挖掘策略时,此过程会在每次日志切换时发生。
因此,如果我们经常遇到这种延迟,我们通常建议检查日志切换的频率。如果您的数据库在不符合 Oracle 指导原则的小窗口内执行过多的日志切换,您的 DBA 可能需要相应地调整数据库。减少日志切换的频率会增加 Debzium 重用同一日志文件进行挖掘的时间,因此减少了构建和解析数据字典的需要。
如果您的表模式不经常更改或根本不更改,您可以将连接器重新配置为使用online_catalog挖掘策略作为替代方案。这避免了将数据字典写入重做日志以及 LogMiner 执行的解析阶段,从而大大加快了挖掘会话在连接器启动和每次日志切换间隔时的启动速度。 - 如何知道何时可以删除归档日志?
-
数据库管理员通常会在短时间内将归档日志保留在数据库服务器上,然后将其删除。这个间隔是可变的,并且取决于许多因素,包括日志创建的频率、它们的大小以及服务器上可用的物理空间。最重要的是,如果 Debezium 需要特定的归档日志,它必须一直可用,直到不再需要用于 CDC 为止。
确定需要哪些日志的最简单方法是通过 JMX 指标,查看OffsetScn字段。此字段引用了连接器在重启时将从中恢复的系统更改号,因此任何包含此系统更改号或晚于此更改号的归档或重做日志都必须保持可用。
Debezium 2.4 将添加另一个 JMX 指标,它将提供基于此OffsetScn的归档日志的截止时间戳。这意味着您将能够直接在 shell 脚本中使用此时间戳来比较文件系统的戳记和 JMX 指标中的戳记,从而安全地知道哪些日志必须保留,哪些可以通过 RMAN 安全地删除。 - 内存占用,如何有效管理?
-
由于 Oracle 归档日志和重做日志写入事务数据的方式,连接器必须管理事务状态的缓冲区。在理想情况下,此缓冲区维护短暂的数据,事务开始,我们缓冲其相关更改,然后我们观察回滚或提交,并处理缓冲区中的数据并清除缓冲区。
由于连接器缓冲事务,因此您需要事先了解您环境的事务模式非常重要。如果此信息可以变化且无法预测,您可能需要考虑使用替代的缓冲区类型而不是默认的基于堆(内存)的缓冲区,因为在这种情况下,如果内存配置过低,很容易导致OutOfMemory异常。
请参考关于 事件缓冲 的文档。Oracle 连接器提供了两个基于 Infinispan 的解决方案,允许连接器将缓冲区存储在堆外,从而减少连接器的内存占用,并能够无缝处理非常大的事务。
关于 Debezium
Debezium 是一个开源的分布式平台,可以将现有数据库转变为事件流,使应用程序能够几乎即时地看到并响应数据库中已提交的每个行级更改。Debezium 构建在 Kafka 之上,并提供了 Kafka Connect 兼容的连接器,用于监控特定的数据库管理系统。Debezium 将数据更改的历史记录在 Kafka 日志中,这样您的应用程序可以随时停止和重新启动,并可以轻松地消费在未运行时错过的所有事件,确保所有事件都被正确且完整地处理。Debezium 在 Apache 许可证 2.0 下是 开源 的。