准确地来说,在大数据里很多时候是ELTL,但是我们仍然保留历史的称呼用ETL来描述从抽数到提供应用之间的所有数据处理步骤。
ETL的第一步总是避不开从业务源系统抽取数据到落地层(Staging)。实践中,大部分时候大厂都用ODS来命名,歪果仁通常称为STG,这里只是叫法不同,作用是一样的——一次读取以缓冲对源系统数据的访问。
EL工具市面上比较常用的是sqoop和dataX,也有通过binlog消费日志再转成增量文件的,还有用flink做实时数仓落地层的同步方案。但不论用什么方式,最终落地层的表结构基本上和业务源系统的表结构保持一致(偶尔也会有不一致的情况),而数据上看可能存在两种情况:全量和增量。
全量同步的方式比较简单粗暴,搭配使用sqoop或者datax的同步方式。在同步数据线程数有限且对产出时间有要求的情况下,适合量级上限在大约几千万行左右的表。因为数据量多意味着抽取时间的线性增长,一般超过一小时都无法全量同步的情况下,会考虑采用增量同步的方式。
同步过程中的源数据变化是全量同步需要引起注意的地方。如果在导入数据的时候,业务系统有DML,是有引起数据不一致的潜在可能的。这与DML的操作是否事务级别无关,与幻读也无关。假设业务有个事务操作是要标记一条记录为delete并新增一条记录。但是导入数据可能是个漫长的过程,在事务发生之前就读入了该条要标记为delete的数据,但是在事务提交之后才读到新增的记录,此时就会出现两条记录同时存在的情况。sqoop搭配-Doraoop.import.consistent.read=true的时候对Oracle的SCN(system change number)可以做到读取同个位点。
增量同步没有数据不一致的潜在风险,但它的一个先决条件是能够完整获取增量部分。对于只有追加没有更新和删除操作的日志型数据,只需要一个created_time就可以进行增量同步。稍微复杂点,对于没有物理删除的数据库设计,只需要有created_time和updated_time够了。对于同时还有物理删除操作的数据,需要通过类似MySQL的binlog或者Oracle的GoldenGate获取完整的操作记录,再用这份数据对前一份数据进行合并(新增、更新和删除)以获得一份当天的数据。
存在HBase里的数据往往都是超大表,全量同步需要很长时间。对于HBase的增量同步机制,可以开启一个设置TTL的增量表,在此增量表的基础上,构建一个Hive的外部表映射到该HBase增量表。最后再对旧数据和增量进行合并。但是这种机制要求HBase没有物理删除的操作。因为物理删除的记录无法通过被同步到TTL表里而表现出来。
用flink同步落地层是一个很有价值的方案,flink的exactly-once使得通过实时方式同步有了数据一致性的保障。姑且不论flink能不能满足完整的实时数仓,单从简单的落地层实时同步能缩小数据仓库在整体产出时间方面的提高,它就是值得尝试的。在ETL出现意外的时候,通过缩短数据同步而压缩整个链路的运行时间,能给数据的产出时间提供有力保障。