Debezium简介

2021-08-11

Debezium是什么?

​ Debezium是一个开源项目,为捕获数据更改(change data capture,CDC)提供了一个低延迟的流式处理平台。你可以安装并且配置Debezium去监控你的数据库,然后你的应用就可以消费对数据库的每一个行级别(row-level)的更改。只有已提交的更改才是可见的,所以你的应用不用担心事务(transaction)或者更改被回滚(roll back)。

​ Debezium为所有的数据库更改事件提供了一个统一的模型,所以你的应用不用担心每一种数据库管理系统的错综复杂性。另外,由于Debezium用持久化的、有副本备份的日志来记录数据库数据变化的历史,因此,你的应用可以随时停止再重启,而不会错过它停止运行时发生的事件,保证了所有的事件都能被正确地、完全地处理掉。

​ 监控数据库,并且在数据变动的时候获得通知一直是很复杂的事情。关系型数据库的触发器可以做到,但是只对特定的数据库有效,而且通常只能更新数据库内的状态(无法和外部的进程通信)。一些数据库提供了监控数据变动的API或者框架,但是没有一个标准,每种数据库的实现方式都是不同的,并且需要大量特定的知识和理解特定的代码才能运用。确保以相同的顺序查看和处理所有更改,同时最小化影响数据库仍然非常具有挑战性。

​ Debezium提供了模块为你做这些复杂的工作。一些模块是通用的,并且能够适用多种数据库管理系统,但在功能和性能方面仍有一些限制。另一些模块是为特定的数据库管理系统定制的,所以他们通常可以更多地利用数据库系统本身的特性来提供更多功能。

Debezium特点

  1. 基于日志的CDC, 确保捕获所有数据更改
  2. 以非常低的延迟生成更改事件,同时避免增加频繁轮询的CPU使用量(例如,MySQL或Postgres的ms范围)
  3. 业务库影响较小,不需要更改数据模型(如“最后更新”列)
  4. 可以捕获删除
  5. 可以捕获旧记录状态和其他元数据,如事务id和引发查询(取决于数据库的功能和配置)

变化数据捕获特性相关的功能和选项:

  1. 快照:可选的,一个初始数据库的当前状态的快照可以采取如果连接器被启动并不是所有日志仍然存在(通常在数据库已经运行了一段时间和丢弃任何事务日志不再需要事务恢复或复制);

    快照有不同的模式,请参考特定连接器的文档以了解更多信息

  2. 过滤器:可以通过白名单/黑名单过滤器配置捕获的模式、表和列集

  3. 屏蔽:可以屏蔽特定列中的值,例如敏感数据

  4. 监视:大多数连接器都可以使用JMX进行监视

  5. 不同的即时消息转换:例如,用于消息路由、提取新记录状态(关系连接器、MongoDB)和从事务性发件箱表中路由事件

Debezium架构

1) Kafka Connect

Debezium是一个捕获数据更改(CDC)平台,并且利用Kafka和Kafka Connect实现了自己的持久性、可靠性和容错性。每一个部署在Kafka Connect分布式的、可扩展的、容错性的服务中的connector监控一个上游数据库服务器,捕获所有的数据库更改,然后记录到一个或者多个Kafka topic(通常一个数据库表对应一个kafka topic)。

Kafka确保所有这些数据更改事件都能够多副本并且总体上有序(Kafka只能保证一个topic的单个分区内有序,如果topic只设置一个分区就能保证有序性)

下图显示了一个基于Debezium的CDC管道的架构:

除了Kafka代理本身之外,Kafka Connect是作为一个单独的服务来操作的。部署了用于MySQL和Postgres的Debezium连接器来捕获这两个数据库的更改。为此,两个连接器使用客户端库建立到两个源数据库的连接,在使用MySQL时访问binlog,在使用Postgres时从逻辑复制流读取数据。

默认情况下,来自一个捕获表的更改被写入一个对应的Kafka主题

如果需要,可以在Debezium的主题路由SMT的帮助下调整主题名称,例如,使用与捕获的表名不同的主题名称,或者将多个表的更改转换为单个主题

2)嵌入式引擎

使用Debezium连接器的另一种方法是嵌入式引擎。

在这种情况下,Debezium不会通过Kafka Connect运行,而是作为一个嵌入到定制Java应用程序中的库运行。这对于在应用程序内部使用更改事件非常有用,而不需要部署完整的Kafka和Kafka连接集群,或者将更改流到其他消息传递代理(如Amazon Kinesis)。您可以在示例库中找到后者的示例。

Debezium使用场景

1)缓存失效

(Cache invalidation)

​ 在缓存中缓存的条目(entry)在源头被更改或者被删除的时候立即让缓存中的条目失效。如果缓存在一个独立的进程中运行(例如Redis,Memcache,Infinispan或者其他的),那么简单的缓存失效逻辑可以放在独立的进程或服务中,从而简化主应用的逻辑。在一些场景中,缓存失效逻辑可以更复杂一点,让它利用更改事件中的更新数据去更新缓存中受影响的条目。

2)简化单体应用

(Simplifying monolithic applications)

​ 许多应用更新数据库,然后在数据库中的更改被提交后,做一些额外的工作:更新搜索索引,更新缓存,发送通知,运行业务逻辑,等等。这种情况通常称为双写(dual-writes),因为应用没有在一个事务内写多个系统。这样不仅应用逻辑复杂难以维护,而且双写容易丢失数据或者在一些系统更新成功而另一些系统没有更新成功的时候造成不同系统之间的状态不一致。使用捕获更改数据技术(change data capture,CDC),在源数据库的数据更改提交后,这些额外的工作可以被放在独立的线程或者进程(服务)中完成。这种实现方式的容错性更好,不会丢失事件,容易扩展,并且更容易支持升级。

3)共享数据库

(Sharing databases)

​ 当多个应用共用同一个数据库的时候,一个应用提交的更改通常要被另一个应用感知到。一种实现方式是使用消息总线,尽管非事务性(non-transactional)的消息总线总会受上面提到的双写(dual-writes)影响。但是,另一种实现方式,即Debezium,变得很直接:每个应用可以直接监控数据库的更改,并且响应更改。

4)数据集成

(Data integration)

​ 数据通常被存储在多个地方,尤其是当数据被用于不同的目的的时候,会有不同的形式。保持多系统的同步是很有挑战性的,但是可以通过使用Debezium加上简单的事件处理逻辑来实现简单的ETL类型的解决方案

5)命令查询职责分离

(CQRS)

​ 在命令查询职责分离(Command Query Responsibility Separation,CQRS)架构模式中,更新数据使用了一种数据模型,读数据使用了一种或者多种数据模型。由于数据更改被记录在更新侧(update-side),这些更改将被处理以更新各种读展示。所以CQRS应用通常更复杂,尤其是他们需要保证可靠性和全序(totally-ordered)处理。Debezium和CDC可以使这种方式更可行:写操作被正常记录,但是Debezium捕获数据更改,并且持久化到全序流里,然后供那些需要异步更新只读视图的服务消费。写侧(write-side)表可以表示面向领域的实体(domain-oriented entities),或者当CQRS和Event Sourcing结合的时候,写侧表仅仅用做追加操作命令事件的日志。

基于数据日志变更的CDC简单对比

组件 Canal Maxwell Debezium Flinx
开源方 阿里 zendesk redhat 袋鼠云
开发语言 java java java java
支持数据库 mysql mysql MongoDB、MySQL、PostgreSQL、
SQL Server 、Oracle( 孵化)、
DB2( 孵化)、Cassandra( 孵化)
MongoDB、MySQL、PostgreSQL
是否支持bootstrap
是否支持解析DDL同步
是否支持HA 需定制 基于kafka-connector
社区活跃度        
文档 中文,百度可以解决 英文,官方文档 英文,官方文档十分详细 中文,github readme 文档
MQ集成 RocketMQ、Kafka kafka kafka Emqx、kafka

补充知识点:

常用的变化数据捕获方法有四种: 时间戳、快照、触发器和日志。

时间戳方法: 需要源系统有相应的数据列表示最后的数据变化。

快照方法: 可以使用数据库系统自带的机制实现,如Oracle的物化视图技术,也可以自己实现相关逻辑,但会比较复杂。

触发器 是关系数据库系统具有的特性,源表上建立的触发器会在对该表执行insert、update、delete等语句时被触发,触发器中的逻辑用于捕获数据的变化。

日志: 可以使用应用日志或系统日志,这种方式对源系统不具有侵入性,但需要额外的日志解析工作。Debezium是基于日志的。

CDC大体可以分为两种:侵入式的,非侵入式的。

所谓侵入式的是指CDC操作会给源系统带来性能的影响。只要CDC操作以任何一种方式对源库执行了SQL语句,就可以认为是侵入式的CDC。

基于时间戳的CDC、基于触发器的CDC、基于快照的CDC是侵入性的;

基于日志的CDC是非侵入性的

1.基于时间戳的CDC

​ 抽取过程可以根据某些属性列来判断哪些数据是增量的,最常见的属性列有以下两种: 1.时间戳:最好有两个列,一个插入时间戳,表示何时创建,一个更新时间戳,表示最后一次更新的时间。 2.序列:大多数数据库都提供自增功能,如果数据库表列被定义成自增的,就可以很容易地根据该列识别新插入的数据。

​ 这种方法是最简单且常用的,但是有如下缺点: 1.不能记录删除记录的操作 2.无法识别多次更新 3.不具有实时能力

2.基于触发器的CDC

​ 当执行INSERT、UPDATE、DELETE这些SQL语句时,可以激活数据库里的触发器,并执行一些动作,就是说触发器可以用来捕获变更的数据并把数据保存在中间临时表里。然后这些变更数据再从临时表取出,抽取到数据仓库的过渡区中。大多数场合下,不允许向操作型数据库里添加触发器,且这种方法会降低系统性能,所以用的不多。 ​ 可以使用源数据库的复制功能,将源库的数据备用到备用库上,在备库上创建触发器。

3.基于快照的CDC

​ 如果没有时间戳,不允许使用触发器,就要使用快照表。可以通过比较源表和快照表来获得数据变化。 ​ 基于快照的CDC可以检测到插入、更新和删除的数据,这是相对于基于时间戳的CDC方案的有点。其缺点是需要大量存储空间来保存快照。

4.基于日志的CDC

​ 最复杂的和没有侵入性的CDC方法是基于日志的方式。数据库会把每个插入、更新、删除操作记录到日志里。