简介
SAP HANA(全称 SAP High-performance ANalytic Appliance)是由SAP开发的一款内置列式数据库的系统平台,除内置数据库以外,还具有高级分析功能(例如预测分析、空间数据处理、文本分析、文本搜索、流分析、图形数据处理),ETL功能,并内置了应用程序服务器,本文提到的 SAP HANA 特指数据平台内置的数据库管理系统。
SAP HANA 是内存数据库系统,可以把系统所有的数据都载入内存中,因此,与传统的将数据存储在硬盘上的数据库相比,HANA的性能可以提升10~10,000倍。一般SAP HANA 内置在 SAP ERP 系统中作为整体提供服务,在制造业应用广泛。
现如今企业都会建立内部的统一数据分析平台,SAP HANA 保存了ERP相关数据,如何实时同步 SAP HANA 的数据到数据平台,一直是困扰企业用户的问题。
CloudCanal 作为一款数据同步工具,新版本中支持 SAP HANA 同步到 MySQL、MariaDB、Doris、StarRocks。下面将介绍 CloudCanal 实现 SAP HANA 实时同步的原理。
整体流程
实现触发器同步的整体流程如下:
- 安装触发器,通过触发器捕获增量变更数据
- 记录位点,记录增量数据数据同步的起点
- 执行全量数据同步
- 执行增量数据同步
触发器安装
触发器是一种自动触发执行的存储过程,它可以在数据变更前执行也可以在数据变更后执行,因为本质也是存储过程,所以存储过程支持的操作触发器均支持。
不同数据库对触发器的支持程度不同,Hana 的触发器支持监听 I(新增)、U(更新)、(删除) 三种事件,因此数据的所有变更都可以通过触发器捕获。
安装触发器的方式与创建存储过程类似,即通过执行 SQL 创建触发器。
通过触发器实现增量数据同步,需要触发器捕获数据的变更事件并写入 增量CDC数据表, 下面是触发器执行的整体流程:
触发器安装示例
下面是安装触发器的示例脚本
sql
CREATE OR REPLACE TRIGGER "MY_SCHEMA"."SYM_ON_I_FOR_TST1_CRP" AFTER INSERT ON "MY_SCHEMA"."TEST1"
REFERENCING NEW ROW NEW FOR EACH ROW
BEGIN
DECLARE NEW_KEY_SUMMARY CLOB := '"COL1":' || CASE WHEN :NEW."COL1" IS NULL THEN 'null' ELSE CONCAT(CONCAT('"',REPLACE(REPLACE(:NEW."COL1",'\','\\'),'"','\"')),'"') END;
DECLARE NEW_COL_SUMMARY CLOB := '"COL1":' || CASE WHEN :NEW."COL1" IS NULL THEN 'null' ELSE CONCAT(CONCAT('"',REPLACE(REPLACE(:NEW."COL1",'\','\\'),'"','\"')),'"') END || ',' || '"COL0":' || CASE WHEN :NEW."COL0" IS NULL THEN 'null' ELSE CONCAT(CONCAT('"',REPLACE(REPLACE(:NEW."COL0",'\','\\'),'"','\"')),'"') END;
DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN END;
IF 1=1 THEN
INSERT INTO "SYSTEM"."CLOUD_CANAL_TRIGGER_DATA" (CATALOG_NAME, SCHEMA_NAME, TABLE_NAME, EVENT_TYPE, TRIGGER_ID, PK_DATA, ROW_DATA, TRANSACTION_ID, EXTERNAL_DATA, CREATE_TIME)
VALUES(
'SYSTEMDB',
'MY_SCHEMA',
'TEST1',
'I',
1,
'{' || NEW_KEY_SUMMARY || '}',
'{' || NEW_COL_SUMMARY || '}',
CURRENT_UPDATE_TRANSACTION(),
'',
CURRENT_UTCTIMESTAMP
);
END IF;
END;
安装好触发器,下面介绍 增量CDC数据表 的具体结构设计
CDC 增量表
增量CDC数据表结构如下:
sql
CREATE COLUMN TABLE "SYSTEM"."CLOUD_CANAL_TRIGGER_DATA" ("DATA_ID" BIGINT GENERATED BY DEFAULT AS IDENTITY (
START WITH 1 INCREMENT BY 1) NOT NULL ,
"CATALOG_NAME" VARCHAR(255) NULL ,
"SCHEMA_NAME" VARCHAR(255) NOT NULL ,
"TABLE_NAME" VARCHAR(255) NOT NULL ,
"EVENT_TYPE" VARCHAR(20) NOT NULL ,
"ROW_DATA" CLOB,
"PK_DATA" CLOB,
"OLD_DATA" CLOB,
"TRIGGER_ID" INTEGER NOT NULL ,
"EXTERNAL_DATA" VARCHAR(50),
"CREATE_TIME" LONGDATE
);
CREATE UNIQUE CPBTREE INDEX "CLOUD_CANAL_IDX_D_ID" ON
"SYSTEM"."CLOUD_CANAL_TRIGGER_DATA" ( "DATA_ID" ASC);
CREATE INDEX CLOUD_CANAL_TRIGGER_DATA_CREATE_TIME_IDX ON "SYSTEM".CLOUD_CANAL_TRIGGER_DATA (CREATE_TIME);
增量CDC数据表记录了每次变更数据的
- 数据库(CATALOG_NAME)
- 模式名称(SCHEMA_NAME)
- 表名称(TABLE_NAME)
- 事件类型(EVENT_TYPE)
- 变更前的数据镜像(ROW_DATA)
- 变更前主键数据(PK_DATA)
- 变更后的数据镜像(OLD_DATA)
- 触发器ID(TRIGGER_ID)
- 变更时间(CREATE_TIME)
另外,需要注意的是使用 自增ID 作为主键,同时记录 创建时间。
自增ID(DATA_ID)可以唯一标识数据变更事件并确保有序,创建时间(CREATE_TIME)作为数据变更事件的时间戳,记录数据变更发生时间。
增量表定时清理
触发器将增量数据写入增量表后,若未及时清理,可能导致空间占用增加。
在 CloudCanal 中可以设置任务参数 triggerDataCleanEnabled 打开自动定时清理增量表功能,并提供两个参数进行控制:
- triggerDataCleanIntervalMin:增量表清理间隔(单位:分钟)
- triggerDataRetentionMin:增量表数据保留时间(单位:分钟)
通过这套机制,用户能够灵活控制增量表的清理操作,同时确保未消费的增量数据不会被意外清除。
增量表自动演进
Hana 增量任务创建时自动生成增量表,CloudCanal 依赖于增量表实现各种能力,但随着 CloudCanal 版本更新,可能对增量表进行变更(比如加入新字段)。
由此带来的问题是:用户在更新 CloudCanal 后需要手动执行 DDL 以适应增量表结构的变化,若存在大量增量表,操作相当复杂。
为解决此问题,CloudCanal 新增 增量表结构 DIFF 能力 ,在任务启动时 自动生成差异 DDL 实现对增量表的自动演进。
扫描 CDC 增量表
扫描 CDC 增量表 需要做到不重复扫、不漏数据、顺序扫描。
保证顺序的方式是通过自增ID排序,即 ORDER BY DATA_ID ASC,通过这个方式相当于对全局的变更事件进行编号,基于编号进行扫描和消费,可确保不重。但只做到这一点还不够,会出现丢数据情况,数据丢失的原因如图所示:
当查询的语句执行时,可能有部分事务没有 COMMIT,导致漏扫,这种问题如何解决?
首先想到的可能是等待一段时间,但是等待多久合适也是问题,时间长了延迟高,时间短了丢数据,而且当事务出现回滚时,自增序列会出现缺失,缺失的原因是事务没有回滚前占用了自增 ID 生成的序号,事务回滚后占用的序号也不会被重复使用。遇到自增 ID 序号缺失的情况,通过等待一段时间方式,只能每次都等待最大超时时间,会导致同步延迟增大。
这个问题的关键点是确定占用自增ID的事务是否还在活跃状态。
CloudCanal 采用的解决办法是 查缺补漏 ,在扫描 增量CDC数据表 时遇到某个数据 ID 缺失的情况,会尝试插入一条相同 ID 的数据,通过唯一键来判断这个 ID 的数据是否被占用,如果出现异常,则重新查询;如没有异常,则会写入数据占用这个 ID,因为这个 ID 的数据已经被填充,因此也不用担心这个 ID 的数据被漏扫,可以继续读取大于这个 ID 数据。
位点管理
位点用于管理数据同步的进度,记录哪些变更事件已经同步、哪些变更事件没有同步。
基于触发器的数据同步方案选择 CDC 增量表的自增 ID 及时间戳作为位点,自增 ID 可以精确定位到每个数据变更事件,时间戳可以方便以用户视角感知同步任务的延迟情况。
什么时候更新位点?
当捕获的变更事件成功写入目标端后更新位点,如果写入数据成功更新位点失败会导致数据不一致吗?
答案是 不会,因为源端的变更事件是顺序保存并且顺序读取,类似 Mysql Binlog,只要 CloudCanal 按照源端的事件顺序消费,当消费到事件末尾时可以确保源端和目标端的的数据状态一致。
如何判断同步延迟?
Hana 源端增量同步使用位点(增量表自增ID)来判断延迟,当位点向前推进时可准确获取延迟,但若无变更事件导致位点不更新,延迟会持续增大,实际上并未发生延迟。
为解决这一问题,CloudCanal 通过查询增量表来判断是否存在延迟,具体逻辑为:
- 若存在数据,系统根据增量数据的时间戳计算延迟。
- 若无数据,任务获取当前时间发送心跳事件,并根据心跳上的时间戳计算延迟。
时间戳仅在重置位点时才用于数据查找,且在查找时进行时区转换处理。
同步演示
添加数据源
- 数据源管理 -> **添加数据源, 添加 Sap Hana、MySQL
创建同步任务
-
任务管理 -> 新建任务
-
选择需要迁移同步的列 ,目标映射规则可选择与源端一致 和转小写
-
任务自动做结构迁移 、全量迁移 、增量同步
测试同步
- 利用造数据工具对 Hana 进行随机增删改操作。
- 数据同步结束后校验 Hana 和 MySQL 的数据,40 万左右的数据是一致的。
总结
本文简要介绍 CloudCanal 数据迁移工具近期对 Hana 源端数据同步的设计思路,希望对读者有所帮助。