查看原文
其他

技术分享|基于SQL Server Change Tracking实现宽表的增量更新

袁同学 dotNET跨平台 2022-09-10


源宝导读:在企业建设信息化的过程中,客户通常会使用一些数仓工具来构建数据资产,随着用户的要求越来越高,传统的ETL技术已经无法满足客户的实时性诉求,本文将分享“天际-数据平台”如何基于SQL Server来实现数仓数据的实时更新。

一、实现思路

为了提升数据的时效性,最简单的方法是将ETL的频率设置为每分钟一次或更短,以达到数据及时更新的目的。很显然,这种方案会给服务器造成很大的压力,有些ETL的执行时长甚至超过了调度周期,并不是一个可行的方案。

但是,我们仔细想想,如果两次调度的间隔非常短,业务系统在这两次调度之间产生的业务数据也会非常少,甚至没有任何数据产生,我们是否可以根据“变化的数据”来实现增量更新呢?
其实,ETL的目的就是为了生成一张大宽表,我们可以基于宽表模型设计一个增量更新的方案:

该流程可以每分钟运行一次,将原先每分钟的“全量计算”优化为了“增量计算”,这样就大幅度的降低数据库的压力。

二、详细方案

在开始之前,先看一下明源宽表的定义:所有的宽表都是“围绕一个业务实体,将该业务实体相关的维度、指标和属性关联在一起,形成了一张数据库表”。
“房间宽表”示例如下:

这里有两个特征:
1、每个宽表必须有一个唯一的主键,这个主键来源于ERP的业务实体表。
2、其它的维度和指标可以来源于不同的业务表,但都能通过宽表的主键联系在一起。
这样的模型非常有助于我们实现增量更新:我们可以让用户将每组指标的计算逻辑拆分出来单独定义,一旦某个业务发生之后,只需要对受影响的指标进行重新计算即可。
那么,如何找到受影响的指标,以及如何找到受影响的行呢?接下来,对流程图中几个关键问题进行详细讨论。

(一)我们需要监听哪些表的数据变化?

参与宽表逻辑计算的表才是我们需要监听的表,其它表都不需要关注。

(二)如何对表的变化数据进行跟踪?

我们可以在表上创建触发器或者新增时间戳字段,用于获取增量变化的数据。但是这种方案会对ERP数据库产生侵入性,并且时间戳字段解决不了delete的场景。针对这个痛点,微软在SQL Server 2008及之后的版本中,提供了SQL Server Change Tracking和SQL Server Change Data Capture两种解决方案,直接在数据库级别记录了各个表的数据变更日志,开发人员按需获取就行。我们需要在Change Tracking(简称CT)和Change Data Capture(简称CDC)之间做一个选择,两者的对比分析如下:
CT和CDC都有类似“版本号”和“序列号”的字段用于记录数据变化的顺序,同时都有“operation”字段用于标识数据变化的类型:

两者的差异点在于:CT只返回了变化表的主键值,而CDC返回了整个数据行。从能力上讲,CDC要大于CT,但是使用CDC需要开启SQL Server Agent服务,而CT则没有任何依赖。相对来说,CT相对于CDC更加轻量级,在产品上线时也没有额外的负担,这也是我们选择CT的核心原因,我们很难确保所有客户的SQL Server Agent服务都正确安装和时刻运行。
我们对相关表开启CT后,就可以通过CHANGETABLE函数来获取增量数据了
SELECT * FROM CHANGETABLE(CHANGES @table_name, @last_synchronization_version) AS CT
@table_name代表获取哪个表的变更数据。
@last_synchronization_version指的是获取哪个版本号之后的变更数据,在每次获取到变更数据之后,我们还需要将返回值中的最大版本号记录下来,以便于以下次获取增量数据。如果是第一次查询,@last_synchronization_version则为0。
(三)如何根据“表的变化”推算出“宽表中受影响的行”?

在通过CT跟踪到每张表的变更数据之后,CT函数只返回了变更表的主键,并不是“宽表的主键”。我们需要让用户针对每个表编写一个SQL语句,用来告诉程序“该表变化后如何推算出宽表中受影响的主键”,这个步骤主要是靠用户来定义。

还是以“房间宽表”为例,当101房间发生了签约业务之后,我们就需要更新101房间的“签约信息”,由于CT中只能获取到“合同Id”,这里就需要用户编写一个SQL语句,便于程序将101房间计算出来。

针对"合同表"的转换SQL可以编写成如下:

SELECT 房间Id FROM 合同表 WHERE 合同Id=@合同Id
"@合同Id"代表CT中监听到的“合同表主键”,我们将它带入到SQL语句的参数中就得到了我们重新计算的“房间Id”。
(四)如何根据“表的变化”推算出“宽表中受影响的列”?
为了实现最小粒度的数据更新,我们需要让用户将清洗逻辑拆分成多个小段,每个小段负责一个或多个指标的逻辑计算,拆分的越小,增量的效果越明显。为了保证程序能够将所有的“小段逻辑”关联起来,每个“小段逻辑”必须输出宽表的主键。
有了上面的定义之后,我们再看“本次变化的表”出现在哪些“小段逻辑”中,这些“小段逻辑”对应的指标就是需要重新计算的列。
(五)如何重新计算受影响的数据?
将“第三步得到的结果”做为过滤条件,拼接在“第四步的小段逻辑中”,即可计算出受影响的数据,示例如下:
SELECT * FROM (--ETL语句SELECT 宽表主键,指标1FROM 表1LEFT JOIN 表2LEFT JOIN 子查询GROUP BY ... ) tWHERE 宽表主键 IN (@宽表中受影响的主键)
最后再将该指标的结果更新到宽表中。

三、如何实现

我们首先需要一个B/S架构的宽表管理系统,客户端用来负责宽表的元数据管理,服务端用于数据的增量清洗。
宽表的元数据包含以下几个对象:宽表的名称、字段定义、每个指标的清洗逻辑、每个表如何转换出“宽表主键”的SQL。
服务端在第一次启动宽表时,需要执行所有的“小段逻辑”,先将全量的数据生成到宽表中,然后再循环执行增量更新的流程。
详细版的增量更新逻辑如下:


四、应用总结

明源宽表不仅解决了报表取数的性能问题,同时具备很好的时效性,为“传统的ETL”技术赋予了“实时”的属性。在明源ERP出库时,已经将“宽表服务”作为了标准配套服务,目前累计服务超过了1400家客户。

----- END ------
作者简介
袁同学: SM,目前负责数据平台相关工作。
也许您还想看:
天际数见数据质量巡检架构优化
技术分享 | 构建图表组件生态化的技术实战

更多明源云·天际开放平台场景案例与开发小知识,可以关注明源云天际开发者社区公众号:
【建模】附件下载支持水印显示,降低资料文档泄露风险
【集成】如何“零”代码实现售楼在线电子签约的交付上线
DevOps平台如何快速创建应用?


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存