引子 传统数据仓库类项目的建设,主要难点如下:
我们通过一个真实的案例,针对以上难点进行具体分析以及如何去解决,同时来思考,在各种新技术层出不穷的今天,数仓应该何去何从?
项目背景
200X年左右某连锁企业实施的第一个数据仓库项目:收银员防舞弊稽核平台。
什么是收银员的舞弊行为?
舞弊,通俗的讲,就是“偷钱”。各行各业的实体店,由于营收环节存在的漏洞,收银员“偷钱”的情况时有发生。那怎么“偷钱”呢?举几个简单的例子:订酒店,消费者用人民币进行支付,收银员可以通过用积分兑换的方式,替换掉消费者的订单;快餐店,收银员可以用优惠券替换掉消费者单点的汉堡可乐订单。
业务难点
人工稽核效率低,一个店经理需要两个小时,才能找到2个舞弊收银员;
舞弊行为多样性,舞弊行为不断变化,无法通过某种数据分析以达到长期效果,业务人员需要不断探索和改变稽核KPI,并且及时得到反馈,才能发现真正的舞弊行为。
技术难点
数据每天增量100G,窗口进行数据处理,时间要求在3小时内完成;
常用数据在3T,所有用户(万人级别)会在短时间内同时访问数仓。
解决方案
业务解决方案
针对业务需求,我们需要为稽核人员提供一个数据及时、数据准确、有科学决策依据的智能化稽核业务平台,同时,该平台还需要具备灵活可扩展的能力,以便满足稽核人员探索的需求。
解决方案的核心思路如下:
业务数据处理流程如下:
收银员的“偷钱”动作多种多样,但是对应到POS机的操作是有限的。最终我们与该企业一起,对POS机操作定义了10种舞弊行为(由于涉及到行业机密,不方便透露具体行为),稽核KPI建立在定义的POS机舞弊行为之上。
由于KPI的计算规则不断调整,需要一个完全动态的稽核引擎做支撑才能满足稽核人员不断探索的需求。实现动态引擎的思路就是一生二,二生三,三生万物。任何一个复杂的计算公式,都可以拆分成简单的原子计算,因此,我们可以提供两类KPI:基础KPI和组合KPI。基础KPI即原子粒度的KPI,组合KPI是通过组合形成的KPI,组合KPI可以由基础KPI和基础KPI或者基础KPI和组合KPI两两组成。基础KPI可以通过直接计算或者通过定义的舞弊行为打标实现。
下面举例说明基础KPI和组合KPI:
比如我们想统计每天的平均客单价,正常的做法是通过一条SQL语句实现,SQL示例:SELECT SUM(销售金额)/SUM(订单数量) AS 客单价 FROM 销售表 GROUP BY 天。
下面我们看一下通过规则配置如何实现:
基础KPI:每天的销售额KPI(A)、每天的销售数量KPI(B)
组合KPI:平均客单价KPI(C) C=A/B
稽核引擎计算KPI (C)就可以得到想要的结果。
每当稽核KPI需要新增或者调整的时候,只需要业务人员在页面更改配置规则,稽核引擎会自动识别,这样不仅满足了用户不断探索的需求,同时,只要重新运行稽核引擎数据,就可以快速的得到反馈,大大的提高了数据的时效性。
稽核引擎的逻辑结构如下:
技术解决方案
由于甲方当时对技术选型有一定的倾向性,所以最终技术选型定为SQL Server2008R2 + SSIS。系统的架构采用经典的三层架构,如下图所示:
数据每天增量100G,常用数据3T这样级别的数仓,在当时的计算能力和存储能力的条件下,处理如此庞大的数据量有着相当大的挑战。我们主要通过数据分层和任务拆分两个方面并结合其他方面来解决这个难点。
以下针对各个方面进行详细介绍。
数据分层
数据分层落地,同时存储对齐,以提高并发计算能力和最大限度的利用存储。
数据分层的好处是减少数据处理程序复杂性,数据不同时在读写状态,各层可并发处理,提高系统整体性能。我们把数据处理分为八层,数据接口(上游系统数据)、基础数据(Dim数据)、收银行为(舞弊行为打标数据)、基础KPI、组合KPI、追踪结果、报表反馈、日志等。
整个处理流程分层次进行处理,每一层数据都有自己的日志队列,由日志队列负责驱动整个计算流程,保证数据层与层之间的幂等性,ETL调度任务可以随时停止和重启,同时,数据仓库分区文件放置在不同的逻辑磁盘,逻辑磁盘直接对应底层物理磁盘,实现存储对齐。
数据分层一定要深入业务,尽量细分,做到性能与耦合的平衡,如果层与层之间有复杂的逻辑关系,那么一定是分层存在问题。
在本案例中,稽核引擎的分层设计,日志驱动流程的分层设计,直到今天,依旧有效。
任务拆分
利用MapReduce概念,把增量100G的数据拆分成多批次多任务并发运行。
此项目中定义的最小粒度为店天,一个店一天的数据为一个事务。后端数据处理的目标是根据参数配置信息,多线程并发处理数据,充分利用硬件和存储处理性能,提高处理速度。如果硬件成本允许,计算引擎的执行ETL可采用分布式部署。
具体架构如下图所示:
日志处理
需要提供统一的日志记录方式,完善统一的日志处理,可以大大的降低运维成本。
控制流的错误日志使用SSIS自带的日志功能,日志数据记录到dbo.sysssislog表中,日志的事件类型建议为:OnError、OnTaskFailed、OnWarning。
数据流日志使用数据转储的方式,需要进行存储的转换任务包括查找转换、派生转换、脚本转换及条件性拆分。
查找转换主要记录未匹配的数据记录,一般将未匹配输出的数据设置为使用未匹配输出流,并使用派生转换添加匹配失败的字段名后记入转储表。
派生转换主要记录类型转换失败或截断错误,可直接使用错误数据流,并使用派生转换添加派生转换的任务名称后记入转储表。
对于重要的脚本转换也需要将导致数据异常的数据进行转储,例如在脚本中导致值溢出、不满足任何控制流分支的数据等,可以通过在脚本转换中定义额外的错误输出数据流实现。
条件性拆分需要对所有条件输出进行处理,丢弃的数据进行行计数后根据业务逻辑决定是否记录额外的日志表,对于确定逻辑错误的数据需要使用派生转换增加错误条件名称后记录转储表。
错误隔离
避免将低质量的数据不断引入目标数据系统,从而限制数据错误的扩散甚至放大。清晰地定义数据处理逻辑中的错误级别以及ETL对不同级别错误的处理方法,例如丢弃、引入默认值或中断执行。数仓项目的数据质量对上游系统的依赖度非常高,所以要利用好错误隔离,实现业务特征数据白名单进入系统。
内存管理
由于数据库是SQL server,对内存管理不够完善,所以必须应用层自行释放内存。微软白皮书上并不建议释放内存,但是经过性能对比,自行释放内存的性能会比操作系统管理内存提高20%以上。
存储过程的使用
考虑到开发人员的技术储备因素,在本项目中一开始使用存储过程实现,但是后面考虑性能、日志体系完整、代码的维护以及未来的迁移成本,最终推翻重来,使用ETL数据流组件,放弃了存储过程。
数据库物理建模
星型结构,尽量使用整型,包括日期(YYYYMMDD)和金额(使用精度实现)字段,字段范围要大于数据源。
KPI统计口径
这是业界的一个难题,至今无法解决。
但是有一个原则:深入业务,深入数据,不要相信任何甲方IT和业务人员,一切以数据说话。
异步报表
这是时代的过渡产物,由于当时查询性能有限,前台页面提交查询条件,后台ETL分拆任务,实现大数据量的菜篮子分析,百亿级数据,查询速度在10分钟以内。
CUBE template
这也是时代的过渡产物,由于微软的SSAS并发性能比较差,数据量大(单个CUBE超2T),用户量多的时候,性能会指数级下降,因此我们给用户提供了属于自己的微CUBE,用户提交查询条件,后台自动生成CUBE,刷新数据后,供用户下载,自行分析研究。
案例小结
项目技术指标:数据仓库服务器配置为24Core,64G,关系数据库存储为SAS 3T,在2小时内处理完100G的增量数据,运行ETL期间,CPU、内存均在90%左右,IOPS基本达到存储峰值。
在项目实施过程中,由于经验欠缺,走了很多弯路,最终满足IT和业务用户所有目标,稽核人员可以根据舞弊人员的行为动态调整KPI,并且在20分钟内完成舞弊人员稽核及追踪。
上线X年后,开始盈利(舞弊人员退回赃款),稳定运行至今。
案例升级
十年过去了,这个案例在2019年的今天,我们应该如何实施?项目的难点是否发生了改变?
带着这两个问题,与行业相关业务和IT人员有过深入的交流,大家一致认为:
升级版的业务难点
随着电子支付的普及,业务模式不断变化,舞弊行为的识别难度增加;
在某些大促的时间段内,需要对某些重点优惠券回收情况进行实时的跟踪。
升级版的技术难点
单纯的收银行为已经无法有效识别出舞弊行为,需要基于大数据和AI技术对多种数据源进行分析计算,并把结果及时推送到稽核人员。
升级版解决方案
在现有业务模式下,业务方案的主要思路较之前没有太大改变,只是用户舞弊的行为已经不能只通过制定规则来稽核,还需要通过多个系统的交叉分析和AI技术来进行深层
的挖掘。
下图是升级版的业务数据流程:
现有舞弊行为举例说明:
员工兑换产品券飞单,员工使用从顾客手中盗刷的积分兑换各种产品券用于自己消费。账户获取了很多积分,积分获取时的特征有:频率密集、来自于不同支付账户、与收银员高度绑定、消费时大量使用产品券、折扣力度大、结账金额低等等。
需要关联交易数据、产品信息、会员系统、积分系统、调研系统、支付系统等平台,通过AI统计算法,对舞弊行为数据打标,最后把数据推送到稽核人员。
升级版技术解决方案
用户需要关联多种数据源,包括结构化和非结构化数据,同时有实时计算的需求,因此我们推荐的技术解决方案如下:
在现有技术条件下,数仓建设的技术难点已经发生了改变,由原来的对大数据量的处理转变为资源池的运维和监控,同时,日志处理依然是重中之重。
数据分层:
源数据、中间结果数据、实时数据以及AI数据模块化存储,以便可以降低业务复杂度,同时更有效的利用资源池的优势。
日志处理:
在传统数仓要求的基础上,增加了对资源池日志的监控需求,不仅能发现数据问题,还要及时发现资源池可能存在的隐患。
任务拆分:
任务调度时可以加大调度粒度,由传统数仓的店天改为天,但是数据存储时,依然要保存最小粒度数据,以便更容易的发现数据问题,提高运维效率。
错误隔离:
与传统数仓类似,此处不再赘述。
组件选型:
分布式时代下,系统的构建大多是“搭积木”的方式,架构师的职责就是让“积木”之间的缝隙足够小。因此在业务设计上要拆分的足够细,系统对组件的依赖要足够小,以便未来可以进行组件无缝替代。
唯一ID:
本案例中主要稽核收银员,面向的用户都存在会员信息,因此数据打通不是问题,可直接利用业务系统用户标识,同时建立一个虚拟用户ID,以便为将来留出数据接口。
准实时/实时计算:
使用Kafka+Flume收集云POS数据,并提供数据给准实时/实时计算框架(由于在业务场景中,我们需要实现exactly once模式,在流式计算中采用Flink,在批次计算中,采用Spark streaming),数据经过计算后,提供给AI学习平台/风控系统。实时计算与离线计算不同,在大促场景下需要提前做压测和相应的准备工作,这一点需要注意。
数据挖掘:
数据科学家和分析师可以利用Presto/Kylin进行大数据量下的多场景数据探索,为AI算法的落地做强有力的支撑。
资源池的运维和监控:
分布式大数据平台,计算能力提高的同时,运维和监控的成本也带来了质的提高。我们需要对每个资源池进行数据的实时采集,监控资源池的使用状态,动态的调整资源的分配,以保证平台的稳定运行。
总结
根据以上的案例,可以总结出解决数仓建设的主要难点,需要遵守以下几个原则:
数据分层
事务性
可追踪
可移植
数据分层:
优点:在业务建模和数据库建模阶段实现,有助于降低业务复杂度,降低数据处理各阶段的耦合度,同时也有助于评估、分析及追踪数据在不同处理阶段所消耗的系统资源,并依此进行调整,优化硬件配置。
要求:数据分层原则要求清晰地定义各阶段处理数据的界线,并在数据结构以及ETL的实现上清晰地体现这些界线。
事务性:
优点:有助于满足实际业务对数据一致性和完整性的要求。
要求:事务性原则要求明确定义数据处理补偿的机制,以及数据覆盖的粒度及范围。
注意:事务是由最小数据粒度决定,通过批次日志控制,调度实现,禁止使用数据库事务。
可追踪:
优点:有助于提高系统对IT及业务用户的友好程度,降低确认日常数据处理任务结果的复杂性,并有助于提高追踪异常及错误数据的效率。
要求:可追踪原则要求在ETL中植入足够详尽的日志功能,对重要的数据处理操作及触发异常操作的原因及结果进行记录,例如因数据关联失败而丢弃多少条数据、ETL程序执行耗时多少时间等信息。
可移植:
优点:有助于降低数仓部署和升级成本。
要求:可移植原则要求在系统中尽量减少组件依赖,同时在代码中避免引用特殊资源,所有引用的资源必须统一存储在相对独立的地址,例如数据库或XML配置文件,而非环境变量。
随着时代的发展,技术的侧重点不断发生着改变,但是解决问题的方式和分层拆解问题的思想一直没有变化。不管使用什么样的技术,选择什么工具,深入的业务理解和正确的业务分层才是解决问题的基本。
后记和思考 为什么我们会把这十年前的案例总结放出来,是因为我们最近在思考,数据类项目和业务应用类项目的区别,不仅仅是OLTP和OLAP,更多的是机器学习类,数据中台类项目。 数据类项目的管理模式,分析方法、测试方法、技术栈、客户沟通管理、范围管理,运维模式有什么区别? 我们越来越发现数据项目的复杂性,不确定性,但是如果不能总结出一个规律,模式,方法论,那么这类型项目就会有更多的试错成本。 这件事情,对于数据行业的发展是有着重要意义的。
投稿请联系:kshi@thoughtworks.com