【发布时间】:2020-10-20 01:34:48
【问题描述】:
如果将数据库中的主键字段自动递增为递增字段,kafka-connect-jdbc 在丢失和重复行方面是否安全?
【问题讨论】:
-
如果你担心这个问题,你可能想看看 Debezium。
标签: apache-kafka confluent-platform
如果将数据库中的主键字段自动递增为递增字段,kafka-connect-jdbc 在丢失和重复行方面是否安全?
【问题讨论】:
标签: apache-kafka confluent-platform
在自增模式下绝对不安全。问题在于事务隔离和由此产生的可见性特征——事务开始的顺序(以及它们可能获取的任何自动递增字段的值)与这些事务提交的顺序不同。这个问题在混合工作负载中尤其明显,其中事务可能需要不同的时间才能完成。因此,作为观察者,您将在表中看到的是可见记录中的临时“间隙”,直到这些事务完成为止。如果事务T0 with key 0 在T1 with key 1 之前开始,但T1 先完成,Kafka Connect 源连接器将观察T1 的影响,发布记录并将水印推进到 key @ 987654330@。稍后,T0 最终将提交,但此时源连接器将继续前进。
这是reported issue,Kafka Connect 文档对已知限制并不透明(尽管自 2016 年以来 KC JDBC 团队就已公开该问题)。
一种解决方法是使用时间戳模式(它本身并不安全),并通过timestamp.delay.interval.ms 属性添加延迟。根据Confluent documentation:
在我们将具有特定时间戳的行包含在结果中之前等待多长时间。您可以选择添加一些延迟以允许具有较早时间戳的事务完成。第一次执行将获取所有可用记录(即从时间戳 0 开始),直到当前时间减去延迟。每次后续执行都会获取从我们上次获取到当前时间减去延迟的数据。
这解决了一个问题(尴尬地),但引入了另一个问题。现在源接收器将在时间戳延迟期间落后于表的“尾部”(可以这么说),因为潜在事务将在该宽限期内提交。宽限期越长——滞后时间越长。因此,在需要近乎实时的消息传递的应用程序中,这可能不是一个选项。
您可以尝试放宽源接收器查询的隔离级别,但这会产生其他影响,特别是如果您的应用程序依赖 transaction outbox pattern 来保证消息传递。
使用 Kafka Connect 的一个安全解决方案是使用 CDC(更改数据捕获)或等效方法,并将源接收器指向 CDC 表(将按提交顺序)。您可以使用原始 CDC 或 Debezium 作为“便携式”变体。这将添加到数据库 I/O,但会为连接器提供线性历史记录。
【讨论】:
我为此目的对其进行了分析,并得出结论,除非您处理的是非事务性数据库,否则对 PK 列使用“递增”模式是不安全的。这是因为自动递增 PK 的序列号是在事务期间(执行 INSERT 时)分配的,但行仅在事务提交时出现,因此它们可能出现乱序。想象一下不常见的场景:
要克服这样的遗漏行,您可以考虑在 jdbc 驱动程序配置中使用 DIRTY READ,但随后您会看到插入的事务可能是后来回滚且不应被读取的事务的一部分。
我建议您考虑“时间戳”或“时间戳+递增”模式,而不是“递增”。 https://docs.confluent.io/current/connect/connect-jdbc/docs/source_config_options.html#mode 并适当地设置“timestamp.delay.interval.ms”配置作为长时间运行的事务完成无序完成的容差。根据经验,我不能说这是否 100% 安全,因为我必须处理的数据库不符合 ANSI SQL,并且 kafka-connect-jdbc 的时间戳相关功能不起作用。
【讨论】: