谈谈数据的增量更新

2,439 阅读6分钟

谈谈数据的增量更新

在数据同步的过程中,必然会遇到数据增量更新的需求。但如果没有一个有效的数据增量更新的设计与机制,可能每次上游数据更新时,你都需要将全量的数据同步一遍,即使只有1%的数据发生了更新。

全量数据同步在数据首次同步的时候是需要的,而且定期的全量同步也能够修复一些偶发的数据不完整的问题。

但是全量同步作为定期的增量数据更新方式并不是十分合适的,因为:

  1. 耗时长:对于具有一定规模的数据量,全量同步通常需要较长的时间,比如几个小时,几天甚至几周,所以全量同步经常无法提供实时或者准实时的数据更新服务;
  2. 占用资源:全量同步会对数据源和全量同步目标同时产生较高的计算压力,可能会影响平台其他的计算、分析服务;
  3. 带宽占用:全量同步会占用较大的网络带宽,可能会导致网络产生抖动。

随着数据量的增长,上面几个问题的影响也会变得越来越明显。

如果要做数据的增量更新,你必须在数据源上能找到定位数据更新/变化的方式,这个概念叫做CDC-Change Data Capture-数据变化捕捉

在实现过程中,有若干种常见数据变化捕捉方式,但以下两种比较常见。

1 基于每一行的更新时间戳来做增量更新

在数据源中,每一行数据通常会有更新时间戳,来表示最近一次创建/变化的时间点。

基于这种情况,我们要做的就是找到哪一列是更新时间戳,基于这个时间戳将需要的增量数据提取回来,大致流程如下:

  1. 确认每一行的更新时间戳,比如last_modified_time, update_time等,并将其最大值进行存储
  2. 在第一次数据同步完成后,后续每一次同步只需要同步更新时间戳****大于上一次同步中最大的更新时间戳

这种基于更新时间戳的方式很方便,使用也很普遍,但也有一些不足的地方:

  1. 一般基于更新时间戳的增量数据更新,并不会记录下来数据删除的情况;所以如果数据源有数据被删除的,是无法被感知到的
  2. 由于每次更新需要根据更新时间戳进行查表,如果没有索引机制,可能会对数据源进行全表扫描,进而对数据源带来较高的计算压力
  3. 增量数据有两类,一类是新增的数据,直接插入即可;还有一类是内容更新的数据,这类数据的更新也会对目标库产生较大的计算压力:根据索引或者全表扫描找到要更新的数据,执行更新操作
  4. 无法记录数据的更新历史,如果需要知道某一条数据经历过多少次更新,每一次更新了什么,需要做额外的记录操作才能实现

2 基于数据库更新日志来做增量更新

在数据库同步中,更新日志(Change logs)也经常作为增量更新的同步机制。

每个数据库都有自己的更新日志,里面包括了新增、修改和删除等一系列的更新记录。这些更新记录可以包含一行完整的信息(主键和所有列的信息),也可以只包含变化的部分(主键和变化列的信息)。

使用更新日志来做增量更新的步骤大致如下:

  1. 使用如**select ***的方式先将全部数据同步到目标库,并记录好主键信息,会用于后面的数据更新和去重等操作
  2. 找到并记录更新日志当前(完成步骤1时)的位置信息
  3. 每次增量更新时,只需要从上次记录的位置来读取更新日志,针对更新日志的每一条更新记录进行操作
  4. 对于数据修改操作:根据主键进行更新和合并
  5. 对于新增操作:直接新增即可
  6. 对于删除操作:根据主键进行删除

使用更新日志来做增量更新的常见挑战有:

  1. 由于数据定期会将日志进行归档来做离线压缩存储,所以程序上经常需要同时读取事务日志和离线归档日志,并将二者合并后作为完整的更新日志来解读
  2. 不同的数据库的不同版本,可能更新日志的格式都不一样,不如SQL语言通用与标准
  3. 需要考虑和处理更新日志中的事务回滚操作

当然,使用更新日志来做增量更新有一些独一无二的优势:

  1. 对数据库的影响是最小的
  2. 数据源固定后,任何表的同步程序都是一样的
  3. 低延迟
  4. 事务完整性

3 数据增量更新的挑战

尽管增量更新相比于全量更新有明显的优点,但在实际落地的过程中可能还是会遇到各种各样的工程问题。

3.1 性能与数据规模

如果每次增量更新的数据量很大,而增量更新的程序性能又很一般,可能会导致一次增量数据还没处理完,下一次的增量更新就要启动了。

这样就会像滚雪球一般,数据的时效性不断延后或者将机器的计算资源快速耗尽。

这就需要具有更优秀性能的增量更新程序与机制来优化增量更新的时间。

3.2 时间精度

如果使用更新时间戳来做增量更新的话,一种常见的情况是更新时间戳的精度只到秒级。比如这次同步到了下午 03:00:01,下一次同步会从下午 03:00:02开始,如果01秒和02秒之间数据源有新的数据进来,这种同步机制则会丢掉这部分数据,而且很难发现和补回这批数据,很可能直到用户侧出现问题才能发现。

这种情况,则需要使用大于等于上一次的更新时间戳来进行增量同步,这样会同步过来重复的数据,所以需要在同步设计上要符合幂等性,来确保重复数据不会带来逻辑上的冲突。

3.3 寻找增量更新方式

在某些情况下,数据源既没有更新日志也没有明显的更新时间戳,这时就需要精心设计增量同步逻辑,比如使用自增主键,比如多列(create_time + update_time + delete_flag)联合起来等价于更新时间戳,比如每次进行大量冗余同步足够覆盖增量更新部分等等。

在最坏的情况下,可能只能每次进行全量同步来进行数据更新。

在某些情况下,更新时间戳

参考材料