SQL Server 增量数据同步

Posted 薛定谔的DBA

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQL Server 增量数据同步相关的知识,希望对你有一定的参考价值。

今天就说说 SQL Server 增量数据同步。当公司的业务数据量越来越多的时候,数据分析部门总想用来做报表,或者提炼出有用的运营数据。通常,相关负责人希望将各个业务系统、各种不同结构的数据同步到数据仓库、数据湖等。

 

若是其他类型数据库,多部署在 Linux 环境中,同步数据有较多及成熟的方案。

 

SQL Server 有哪些增量同步方案呢?

 

方案一:更改跟踪(Chang Tracking)

更改跟踪是表级别的跟踪,记录的只是行已更改的事实,而不是行更改的次数或任何中间更改的值。因为只存储的行标识符,这就要求表必须有主键。当对表进行DML操作时,系统表都会将主键记录下来,生成自己的行版本。

 

那具体的行数据在哪呢?当然还是在原来的表了,只要将跟踪的主键与原表关联,可以找到新增和最后一次更改的行记录。对于删除的记录,原表没有了,我们知道主键就可以同步到目标表进行删除。通过记录版本,每次可同步最近的操作。

 

CT原理图

 

不过,更改跟踪在实际工作中,很少人会使用。我想有几个原因:

  • 更改的中间值没有记录,对数据有严格的事务属性不适用。

  • DML频繁,也影响到跟踪表记录的DML频繁,也会影响一些性能。

  • 了解的人很少,毕竟几乎所有的第三方同步工具,都是基于变更数据捕获(CDC),而不是更改跟踪。

  • 更改跟踪不算增量同步一种,相当于同步当前一次快照记录。

 

 

方案二:变更数据捕获(Change Data Capture,CDC)

CDC 应该是多少大数据开发人员了解的,CDC 也是表级别的数据同步。CDC 同样有一张表记录跟踪,当对源表进行 DML 时,在事务日志会记录相关操作信息。变更数据捕获代理使用异步进程读取事务日志,将相关操作结果应用到副本表(捕获实例表)中,这样就完成了对源表操作的记录跟踪。即使数据库恢复模式处于简单模式,也同样适用。CDC 原理如下图。

 

CDC 原理

 

对于增量跟踪,应用程序在每次操作时,记录相应的时间或者事务日志序列号(LSN),下次读取大于该 LSN 即可。既然是读取数据库的事务日志,CDC 进程逻辑嵌入在存储过程 sp_replcmds中(事务复制也使用相同的进程)。

 

CDC 是比较常用的,但是当对表新增字段的时候,新字段是不会跟踪的。一般的解决方法是:新增字段后,对原表再启用一个CDC跟踪实例表,待原来的跟踪记录同步完成后,删除原跟踪实例表。字段新增方案其实还有一种,我这有一个脚本(如下链接)。当新增字段后,执行链接中脚本,将生产新的一个临时CDC跟踪实例,根据该实例生成的存储过程信息,替换原来的存储过程,然后禁用该新实例。这样增加字段后就不必等数据同步完成,继续使用表第一次启用CDC的系统对象。

https://github.com/hzc2012/SQLServer/blob/master/Scripts/Sync_CDC_Table_Columns_To_CT

 

 

方案三:UpdatedTime 字段

该方案也是非常普遍的同步方案。表中新增一个时间字段,默认值为当前时间 GETDATE()。然而该字段不像 mysql 可以自动更新。只能添加一个更新触发器,当行数据被更改时,UpdatedTime 更新为当前时间。如下示例。

CREATE TABLE [dbo].[TestTab](
  ID INT IDENTITY(1,1) NOT NULL,
  Name VARCHAR(50) NOT NULL,
  UpdatedTime DATETIME NOT NULL DEFAULT(GETDATE())
)
GO
CREATE TRIGGER tr_TestTab_update  
ON dbo.TestTab  
AFTER UPDATE   
AS
BEGIN
  UPDATE t SET UpdatedTime=GETDATE() 
  FROM dbo.TestTab t 
  INNER JOIN inserted i ON t.id=i.id
END
GO

数据的同步则按照时间进行增量查询,捕获相应的变更数据。对于触发器,相信大家都比较抵制,触发器每次都得进行一次表关联更新,增加了系统资源开销。若每次更新表数据都更新一个时间字段也不合适。那如何是好啊!下面,我们使用另一种方案!

 

方案四:行版本 rowversion / timestamp

该方案在大多数同学中还不被熟悉,估计只有较专业的 SQL Server DBA 有可能了解。说起 timestamp 可能很多同学都听过,在 SQL Server 中,timestamp 只是 rowversion 的一个同义词(可以说是别名)。timestamp 是 SQL Server 早期版本使用的名称, timestamp 已过时,在以后的 SQL Server 版本中将被删除。接下来,我们就使用 rowversion 吧。

 

rowversion 是数据库中自动生成的唯一二进制数字的数据类型。rowversion 通常用作给表行加版本戳的机制。存储大小为 8 个字节。rowversion 数据类型只是递增的数字,不保留日期或时间。不可为空的 rowversion 列在语义上等同于 binary(8) 列。可为空的 rowversion 列在语义上等同于varbinary(8) 列。

 

我们做一个示例,插入5行数据。

CREATE TABLE TestTab (id INT NOT NULL,rv ROWVERSION );
GO
INSERT INTO TestTab(id) VALUES(1)
GO 5
SELECT * FROM TestTab
GO

id  rv
1  0x00000000000007D5
1  0x00000000000007D6
1  0x00000000000007D7
1  0x00000000000007D8
1  0x00000000000007D9

SELECT @@DBTS AS 当前RV,MIN_ACTIVE_ROWVERSION() AS 下一个可用RV
0x00000000000007D9  0x00000000000007DA

使用系统变量和函数 @@DBTS 和 MIN_ACTIVE_ROWVERSION() 查看数据库的行版本号。也可以创建多个表进行验证,行版本是数据库范围级别的,不过增量同步数据时,还是按表将行版本存储起来,待下一次增量读取表记录。(是不是类似方案三?)

 

 

总结:

SQL Serve 中的增量数据同步方案,相对其他数据库来说是有点损耗性能的,毕竟闭源,只能连接数据库进行数据查询操作。不过,也有较多的第三方工具做的不错,也是基于上面的几种方案进行数据增量抽取。如 Oracle GoldenGate、Debezium 是基于CDC的,而 alibaba/DataX 对于SQL Server 是基于查询抽取数据的。根据不同的业务场景或架构情况,选择符合自己的方案才是最好的。

 

 

以上是关于SQL Server 增量数据同步的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server -;; 基于表TIMESTAMP类型字段+NOLOCK脏读的ETL增量同步方案发现的数据遗漏问题

[Kogel.Subscribe.Mssql]SQL Server增量订阅,数据库变更监听

如何同步SQL Server数据库中的数据?

从Oracle到SQL Server和从SQL Server到Oracle的迁移指南

使用新的dbForge Studio for SQL Server v5.8进行数据库差异管理

数据同步