一、增量与全量同步的合并
问题:传统数据同步方式为周期全量数据同步,但随着业务发展数据量的急剧增加,周期全量同步的效率太低了。
解决方案:每个周期只同步增量数据,然后与上一个同步周期获取的全量数据进行合并,获取最新版本的全量数据。
传统数据整合方案:merge 方式(update + insert );
当前大数据平台不支持 update 操作,而采用:全外连接(full outer join) + 数据全覆盖重新加载(insert overwrite);(即如日调度,则将当天的增量数据和前一天的全量数据做全外连接,重新加载最新的全量数据)
如果担心数据更新错误:每条保持一个最新的全量版本,保留较短的事件周期。(另外,当业务系统的表有物理删除数据的操作,而数据仓库需要保留所有历史数据时,也可以选择这种方式,在数据仓库中永久保留最新的全量数据快照。)
例:淘宝订单表
1:数据更新
方案一:数据更新可以增量抽取合并merge,得到最新数据
方案二:采用kudu数据库,kudu支持主键更新操作
2、数据删除
一般业务系统删除数据,业务库逻辑删除,即打删除或者无效标签字段,
但DBA物理删除这么办?下游无感的,这样ODS以及数仓永远存在这条数据了。
二、实时同步,既解决数据性能,又解决数据更新删除问题
DSG/canal :用来监控数据库数据的变化,从而获得新增、修改、删除等的数据。
方案一:DSG同步到另一个ORACLE数据库,再sqoop从数据库抽取,这样包含增删修改的数据,可解决数据删除问题
方案二:DSG/canal +Kafka + Spark解析数据+Hive、Kudu
二、同步性能的处理
数据同步任务是针对不同数据看系统之间的数据同步问题而创建的一些列周期调度的任务。在代行的数据调度工作台上,每条会运行大量的数据同步任务。针对数据同步任务,一般首先需要设定首轮同步的线程数,然后运行同步任务。这样的数据同步模式存在以下几个问题:
- 有些数据同步任务的总线程数达不到用户设置的首轮同步的线程数时,如果同步控制器将这些同步县城分发到 CPU 比较繁忙的机器上,将导致这些同步任务的平均同步速度非常低,数据同步速度非常慢;
- 用户不清楚该如何设置首轮同步的线程数,基本都会设置成一个固定的值,导致同步任务因得不到合理的 CPU 资源而影响同步效率;
- 不同的数据同步任务的重要程度是不一样的,但是同步控制器平等对待接收到的同步线程,导致重要的同步线程因得不到 CPU 资源而无法同步;
- 上述三种情况可能会导致同步任务不稳定。
阿里集团的解决思路:通过目标数据库的元数据估算同步任务的总线程数,以及通过系统预先定义的期望同步速度估算首轮同步的线程数,同时通过数据同步任务的业务优先级决定同步线程的优先级,最终提升同步任务的执行效率和稳定性。
- 具体实现步骤:
- 用户创建数据同步任务,并提交该同步任务;
- 根据系统提前获知及设定的数据,估算该同步任务需要同步的数据量、平均同步速度、首轮运行期望的线程数、需要同步的总线程数;
- 根据需要同步的总线程数将待同步的数据拆分成等数据量的数据块,一个线程处理一个数据块,并将该任务对应的所有线程提交至同步控制器;
- 同步控制器判断需要同步的总线程数是否大于首轮运行期望的线程数,若大于,则跳转至(5);若不大于,则跳转至(6);
- 同步控制器采用多机多线程的数据同步模式,准备该任务的第一轮线程的调度,优先发送等待时间最长、优先级最高且同一任务的线程;
- 同步控制器准备一定数量(期望首轮线程数 - 总线程数)的虚拟线程,采用单机多线程的数据同步模式,准备该任务响应实体线程和虚拟线程的调度,优先发送等待时间最长、优先级最高且单机 CPU 剩余资源可以支持首轮所有线程数且同一任务的线程,如果没有满足条件的机器,则选择 CPU 剩余资源最多的机器进行首轮发送。
- 数据任务同步开始,并等待完成;
- 数据任务同步结束。
三:数据漂移
通常把从源系统同步进入数据仓库的第一层数据成为 ODS层数据。
数据漂移:指 ODS 表的同一个业务日期数据中包含前一天或后一天凌晨附近的数据,或者丢失当天的变更数据。
由于 ODS 需要承接面向历史的细节数据查询需求,这就需要物理落地到数据仓库的 ODS 表按时间段来切分进行分区存储;(通常的做法是按某些时间戳字段来切分,而实际上往往由于时间戳字段的准确性问题导致发生数据漂移)
问题:通常,时间戳分为四类(根据其中的某一个字段来切分 ODS 表,导致产生数据漂移):
- 数据库表中用来表示数据记录更新时间的时间戳字段(假设这类字段叫 modified_time);
- 数据库日志中用来表示数据记录更新时间的时间戳字段(假设这类字段叫 log_time);
- 数据库表中用来记录具体业务过程发生时间的时间戳字段(假设这类字段叫 proc_time);
- 标识数据记录被抽取到时间的时间戳字段(假设这类字段叫 extract_time);
理论上以上几个时间应该是一致的,但实际生产中,这几个时间往往会出现差异,可能的原因有一下几点:
- 由于数据抽取是需要时间的,extract_time 往往会晚于前三个时间;
- 前台业务系统手工订正数据时未更新 modified_time;
- 由于网络或者系统压力问题,log_time 或者 modified_time 会晚于 proc_time;
数据漂移场景:
- 根据 extract_time 来获取数据(ODS 数据)。(数据漂移问题最明显)
- 根据 modified_time 限制。(最常见,但是往往会发生不更新 modified_time 而导致的数据遗漏,或者凌晨事件产生的数据记录漂移到前一天。)
- 根据 log_time 限制。(由于网络或者系统压力问题,log_time 会晚于 proc_time,从而导致凌晨时间产生的数据记录漂移到后一天。)(例:天猫“双11”大促期间,凌晨时间产生的数据非常大,用户支付需要调用多个接口,导致 log_time 晚于实际的支付时间)
- 根据 proc_time 限制。(得到的 ODS 表只是包含一个业务过程所产生的记录,会遗漏很多其他过程的变化记录,违背了 ODS 和业务系统保持一致的设计原则)
两种解决方法:
(1)多获取后一天的数据
- ODS 每个时间分区中,向前、向后多冗余一些数据,保障数据智慧多不会少,而具体的数据切分让下游根据自身不同的业务场景用不同的业务时间 proc_time 来限制。
- 缺点:产生数据误差。(例:一个订单是当天支付的,但是第二条凌晨申请退款关闭了该订单,那么这条记录的订单状态会被更新,下游在统计支付订单状态时会出现错误)
(2)通过多个时间戳字段限制时间来获取相对准确的数据
- 首先,根据 log_time 分别冗余前一天最后 15 分钟的数据和后一天凌晨开始 15 分钟的数据,并用 modified_time 过滤非当天数据,确保数据不会因为系统问题而遗漏;
- 然后,根据 log_time 获取后一天 15分钟的数据;针对此数据,按照主键根据 log_time 做升级排列去重。(因为最终需要获取的是最接近当天记录变化的数据(数据库日志将保留所有变化的数据,但是落地到 ODS 表的是根据主键去重获取最后状态变化的数据))
- 最后,将前两步的结果数据做全外连接,通过限制业务时间 proc_time 来获取所需要的数据。
例:淘宝交易订单的数据漂移;
- “双 11”的交易订单中,有一大批在 11 月 11 日 23:59:59 左右支付的交易订单漂移到了 12 日。主要原因是用户下单支付后系统需要调用支付宝的接口而有所延迟,从而导致这些订单最终生成的时间咵天了。即 modified_time 和 log_time 都晚于 proc_time。
- 难点:
- 如果订单只有一个支付业务过程,则可以用支付时间类限制就能获取到正确的数据。但是往往实际订单有多个业务过程:下单、支付、成功,每个业务过程都有响应的时间戳字段,并不只有支付数据会漂移。
- 若果直接通过多获取后一天的数据,然后限制这些时间,则可以获取到相关数据,倒数后一天的数据可能已经更新多次,直接获取到的那条记录已经是更新多次后的状态,数据的准确性存在问题。
- 解决方法:
- 根据实际情况获取后一天 15 分钟的数据,并限制多个业务过程的时间戳字段(下单、支付、成功)都是 “双 11” 当天的,然后对这些数据按照订单的 modified_time 做升序排列,获取每个订单首次数据变更的那条记录;
- 根据 log_time 分别冗余前一天最后 15 分钟的数据和后一天凌晨开始 15 分钟的数据,并用 modified_time 过滤非当天数据,针对每个订单按照 log_time 进行降序排列,去每个订单当天最后一次数据变更的那条记录。
- 最后将两份数据根据订单做全外连接,精漂移数据回补到当天数据中。