全部产品

离线计算中的幂等和DataWorks中的相关事项

更新时间:2018-03-12 14:31:16

幂等这个词在软件研发中经常被提到。比如消息发送时不应该同时给同个用户推送多次相同的消息,针对同一笔交易的付款也不应该在重试过程中扣多次钱。曾见过一个案例,有个对于一个单据的确认模块没有考虑到幂等性,导致对应的单据有两条确认记录。其实幂等这个词是个数学的概念,表示这个操作执行多次的结果和执行一次是完全一样的。严格的定义这里不展开讨论,有兴趣的可以到网上搜一下,会有很多介绍。通俗一些说,幂等表示这个操作可以多次重跑,不用担心重跑后到结果会乱掉。就赋值而言,i=1就是个幂等到操作,无论做多少次赋值,只要有做成功一次,i的值就是1。而i++就不是一个幂等的操作。如果多次执行这个操作,i的值会不断增加1。

从前面的示例可以看出,幂等的优势是可以屏蔽重试带来的问题。在分布式的环境里,一般会通过消息中间件、异步调用等方式实现服务之间的解耦。在此过程中,如出现系统异常状况下的状态不明确的情况,一般会进行重试。如果应用不满足幂等的要求,则会出现错误的结果。

离线计算与幂等

离线计算中的作业量较大,跑一个作业需要较多时间。而且由于其特性,经常是凌晨开始计算,在OLTP业务调用量上来以前需要产出结果。如果发现问题,经常没有太多的时间留给技术人员去详细定位问题的原因,然后清理脏数据后重新进行计算。这时候您需要计算能够进行任意次的重跑,也就是说计算需要满足幂等性。对于一个满足幂等性要求的作业,出现问题的时候,您可以首先先重跑一下作业,以期能尽快恢复业务,后续再根据之前的日志慢慢定位问题。

下面以MaxCompute+DataWorks为例,从不同的角度里讨论离线计算的典型场景——离线数仓,看看都有哪些地方需要做到幂等以及如何做到。

计算

目前的离线计算,出于开发的效率考虑,一般都会考虑使用SQL进行代码开发。SQL中包含DDL和DML两种语句。除了SQL,计算引擎一般还支持MapReduce、Graph等计算模型。

DDL

DDL语法可以通过语句里的if exists/if not exists来确保幂等性。比如创建表可以用create table if not exists xxx,删除表可以通过drop table if exists xxx来保证不报错而且可以重复执行。当然创建表也可以先删除后再创建来实现幂等性。当然,如果是建表这种一次性的操作,可以在上线的时候手工做好,但是日常的分区创建/删除等操作就需要通过写进代码里,通过if exists/if not exists来保证可以重试。

DML

DML对数据有影响的是Insert操作。目前Insert有两种模式:Insert into和Insert overwrite。

其中Insert into是把数据追加到原来的数据里,而Insert overwrite是把以前的数据直接覆盖。所以可以清楚地看到,Insert into不满足幂等性要求,而Insert overwrite满足。如果使用Dataworks的SQL节点跑一个Insert into的作业,会有如下提示:

  1. !!!警告!!!
  2. SQL中使用insert into语句有可能造成不可预料的数据重复,尽管对于insert into语句已经取消SQL级别的重试,但仍然存在进行任务级别重试的可能性,请尽量避免对insert into语句的使用!

一些使用Insert into的用户,要使用这种数据更新方式的原因,除去手工数据订正,发现一般都是针对一些不会变化的数据(比如网站的日志、每天的统计结果等)每天需要追加到表中。其实更好的方法是创建一个分区表,把每天需要Insert into的数据改成Insert overwrite到每天的一个不同分区里。

MapReduce

MapReduce默认使用覆盖写入的模式。如果确实有需要追加写入,可以使用com.aliyun.odps.mapred.conf.JobConfsetOutputOverwrite(boolean isOverwrite)来实现。如果需要改成幂等的,可以使用前面SQL里提到的,把数据写入特定的分区里来实现。

ETL

ETL暂时不考虑数据清洗(一般数据清洗是通过计算来实现的),只讨论数据的同步。在Dataworks中,数据的同步通过数据集成模块来实现。在数仓中,数据同步包括数据导入到数仓和数据从数仓中导出两种场景。

数据导入的场景要实现幂等性比较容易。首先我们对于导入数据,建议把每天新增的数据导入到新的一个分区里,然后只需要设置导入的MaxCompute表的清洗规则为写入前清理已有数据Insert Overwr即可。这样数据在导入的过程中会先清空数据后再导入,从而实现幂等。

数据导出的场景,如果数据是全量导出的,也可以用类似数据导入的方法,配置导入前准备语句,把原来的数据全部删除后重新导入。另外如果数据源支持主键冲突设置时,可以通过主键冲突设置成Replace Into来实现数据的替换。

由上图可见,目前Dataworks本身就支持设置出错重试,如果同步作业满足幂等性要求的,可以大胆开启这个设置,从而降低运维成本提高稳定性。