DBus数据库表结构变动处理方案

导读:DBus是咱们要介绍的在敏捷大数据(Agile BigData)背景下的第一个平台。企业中大量业务数据保存在各个业务系统数据库中,为同时解决数据同步的一致性和实时性问题,DBus(数据总线)平台应运而生。java

DBus专一于数据的实时采集和实时分发,是一种基于日志的解决方案,同时可以提供消息订阅的方式给下游系统使用。本篇文章主要介绍在DBus的设计中,它是如何处理表结构变动及其带来的各类问题的。git

数据库表结构变动在软件产品快速迭代过程当中是广泛存在的现象,抽取数据库中的数据是DBus最重要的功能之一,那么对于数据库中表结构变动及其带来的各类问题,DBus是如何处理的呢? (本文仅讨论DBus for Oracle的实现方案)github

贴源输出是DBus的基本设计原则之一,经过解析后的数据库日志获取数据转换成UMS输出到Kafka,当表结构发生变动时DBus必须可以及时的调整输出UMS的结构,以确保和数据库中表结构保持一致,这里有两个问题须要解决:数据库

1)如何感知表结构变动?服务器

2)表结构变动后,新的表结构要如何与OGG输出的二进制数据关联?网络

1、感知表结构变动

对于感知表结构变动,Oracle已经经过DDL trigger为咱们提供了很好的支持,接下来咱们要考虑的是如何让DBus感知到表结构变动? 咱们讨论出如下两种方案:并发

1.1 RPC方案

在DDL trigger中调用DBus提供的REST服务,将表结构变动事件发送给DBus。oracle

该方案思路简单容易实现,但也有一些明显的弊端,好比DBus须要提供高可用、低延时的REST服务,不然可能会使数据库中的DDL操做变得缓慢甚至执行出现错误; DBus 的REST服务器对有数据实时同步需求的全部数据库都必须开通防火墙策略,这将给DBus的部署带来很大的麻烦。大数据

1.2 OGG实时同步方案

在DDL trigger中将表结构变动事件存储到一张Event表里,而后经过OGG实时的从日志中将数据同步到Kafka,从而感知表结构变动事件。设计

该方案实现相对复杂但具备不少优势,好比对数据库的侵入性相对较小,DDL执行时只是将数据写入到Event表中,相对网络通讯来讲,其延时更低、可靠性更高;更明显的优点是这种方案基于数据库日志实现,可以使用Event表的数据,严格的将表结构变动先后的数据区分开。

举例来讲,对于表:test来讲,依次执行insert → alter → insert 三个操做,由于OGG读取数据库日志存在延时,若是利用RPC方案,可能出现这样的一种状况:DBus REST服务接收到alter事件以后,第一个insert的记录才被OGG捕获并发送给DBus,此时DBus会认为这条数据中包含alter变化后的数据。这是一个很严重的问题,而OGG实时同步方案不管数据仍是时间均经过OGG读取日志的方案实现,能够完美的避免这种问题的发生。

对比两种方案OGG实时同步方案优点明显,最终咱们采用此方案。

然而,采用这种方案也并不是一路顺风,按照该方案的整体思路实现之后,咱们遇到了一个很奇怪的问题:经过DDL trigger写到Event表中的数据没法被OGG读取,在经历多番尝试无解以后,咱们试图到OGG的文档中寻找答案,而最终的结果倒是:DML or DDL operations performed from within a DDL trigger are not captured.

这个答案让问题变得更棘手,但这是最佳方案,咱们没有理由放弃。因而咱们开始尝试在DDL trigger中调用存储过程,在存储过程当中执行Event表的insert操做,但因为存储过程和DDL trigger仍然属于同一个事务,所以Event表的数据依然不能被OGG捕获,但经过这个尝试咱们以为只要在另一个事务中写Event表就能解决咱们面临的问题,因而咱们又想到了RPC,但RPC缺点太过明显。那么有没有其余能够替代的方案呢?

实际上oracle数据库里可使用多种语言来编写存储过程,Oracle 8i开始支持java编写存储过程,因而咱们当即开始实现java存储过程,经过JDBC链接数据库实现Event表的写入并提交事务,最终经过实践验证了这种办法的可行性,OGG成功的获取到了DDL trigger调用java存储过程写入到Event表的数据。

然而,这种实现并不算完美。当咱们在生产环境部署DDL trigger的时候,发现数据库服务器中并无安装执行java所须要的组件,每次部署都须要DBA同窗安装执行java存储过程所须要的组件,咱们试图找到一个不使用java存储过程的方案。这里要感谢韩锋老师对咱们的帮助,韩老师在听了我 们的实现原理以后,启发咱们自治事务应该能够解决这个问题,咱们即刻动手开始改造DDL trigger,使之支持自治事务,通过改造以后该方案才算完美,最终实现逻辑如图1所示:

2、处理表结构变动事件

DBus已经具有经过事件方式感知表结构变动的能力,接下来详细说明一下表结构变动事件该如何处理。

下图描述了Event的完整处理流程:

Event中描述了发生结构变动的表名、该表所属的schema以及元数据版本号,DBus接受并解析Event以后,根据表名、schema以及版本号调用元数据抓取模块获取该表的元数据(包括表的字段类型、长度以及注释等)信息,实际上DDL trigger和alter语句在一个事务中执行,这样在trigger执行过程当中没法从oracle的数据字典里获取到修改以后表结构元数据,咱们写入到meta_history表中的元数据只是执行alter语句以前的元数据信息(所以咱们给这个表取名为table_meta_his),要获得完整的元数据信息须要联合table_meta_his和数据字典进行查询,示意SQL以下:

这个SQL的结果有两种可能:

1)只包含all_tab_cols视图中的数据

2)既包含all_tab_cols视图中的数据又包含table_meta_his表的数据(is_current字段的做用是区别该字段的来源)

结果A代表在table_meta_his表中没有找到数据,这说明在生成表结构变动Event至元数据抓取程序成功获取元数据期间没有再次发生表结构变动,结果B则说明在此期间又发生过一次或屡次表结构变动。

为何要使用union all?

单独使用上图中的两个SQL可能致使元数据获取程序获取到错误的结果,例如:接到表结构变动Event 1后,咱们调用SQL 1 查询table_meta_his结果集为空,在调用SQL 2以前表结构再次发生变动(命名为Event 2),这种状况下咱们经过SQL 2 查询到的结果其实是再次变动后的结果,使用这个结果产生的元数据去解析Event 1和Event 2之间的数据,若是两次表结构变动是不兼容的,那么必然会致使解析失败。

感知表结构变动以及处理表结构变动事件的最终目的是可以生成正确的输出结果,其中的更多细节以及实现能够参考:

https://github.com/BriData/DBus

做者:张玉峰

来源:宜信技术学院

相关文章
相关标签/搜索