ETL数仓测试
前言
datalake架构
离线数据 ODS -> DW -> DM
- https://www.jianshu.com/p/72e395d8cb33
- https://www.cnblogs.com/wang3680/p/11538451.html
- https://blog.csdn.net/hello_java_lcl/article/details/107025192
- ODS层的逻辑主要是做隔离和部分清洗
实时数据
名词解释
名词 | 描述 |
---|---|
源表 | 数据清洗之前的原始数据表 |
目标表 | 数据清洗之后的表 |
单源 | 一个渠道的数据落到一个表里面 |
多源 | 多个渠道的数据落到一个表里面 |
增量更新 | 每次只对需要更新的时间范围进行更新 |
全量更新 | 每次更新时都需要更新整个数据集 |
关联逻辑 | 一组关联逻辑里含3部分:关联关系、过滤条件、聚合取行 |
宽表 | 宽表从字面意义上讲就是字段比较多的数据库表。通常是指业务主题相关的指标、维度、属性关联在一起的一张数据库表。 |
指标类型 | 1、基础指标:指表达业务实体原子量化属性的且不可再分的概念集合,如交易笔数、交易金额、交易用户数等。 2、复合指标:指建立在基础指标之上,通过一定运算规则形成的计算指标集合,如平均用户交易额、资产负债率等。 3、派生指标:指基础指标或复合指标与维度成员、统计属性、管理属性等相结合产生的指标,如交易金额的完成值、计划值,累计值、同比、环比、占比等。 4、标准信息:经过标准化处理的实体非量化属性信息,或分段统计信息,省份(北京、广州等)、缴费方式(缴费、充值)渠道(网厅、直充、专区)、消费金额(30元、50元、100元等)。 |
ETL测试
1、关联逻辑测试
1.1、内容
由数据源一个表或多个表关联并增加一些过滤条件,如果需求中要求根据业务主键分组取其中一条,那么过滤条件中需要有分组取TOP1的逻辑,如DL层根据order_no分组,biz_date倒序>fetch_time倒序>file_line_no倒序的排序规则取top 1记录,能够尽量保证所取的结果比较稳定。
关联的过程如果非常复杂,可能有多个子关联的步骤,本质上一个子关联就是上面的情况。
普通关联一般有以下3部分组成:
- 表之间的join关系;
- 过滤条件,如果业务隔离、无效数据隔离、重复数据取一条;
- 基于前面两步后的取record的逻辑,业务主键相同的数据分组取TOP1。
源主表和目标表关联字段的对应关系有几类:
源主表关联字段 | 目标表关联字段 | 关系 | 备注 |
---|---|---|---|
不为主键 | 为主键 | 多对1 | 分组取一条或者分组合并 |
为主键 | 为主键 | 1对1 | 总量保持不变 |
为主键 | 不为主键 | 1对多 | 如表拆分,ETL中暂未遇到这些情况 |
不为主键 | 不为主键 | 多对多 | 如平移的情况 |
1.2、方法
验证目标表的数据量,实现ETL的关联逻辑将源表关联起来,得到关联之后的数据量,跟实际ETL之后的数据量对比,预期这两个数据量是一致的。
其中对于复杂关联策略,将源表关联起来的时候可以拆成多个步骤来实现,每个步骤的测试过程需要一个中间表,这个中间表的形式可以是新建的一个普通表,或者一个临时表,或者一个视图。
测试join后分组取排序取Top1的情况是否稳定可参考大数据原则中第七个原则的case4。(以后有时间了会再写一篇文章讲讲大数据原则)
关注因关联关系而丢掉的数据量,如果数据量过大,需要相关DA关注并进一步分析是否存在潜在数据问题。
关注因过滤条件而筛掉的数据量,如果数据量过大,需要相关DA关注并进一步分析是否存在潜在数据问题。
1.3、数据质量分析
在需求分析后,对内容和测试方法有了方向后,需要对源数据进行表画像和关联表画像的分析, 评估待测数据是否有质量问题,如果有再跟ETL设计沟通是上游数据问题还是设计问题。
1.4、案例
类型 | 逻辑 | 细节 | 数据质量分析 | 备注 |
---|---|---|---|---|
关联关系 | A left join B on A.a = B.b | A表的关联字段组合起来是否是主键,如果不是主键则B表有数据关联不上发生丢数据 | ||
过滤条件 | order_no is not null | |||
聚合取行 | group by order_no order by xdate desc,ydate.asc | 分组排序取Top1,按order_no分组,根据xdate降序排、ydate升序排,然后取第一条记录 | ||
多层关联逻辑 | 通过多组关联逻辑得到一个或多个中间结果表;再基于中间表等通过一组关联逻辑得到最终ETL的中间表 | 关联逻辑的解释可参看名词解释 |
2、转换测试
2.1、内容
字段取值逻辑
-
通用取值逻辑,通常可以定义一些通用的UDF来处理:
数据格式的合法性,如对于数据源中时间、数值、字符等数据的处理,是否符合数据仓库规则,是否进行统一的转换;
空值的处理,如是否捕获字段空值,或者需要对空值进行替换为其他含义值的处理。
-
业务取值逻辑
值域的有效性,如是否有超出维表或者业务值域的范围;
主键的有效性,如主键是否唯一;
脏数据的处理,如比如不符合业务逻辑的数据。
-
其它
源表数据类型转换成期望的目标表数据类型;
验证源表编码转换成期望的目标表编码;
乱码的检查,如特殊符号或者乱码符号的护理规则;
目标表增加字段的内容和逻辑,如增加了一些ETL时间的字段等;
抽样测试,根据具体情况确定样本量。
2.2、方法
验证目标表数据内容的正确性。
基于1.2中的由ETL的关联关系得到的中间表,抽样或者全量数据基于中间表逐一将单个字段进行逻辑处理,得到期望结果,验证实际结果是否匹配期望结果。
2.3、数据质量分析
在需求分析后,对2.1内容和2.2测试方法有了方向后,需要对源表中数据进行字段画像的分析,评估待测数据是否有质量问题,如果有再跟ETL设计沟通是上游数据问题还是设计问题。
2.4、案例
类型 | 逻辑 | 细节 | 数据质量分析 | 备注 |
---|---|---|---|---|
通用 | 脏数据处理 | 在某一层(例如落到DW时)将脏数据置为空串,或null,且在系统中保持一致 | ||
业务隔离 | 将不符合规则的数据过滤(如电话号码格式不正确) | GRL: 在ods 加一个data_invalid_type 的分区,将合法和不合法数据存储在不同的分区中 | ||
通用 | 字段类型转换 | 字段类型转换处理,遵循两个原则,即含脏数据处理逻辑、只能大转小 | 通用类型的可以定义为UDF:比如在 ETL过程中会对一些敏感数据做加密,可将加密逻辑做成 UDF 函数,可以供所有的表使用 | |
string – > date | if (yyyy-MM-dd HH:mm:ss字符串) {转date类型} else {null} |
E.g.: to_date(‘2019-04-19 10:29:50’) | ||
string – > timestamp | if (yyyy-MM-dd HH:mm:ss字符串) {转timestamp类型} else {null} |
|||
timestamp – >date | if (timestamp) {转date yyyy-MM-dd HH:mm:ss类型} else {null} |
select from_unixtime(cast(timestamp时间戳/ 1000 as int), ‘yyyy-MM-dd HH:mm:ss’); | ||
date(yyyy-MM-dd HH:mm:ss) – >date (yyyy-MM-dd) | 时间字段截取到天 | select substr(date,0,10) | ||
date(yyyy-MMM-dd HH:mm:ss) – >date (yyyyMMMdd) | 时间字段格式转换 | from_unixtime(unix_timestamp(substr(date,0,10),’yyyy-MM-dd’),’yyyyMMdd’) | ||
string – > int | if (数值字符串&在int类型的取值范围) {转int类型} else {null} |
INT/INTEGER (4-byte signed integer, from -2,147,483,648 to 2,147,483,647); Hive: Cast(字段 as int) |
||
int – > bigint | ||||
varchar – > char | ||||
空字符 – > null | select count(1) from 表 where 字段=”; 预期是0 | 减少数据大小,规范数据格式 | ||
通用 | isnull(A,B,C) | |||
窗口函数: row_number() over(partition by XXX order by XXX) |
用于多条记录选取其中一条的情况,如选取用户最后一次购买的产品 | row_number() over( partition by userid order by date desc ) |
||
时间相关 | 计算日期差: datediff(date1,date2) |
用于计算两个日期之间的间隔 | select datediff(to_date(‘2020-12-03’),to_date(‘2020-12-01’)); –2 |
|
计算月份差:months_between() | 用于计算两个日期的月份间隔 | 一:select months_between(‘2020-10-10′,’2020-09-10’); –1 二: select months_between(to_date(‘2020-10-10’),to_date(‘2020-09-01’)); –1.29032258 |
||
取某个月份的同一天add_months() | 如:取一年前的20号 | select add_months(‘2020-12-21’,-12); –2019-12-21 |
||
计算多少天后:date_add() | select date_add(‘2020-09-15’,1); –2020-09-16 |
|||
条件函数 | if(A,B,C) | 如果A满足,则返回B, 否则返回C | select if(1>2,1,0); –0 |
|
case when 条件1 then 结果1 when 条件2 then 结果2 … end |
如果满足条件1 则返回结果1 如果满足条件2 则返回结果2 … |
|||
字符串函数 | substr(order_code,1,2) | 字符串截取 | select substr(‘abcde’,0,2) ; –ab |
|
length() | 返回字符串的长度 | select length(‘abcde’); –5 |
||
instr() | 寻找字符串的位置,没有找到则返回0 | select instr(‘abcde’,’b’); –2 |
||
split() | 字符串分割 | select split(‘EI4331_1′,’_’)//返回数组类型 [“EI4331″,”1”] select split(‘EI4331_1′,’_’)[0] EI4331 |
||
regexp_replace() | 字符串替换函数 | select regexp_replace(‘abcdef’,’c’,’g’); –abgdef |
||
3、更新策略测试
3.1、内容
数据迁移到数仓的过程,通常步骤如下:
Step 1:初始化数仓,即将历史全量数据迁移到数据仓库;
Step 2:定时启动迁移动作。
定时启动迁移动作将新生成的数据或者变化的任何数据更新到数据仓库的过程,就需要考虑更新逻辑或者叫更新策略。
注意,每个目标表都有Step 1和Step 2所用到的两种更新策略,如果开发实现是不同的,那么要分开测试。
3.2、方法
验证目标表数据量和数据内容的正确性。
3.2.1、单源增量更新
目标表仅单一源数据,如Tmall数据。
Pre-Condition:源表的关联、转换逻辑是正确的,这里单独测单源更新策略。
Step 1:构造源表测试数据;
Step 2:执行ETL,得到实际落地目标表的数据;
Step 3:构造源表中满足更新策略的数据;
Step 4:执行ETL,验证目标表中落地数据量和内容的正确性。
3.2.2、多源增量更新-全表删除插入方式
目标表有多个源数据,如Tmall、JD、Offline数据。
Pre-Condition:源1、源2单独的关联、转换逻辑是正确的,这里单独测不同源之间的更新策略。
Step 1:构造源1测试数据;
Step 2:执行ETL,得到实际落地目标表的数据;
Step 3:构造源2中满足更新策略的数据;
Step 4:执行ETL,得到实际落地目标表的数据;
Step 5:根据关联、转换逻辑,设计sql得到源2单独落地的目标数据;
Step 6:用Step 5中数据根据更新策略覆盖Step 2中落地目标表的数据,得到Step 4期望的目标表数据;
Step 7:对比Step 4和Step 6,验证实际落地数据量和内容是否正确。
3.2.3、增量更新-时间戳方式
测试结果是否遗漏数据,如果为时间戳方式,要尤其注意时间戳是否带时分秒
增量规则是否正确,对于源表做好足够的数据探查,明白源表中的数据的增量是怎么回事,必要时需要讨论,然后根据业务规则做增量规则方案。
3.2.4、全量更新-全量加载方式
同关联和转换部分的方法,所以本质上全量更新策略不需要单独测试。
3.2.5、测试执行的小TIPS
分来源的表,建议开发每准备启动ETL Job处理下一个来源前备份一次数据;
构造数据时,准备全面的数据,避免来回上传集群重跑数据,占资源,浪费时间。
3.2.6、增量/全量的适用场景
- 全量同步主要是对数据量少的表,比喻电商项目中的商品表、用户表、商户表、地区表、订单状态等实体和维度表,这类数据量少,可以使用全量同步。优点在于简单快捷.
- 增量同步主要是针对数据量庞大的大的场景,比喻订单表,每天产生的订单表数据量庞大,如果使用全量同步,数据占用磁盘的将会非常恐怖,所以一般会采用增量同步的方式。这种方式逻辑较复杂,速度较慢,且要求源表主键能够匹配识别。
4、调度策略测试
4.1、内容
整个ETL处理的过程有多个数据层,假如将每一层的处理到每个目标表的过程定义一个的Job,那么会有很多Job,同一层的Job可能会存在依赖关系,层与层之间肯定会存在依赖关系。因此,快速见效的ETL处理流程将是一个比较复杂的过程,为了对复杂的ETL处理过程更好的调度,更充分得利用系统资源,从而要考虑和设计最优的调度策略。
根据更新策略的更新周期可以确定调度策略的周期,预期是保持一致的。
验证调度策略依赖关系是否合理,最终落地数据的完整性、正确性。
验证job的上下游配置正确。
4.2、方法
测试方法同关联、转换、更新策略。
5、性能测试
5.1、内容
基于某环境参数和数据量,统计单个任务或任务树的执行时间以及资源占用情况,以确认改进的性能和可扩展性,可贯穿整个测试周期。
对于任务树的性能,确保数据在规定和预计的时间内被加载到相应的位置。
性能指标,在指定数据量的情况下,一般从第一个job开始到最后一个job执行结束所花费的时间跨度越短、资源利用率越在70%左右越好。
5.2、方法
统计不同数据量下每个Job执行的时间;
统计不同数据量下全部Job执行的时间;
统计不同数据量下系统资源利用率。
6、测试用例
目前项目的测试用例一般覆盖以下几个部分:
- 关联;
- 转换;
- 调度策略;
- 更新策略。
具体的项目测试用例根据项目的测试范围来决定测试用例覆盖的内容。
关于转换逻辑部分的编写说明:
1、if else的情况:
- 条件逻辑对比总数【测试用例里体现一个,实际可抽样两个写进测试用例】;
- 每个条件逻辑抽样对比所有字段;
2、枚举类型:
- distinct目标表字段,验证结果是否在枚举范围内;
- 枚举类型对比总数【测试用例里体现一个,实际可抽样两个写进测试用例】;
- 部分枚举类型抽样对比所有字段;
- 源数据不再此枚举范围内的,验证全部留空
3、isnull(A,B,C)
同如下逻辑:
if A is not null and A <> ”
取A;
else if B is not null and B <> ”
取B;
else if C is not null and C <> ”
取C;
else
取NULL
注意:数据库字段长度的空字符串值全都重置为null
验证方式:
确定表之间的关联逻辑;
查询A非null和‘’,验证取A
查询A是null且B非null,验证取B;
查询A是‘’且B非null,验证取B;
查询A是null且B是null且C非null,验证取C;
7、可能遇到的挑战
ETL设计复杂,设计人员没有花足够的精力去优化,导致开发和测试人员分析困难;
需要足够的敏感性去分析ETL设计是否合理,如果关联部分分组取TOP1是否稳定合理,转换逻辑中的枚举状态是否覆盖源数据所有值;
ETL关联、转换、更新策略、调度策略的复杂性是非常巨大的。