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

Stella981
• 阅读 719

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

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

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

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

1)如何感知表结构变更?

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

一、感知表结构变更

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

1.1 RPC方案

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

该方案思路简单容易实现,但也有一些明显的弊端,比如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所示:

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

二、处理表结构变更事件

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

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

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

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

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

这个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

作者:张玉峰

来源:宜信技术学院

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Karen110 Karen110
3年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这