快速、高质量、稳定的将数据从业务系统接入到数据平台是至关重要的一环。前面的文章中,我们分别提到了关系型数据库的数据接入和非关系型数据库的数据接入。除了来自技术上的挑战,数据接入还会遇到哪些其他挑战呢?
本文将尝试基于项目中的实践,给大家分享一下我们的思考。
一般而言,实施数据接入将可能碰到如下问题:
- 数据源多种多样,常见的比如:各类关系型数据库、
NoSQL
数据库、日志数据等。如何处理这么多种类型的数据源? - 数据接入的策略是什么样的?时机如何选择?频率如何确定?
- 数据接入不能对业务系统的稳定性产生影响,如何实现?
- 数据入库后,如何在数据仓库中进行存储和管理?
- 如何保证多张入库表的数据一致性?
- 如何和业务系统开发团队分工协作?
下面将结合我们在项目中的实践,分享一下我们的思考。
处理多种类型的数据源
关系型数据库数据接入
我们先来看基于关系型数据库的数据源。关系型数据库类型多种多样,常见的如MySQL
Oracle
SQL Server
PostgreSQL
等等,如何支持这些数据库呢?
在大数据平台下,我们可以选择Sqoop
或Spark
的方案。得益于Java
丰富的周边生态(Java
语言提供了对于数据库访问的统一抽象JDBC
,通过实现JDBC
驱动器就实现使用统一的JDBC
API
访问数据,目前市面上几乎所有数据库都提供了驱动器实现),Sqoop
和Spark
都提供了完善的数据库类型支持。
Spark
和Sqoop
起步都很早,但Spark
定位是通用的计算引擎,比起Sqoop
只是定位于Hadoop
下的数据导入导出工具而言,Spark
的功能要复杂和丰富得多。在技术选择上,Sqoop
基于MapReduce
计算引擎实现,Spark
的数据导入导出功能基于自身计算引擎实现。近几年,Spark
由于其灵活性、高性能及成熟稳定的功能,越来越成为大家首选的数据项目框架。
如果查看Spark
和Sqoop
的代码提交频率,即可看到在活跃度上Spark
优于Sqoop
很多。
在功能性上,基于Spark
的方案也不弱于Sqoop
。
Sqoop
支持了常见的数据接入需要的功能,如根据查询条件进行数据选择、并发数量控制、读数据时的事务级别、数据类型转换、增量数据导入等。在导入目的地上,可以支持Hive
、HBase
、文件等。
Spark
同样可以支持这些功能,不过使用了Spark
特有的术语,如:
- 需要根据查询条件进行数据选择时,不仅可以在读取数据时,指定
query
参数,还可以在调用load
得到DataFrame
之后,使用DataFrame
灵活的API
进行数据筛选(Spark
将在执行时可以通过谓词下推将查询条件放在数据库端执行)。 - 需要并发进行数据导入时,可以按照某个字段进行分区,并控制分区数量。
- 需要进行数据转换时,使用
DataFrame
灵活的API
做起来更是得心应手。 - 进行增量数据导入时,可以根据元数据的特点灵活选择参考字段(如数据更新时间、
ID
等),通过编程方式指定查询条件来实现。 - 导入目的地的支持更是广泛,不仅拥有良好的
Hive
兼容性以便可以轻易将数据导入到Hive
,还可以保存各类支持的文件。
根据上面的分析及我们的实践经验,相比Sqoop
,我们会更推荐Spark
来完成数据接入。
其他数据源数据接入
对于非关系型数据库的数据接入,在前一篇文章(非关系型数据库的数据接入)中我们进行了很多的讨论。
总结一下,对于无schema
或者schema
比较自由的数据库,有如下经验可能值得借鉴:
- 考虑使用数据库自身提供的库来进行数据读取,避免直接使用
Spark
进行读取数据。这将很大程度上避免数据读取过程中的异常。 - 接入数据到数据平台时,考虑用
json
格式存储原始数据,这可以避免在接入数据时进行数据解析带来的问题,使得数据接入过程更稳定。
对于其他基于文件的数据源,可以考虑的做法是先将文件复制到数据平台HDFS
,然后用Spark
读取HDFS
中的文件,然后再写入Hive
(或其他)数据仓库。
数据接入策略
数据接入可以简单的分为全量数据接入和增量数据接入。全量数据接入是指每天都将所有数据从业务系统复制到数据仓库,适合数据量比较小或者没有数据更新时间字段的数据表。增量数据接入是指每天将改变的数据接入到数据仓库,是最常见的数据接入方式。
一般而言,增量数据接入可以分为两个步骤完成:
- 第一次全量数据接入。通过此步骤一次性将所有历史业务数据导入到数据平台。
- 增量数据接入。在第一次全量数据接入之后,每天业务系统的增量数据(新增或修改)导入到数据平台。
听起来很简单,但是仔细分析一下,就会发现很多问题。
第一次全量数据接入需要应对所有历史数据,因此可能需要应对大数据量的问题。
后续增量数据接入需要注意数据接入的范围,通常是前一天所有的新增或修改的数据。这里面存在以下几个问题:
- 如果数据接入的时间是凌晨1点,那么从0点到1点间可能有数据更新,这些数据如何入库呢?一般而言,可以选择
创建时间在昨天
或更新时间在昨天
的数据进行接入,这样就可以包括从0点到1点间的所有更新。但同时这也有一个副作用,就是有可能包含第二天的某些更新的数据。比如,昨天创建了一条数据,今天0点30分做了数据更新,这条数据会同时存在于昨天和今天的接入数据中(数据重复入库)。 - 如果业务系统的数据里面没有标识创建时间的字段,但有标识更新时间的字段,此时可以选择
更新时间在昨天
的数据范围进行数据接入,同时避免了数据重复入库问题。 - 如果业务系统的数据里面既没有标识创建时间的字段也没有标识更新时间的字段,此时我们无法针对数据进行增量接入。此时应该考虑让业务系统做改进加入相关字段(针对大表),或者退化为每天做全量数据接入。
- 数据最好不要有删除的情况,这需要跟业务系统开发团队确认。如果存在数据删除,我们无法快速通过单表来找到被删除的数据(理论上可以实现,比如可以比对数据仓库中的所有数据业务主键与业务系统中的对应所有主键,但是技术实现上存在很大挑战)。此时可以让业务系统做改进,比如,将所有被删除数据的主键保存在另一张数据库表中。也可以考虑退化为使用全量接入的策略。
除了上面提到的问题之外,还需要注意的问题是如何应对失败。比如,某一天,由于业务系统和数据平台网络断联,此时数据接入随之失败,如何恢复数据导入任务呢?
如果业务系统允许或者存在从库,则可以随时重新触发任务。越早触发任务,丢失的数据更新越少。如果业务系统只允许在系统闲时进行数据接入,则只能在下一个可接入数据的时段重新触发前一天的数据接入任务。
从数据接入频率来看,目前多是T+1
的方式,即每天定时数据接入。这样的数据接入频率一般可以保证第二天可以看前一天的数据。
数据接入对业务系统的影响
在多数业务相对单一的互联网公司构建数据平台时,这个问题可能并不是一个值得关注的问题。
但是,对于很多相对传统的企业,他们通常拥有很多业务系统,不少系统可能开发于一二十年前并经过了长时间的逐步迭代,不同的系统可能由不同的供应商开发而成,不同的系统可能需要支持不同的业务线。基于这样的现实情况,谈论数据接入对业务系统的影响就显得很有必要,因为:
- 业务系统可能还没实现读写分离,因此没有只读的从数据库
- 业务系统可能需要支持某个关键的业务线,需要24小时在线
仔细设计方案,小心导入数据
数据接入的一个重要目标是在尽量降低对业务系统影响的情况下完成。对于没有只读从库的业务系统,如何实现这一点呢?下面尝试对不同的数据接入任务进行分析。
全量数据接入时,由于需要应对所有历史数据,此时的数据量可能会非常大。我们在进行这一步时,通常要面对的是数千万行数亿行,数十到数百GB的数据。进行如此大量的数据传输,需要仔细设计方案。通常可以参考如下步骤完成:
- 在业务系统中统计数据量大小,包括数据行数,数据文件大小等。很多数据库都支持统计信息查询,比如
Oracle
下执行查询SELECT segment_name, bytes/1048576 FROM DBA_SEGMENTS WHERE segment_name='table name' AND segment_type='TABLE'
可得到数据大小 - 测试数据平台和业务系统之间的网络带宽,根据网络带宽预估两个系统间数据传输的时间
- 根据数据传输时间预估,制定数据传输计划,包括每个待接入数据库表的计划接入时间,接入失败的处理方式,超时的处理方式等
- 根据数据导入计划,与业务系统开发团队商定一个合适的时间进行数据导入(通常可以安排在周末或者夜间系统闲时)
- 按照数据导入计划进行执行,并处理数据导入过程中的各种异常情况
增量数据接入时,面对的数据量通常比较小,此时和业务系统商定一个合适的时间进行数据导入即可。
除了以上问题,还需要考虑数据读取的并发数,如果并发太高,可能给业务系统造成很大压力。通常业务系统会控制数据库最大连接数,这也给数据接入时的并发数设置了限制。一般而言,并发数并不会成为数据接入速度的瓶颈,最大的瓶颈常常来自网络带宽。我们通常可以采用较小的并发数读取数据,通过设置Spark
的numPartitions
参数可以达到控制并发数的目的。
尝试自建从数据库
另一个值得借鉴的做法是,在数据平台搭建一个跟业务系统数据库同样的数据库(可视为帮助业务系统建立了一个只为数据接入使用的从数据库,下文直接称从库),用以辅助进行数据导入。这可以带来以下几个好处:
- 数据导入更稳定和高效。可以先使用业务数据库本身提供的数据导入导出工具将业务数据库的数据导入到从库,这一步一般可以非常稳定而高效。
- 可以无忧的使用
Spark
(或其他工具)将数据从从库接入到数据平台。因为是从从库进行数据读取,所以根本无需考虑并发读数据对业务系统的影响,也无需考虑数据读取的时间。 - 可以辅助开发数据接入代码。因为对业务系统无影响,所以我们完全可以在这个数据库上面进行参数的调整和测试,无需担心对业务的影响。
在我们的项目实践中,针对没有从库的Oracle
业务系统数据库,我们用docker搭建了一个Oracle
数据库用于充当业务系统数据库的从库,大大提高了数据接入的稳定性和开发效率。
在导入数据到从库时,可以忽略索引,这可以有效减少数据库需要使用的存储空间,也可以加快数据导入的速度。
在增量数据接入时,我们可以考虑新建一张数据库表来存储数据,并且只存储增量的数据,此时不仅可以大大减小数据量从而加快数据接入的过程,还可以用于应对潜在的数据Schema
变更。
在数据仓库中进行数据存储和管理
现在我们应该可以顺利把数据导入到数据平台了,那么导入的数据要怎么存储和管理呢?首先要找个地方存,那就是数据仓库(Data Warehouse,简称DW
)了,通常以Hive
数据库来实现。数据平台可以视为以数据仓库为中心存储的平台。
设计贴源层
很多数据仓库建设的文章中都会建议我们设计一个专门用于存储业务系统数据的层,名为贴源层,或ODS
(Operational Data Store)层。贴源层的数据需要保持和业务系统一致,不做任何的加工。
设计贴源层的好处是:
- 这一层的逻辑比较简单,通常稳定性可以得到保证。
- 贴源层保存了所有的业务系统数据,因为原始数据始终存在,所以上层可以随时进行数据查询,这带来了很大的灵活性。
- 拥有随时可查询的数据,可以很好的支持上层数据开发过程中的代码调试。
- 当我们发现上层的数据处理逻辑需要修改时,可以更从容的完成,无需考虑数据任务运行的时间及对业务系统带来的影响。
我们也同样建议设计一个贴源层用于存储来自业务系统的原始数据。相比多增加一层带来的数据存储成本,其优势非常明显。
设计分区字段
如何存储数据呢?Hive
数据库支持分区表,可以为数据存储提供支持。
简单来说,我们可以为所有业务系统的表增加设计一个分区字段用于存储当次接入的数据。此字段可以为整型或字符串类型,其值为数据更新时间。
如果按天进行数据接入,则存储数据更新的当天,比如可以存储为20210520
。如果更频繁的每小时进行数据接入,可以将此分区字段的值扩展支持小时即可。需要注意的是,这里的分区字段值最好和数据更新时间对齐,这可以大大方便后续团队成员理解数据、查询数据,从而避免潜在出问题的风险。
分区字段可以很好的应对全量数据接入和增量数据接入的策略。增量的情况下需要注意,第一个分区为全量数据分区,通常数据量会特别大,而且数据没有历史。后续所有更上层的数据加工都要基于这个基本认识,比如:
- 在进行数据处理时,如果涉及全量分区,可能要配置更多的计算资源。
- 处理包含全量分区的数据时,需要考虑数据倾斜的问题,可能会由于第一个分区太大影响整个数据任务执行时间。
- 进行表间数据关联时,如果当前数据分区的时间早于关联表的数据分区时间,则应该关联到关联表的全量数据分区中的数据。
多张表入库时的数据一致性
由于数据接入的过程常常会涉及多张数据库表的数据读取,那么一个很自然的问题是,数据一致性如何保证?
这是一个数据仓库实现的难点,有很多种情况可以影响数据一致性,比如:
- 多张数据库表读数据的时间很可能不一样
- 大量多表数据读取需要较长的读数据时间,难以在一个数据库事务内完成
- 数据库配置的事务隔离级别不够,可能导致读到脏数据
数据分析对一致性的要求
在实际的数据仓库建设过程中,由于数据一致性难以保证,我们常常会一定程度上忽略数据强一致性。这可能带来轻微的数据不准确问题,一般而言,这不会带来太大的问题。因为数据平台是主要是为了支持数据分析,而分析常常是统计意义上的结果,所以统计上不会由于有很小的偏差而带来大的影响。这些统计结果一般也是为支持公司决策,公司决策自然也不会轻易受到某一个数值的微小变化的影响。比如,公司的产品销量分析,即便有几笔订单由于数据不一致没有统计到最终的销量中,一般也不会带来太大影响。
虽然多数情况下的数据分析对数据一致性不太敏感,但是还是存在很多对数据准确性要求很高的场景。比如:
- 考虑大宗商品的销售,如果少统计了一个订单,可能带来统计值的较大偏差
- 考虑零售的场景,如果经销商可以根据销量等级的不同而获得厂家的不同补贴,这时,即便一个订单也不允许统计出错,因为这会影响到经销商的收益
应对强一致性要求
对于这类对数据一致性很敏感的场景需要如何处理呢?这里有一些建议。
一是可以尝试在一个数据库事务中读取所有相关表的数据。如果数据源对应的数据量较小,特别是对于增量数据读取,也是具备较高的可行性的。
二是可以配合业务系统一起实现高一致性的数据入库。如果有来自业务系统的配合,一致性相对更容易实现。比如,如果业务系统允许每晚停止服务(或停止数据修改,只允许读数据的业务)一段时间,则可以在停止服务(或只读)时进行数据接入。
三是可以提供补数据的机制。比如,如果我们发现某些统计指标与实际的相差几个数,可以提供一个流程来补充没统计到的数据。简单来说,这可以通过:1. 相关人员进行邮件确认;2. 在计算出来的统计值上面进行最终结果调整。当然,如果有比较充足的时间,也可以实现一个支持系统用于辅助实现这个流程。
总之,要实现更强的数据一致性,所需的成本也是更高的。好的一点在于,在实践过程中,我们常常不需要这样强的数据一致性。当需要保证很强的数据一致性时,可能需要综合具体情况来判断什么样的处理方式是更好的。
和业务系统开发团队分工协作
上面谈了很多关于如何接入数据,如何在数据仓库中存储数据的经验,其中很多都是基于一个基本假设,那就是这些工作是由一个独立的数据平台团队来完成。有的同学可能会有一个疑惑,为什么数据接入这件事是数据平台团队来做?是不是可以直接交给业务团队来做?业务团队来做这件事不是更容易吗?
企业组织结构和团队分工
这涉及到企业组织结构和团队分工的问题,实际情况常常十分复杂,每个企业都可能根据自身实际情况不同而不同。
一般而言,实际情况常常是由一个独立的数据团队来负责此类工作。原因在于:
- 业务系统开发团队会更专注在自身业务功能的开发,对于数据的关注不足
- 业务系统开发团队具备的数据相关专业能力(包括工程能力、技术能力等)相对较弱,而数据平台建设需要较强的此类能力
- 业务系统开发团队本身可能已经是一个大的团队,如果加入更多的团队角色,将进一步增加团队大小而带来团队管理问题
如果说企业的数据平台建设已经比较完善,或者企业内部的IT技术能力较强,则情况可能不一样。在一些大型的以软件技术为核心的企业,比如一些大型的商业银行,他们的情况可能是这样:
- 可能已经建设了一个独立的数据团队,负责提供各类数据工具给各个业务系统开发团队使用
- 软件基础设施可能已经比较完备,数据接入已经有完善的内部系统做支持
- 可能有较好的培训体系,可以通过培训来提升业务系统开发团队的数据意识和数据专业能力
此时,业务系统开发团队可能只需要通过简单的几步操作即可实现数据接入数据平台。如果是这样,通常不仅数据接入可以交给业务系统团队自己完成,甚至上层的数据开发、报表展示等工作也可以由业务系统团队完成。数据团队在此时的工作重点会变为:
- 为业务系统团队开发各类数据工具或系统
- 提供技术支持和咨询服务
- 提供培训服务
所以,如果我们考虑数据团队的组织和分工,不同企业可能大不一样,但是可能又都是适合企业实际情况的。
缓解两个团队间的矛盾
如果是由一个独立的数据团队去完成数据接入以及后续的数据加工处理、指标计算等工作,这常常会带来一个问题,那就是需要和业务系统团队较为紧密的协作。这样的协作常常不够顺畅,为什么呢?究其原因,两个团队没有共享同一个目标。数据团队一般基于各业务系统的数据,计算指标,输出报表,或者建立机器学习模型进行业务分析等。其目标是支持业务分析或支持决策。而业务系统团队的目标是开发新功能,维护系统稳定,支持业务运转。
目标不一致会带来很多问题。比如,数据团队觉得优先级高的事情,在业务系统团队看来优先级很低。更具体一点的例子是,如果业务系统中没有数据删除,那么数据接入就可以较容易的实现,但要使得业务系统保留所有数据,这给业务系统的开发带来了更多的看起来没必要的麻烦。
如何缓解两个团队的矛盾呢?一般情况下,可能需要数据团队做出一定让步,因为在数据的价值还没有体现出来时,从企业角度来看,我们需要优先保证业务的正常运转。然后,在此基础上,数据团队可以引导业务系统团队人员进行数据消费,帮助他们认识数据对他们的价值。包括:
- 给业务系统团队人员培训数据相关的知识和技能
- 将加工出来的数据给到业务系统团队进行分析
- 引导业务系统团队进行跟自己业务相关的简单的数据分析,帮忙他们通过数据改进自身业务
总体上看,要想两个团队能友好的高效的合作,还需要双方求同存异,共同为企业大的目标一起努力才行。这可能需要更多的企业文化建设工作。
其他问题
数据接入的过程中常常还会伴随很多其他问题,这里列举一些比较典型的问题,与大家一起讨论。
业务系统提供专用接口进行数据接入
我们在进行数据接入的时候,业务系统团队担心他们数据库变更对数据平台造成影响,可能会建议由他们提供API
进行数据查询,而不是直接提供数据库访问接口。
在我们看来,由业务系统开发API
进行数据导出并不是一种好的方式。主要理由是:
- 会给业务系统团队带来额外的开发和维护成本
- 增加了两个团队沟通协作成本(需要讨论
API
(或数据导出任务)形式,进行联调等) - 数据团队需要等待业务系统团队进行
API
(或数据导出任务)开发和上线,效率较低 - 添加新数据效率低。如果发现之前的
API
(或数据导出任务)中的数据不能满足分析需要,则需要和业务系统团队重新沟通API
(或数据导出任务)设计,然后交由业务系统团队开发、测试、上线,整个周期特别长
当然,通过开发API
(或数据导出任务)的方式进行数据对接也有其好处,那就是两个系统间的接口相对稳定。但是相比其带来的成本而言,这个优势显得不够明显。
业务系统开发导出数据的任务
比开发特定API
接口进行数据对接要更好一些的是,业务系统可以开发数据导出任务,定期运行此任务导出数据。这会给业务系统团队带来一定的开发成本,但是不失为一种保障业务系统稳定运行的好的方式。
此时,我们会建议使用强类型的专用的数据导出格式,而不是基于csv
。比如,如果业务系统数据库是MySQL
,则考虑使用mysqldump
工具进行数据导出。如果是Oracle
,则考虑使用exp
/expdp
工具。如果是MongoDB
,使用mongodump
。
使用专用的数据导出格式的好处是:
- 几乎不需要业务系统团队编写代码就可以实现数据导出
- 数据导出速度通常最快且最稳定
- 数据可以很容易的导入到跟源系统同样版本的数据库中
如果使用某种中间格式,比如csv
,其问题在于:
- 需要增加不少额外的开发成本
csv
会丢失数据类型,比如在csv
文件中,对于整型数值、浮点型数值和字符串的区分比较困难,这将带来额外的无必要的数据平台数据接入逻辑csv
在处理空字符串、null
值、换行符等特殊值时存在问题,在处理一些不可见的二进制特殊字符也会存在问题,需要在数据接入时做更多的容错处理csv
可能带来列错位的问题
在实际的项目实践过程中,我们耗费了很大的精力在应对csv
格式的数据接入这件事上,最后的结果也不尽如人意。所以,一般而言,我们会强烈建议避免使用中间文件格式进行数据接入。
虽然开发数据导出任务进行数据接入这种方式看起来不错,但是综合起来看,更为推荐的方式还是直接进行数据库对接,比如:
- 由业务系统团队建立从数据库用于各类数据同步和分析,数据接入通过从数据库完成。
- 以只读账号直接连接业务系统生产数据库,在系统闲时进行数据访问。
标准化的数据接入工具是否有必要
当前很多云服务提供商也发布了很多相关的数据接入软件,使用这些软件,通常只需要简单的配置即可实现数据接入其云上的数据平台。那么我们是否可以从这样的工具或服务中受益呢?
现在的市场上有大量类似的数据产品存在,但是,事实上通常这种数据产品在很多企业中难以落地实施。经过上面的分析,可以知道,很多情况下,数据接入这个问题并不是技术上可以直接解决的。
这里面涉及:
- 网络传输问题。比如,业务系统可能并不在云上,或者并不在当前云服务提供商的云上。还比如,可能业务系统的数据量太大,传输很慢。
- 数据安全问题。很多企业会担心数据上云之后带来更大的数据安全风险。
- 沟通协作问题。根据上面的分析,对于数据接入工作,有很多的时间都花在了团队协作上,这是工具不能解决的问题。
- 工具灵活性问题。业务系统数据库可能多种多样,这些数据产品是否可以支持这么多类型的数据库?是否支持足够好?如果我们想要定义数据接入的分区字段的值,是否可以很容易的实现?
综合以上这些因素考虑,我们不应该被一个看起来很好用的数据接入UI
界面所迷惑,而是应该立足企业具体情况,根据情况选择更合适的方式。
总结
本文从数据接入策略开始,依次讨论了数据接入对业务系统的影响,数据应该如何在数据仓库中存储,接入的数据的一致性,团队组织和分工等问题。其中,分享了很多来自我们的真实项目实践中的经验。
数据接入到数据平台是数据平台构建的第一步,也是极为关键的一步。在实际项目过程中,我们常常需要花费大量的时间在这一步上。为了应对各种数据接入的挑战,我们需要保持清晰的认知,综合考虑各类因素,以便找出最优的解决办法。