离线数据导出背景
数仓、数据湖中我们通常会谈到明细事实数据和维度汇总数据,这些数据有着丰富的应用场景,比如根据ID查询明细数据,流计算时根据ID与维度表Join补齐环境信息,根据条件在大宽表里检索数据,或者多条件跨表Join进行圈人。这些场景通常具有高并发、实时响应的需求,是离线系统满足不了的, 将离线数据导入到HBase/Cassandra、Solr/ES、Clickhouse、MySQL等在线系统是开源生态的成熟解决方案。但数据导入一直存在着成本高、一致性差、稳定性不足等问题,并随着数据体量的增长而愈发明显。本文介绍离线数据导入云原生多模数据库Lindorm,首先分析数据导入的问题和现状,然后介绍Lindorm Bulkload批量导入技术的演进和优势。
Lindorm是阿里云NoSQL 数据库团队推出的云原生多模数据库产品,支持多类型、任意规模数据的低成本存储处理和自适应弹性伸缩,服务于互联网、IoT、车联网、广告、社交、监控、游戏、风控等场景。Lindorm Bulkload是由LTS(Lindorm Tunnel Service)提供的低成本、高可靠、高性能的企业级批量导入服务,支持将MaxCompute、Hive等数据源中的数据导入Lindorm/HBase。利用Lindorm我们可以简化上面的架构:
Lindorm 云原生多模数据库整体架构及背后的思考参考 乘云观海的新起点,新征程 —— 新Lindorm 2020
离线数据导入在线系统存在的问题
成本高
离线导入通常是一种周期性的全量覆盖,全量意味着大规模数据,我们在生产环境中已经看到10TB级别日常导入任务。大的导入任务意味着密集的资源消耗,为了持久化要增加写Log,为了可用性要多副本,为了事务要加锁,再加上RPC、压缩等开销。对于线上系统的成本控制来说,不可预知的大流量非常可怕,我们必须预留更多的资源buffer,这往往意味着成本的浪费。除了凌晨或午高峰的定时型任务外,还会有运营活动临时触发的计算结果回流任务,这种任务对时效性要求很高,最多1-2小时内就需要完成大量数据的导入,时效性的要求提高,意味着对线上资源的挑战更大。目前我们已经在用bulkload服务支持上亿数据分钟级别导入,普通的方式你可以通过线上集群加资源来解决问题,但因为临时的导入任务,使得整体集群成本直线上升,利用率直线下降,这显然让人无法接受。
Lindorm采用LSM Tree架构。读取存储到Lindorm里的一条记录需要合并多个SSTable后提交给客户端。基于这样的原理,Lindorm可以实现直接生成并向系统中“插入”新的SSTable,从而实现“新”数据的加载。开源生态中HBase也同样具备该能力,可以利用TableOutputFormat在MapReduce中直接生成SSTable,并通过API直接加载SSTable到HBase。我们把这种导入方式叫做“Bulkload”,Bulkload可以有效降低写入成本,其不需要日志、事务和RPC,并且SSTable生成过程可以与Lindorm分离使用独立的资源,提高资源利用率。
数据一致性差
离线导入通常是一种周期性的全量覆盖,另一项挑战在于如何保证数据的一致性。业务希望要么看到前一天的数据,要么看到今天的数据,如果读到部分更新的数据会造成一定的问题,希望数据更新本身要么成功,要么全部回滚。目前的系统仅能提供最终一致性,有些甚至最终一致也做不到。对于通过API写入的方式,一种方式是先把数据复制到本地,确认成功后再解析并写入系统达到最终一致性。Bulkload本身可以做到原子加载,较长的写入的过程只是在生成文件,用一个过程较短的load操作使得数据同时生效,几乎不会出现中间状态。但Bulkload不支持覆盖,比如某一行昨天有三个列,今天想更新为,但写入的结果是,C列没有被删除,这是由Lindorm/HBase自身的动态列特性造成的,没有Overwrite整行的逻辑。即便支持了整行更新也还会存在漏洞,如果昨天的数据存在行Row1,今天的离线数据中没有Row1,在Bulkload后昨天的Row1依然存在。
业务侧可以通过切换表的方式来实现强一致,每次导入数据前新建表T-new,导入成功后切换读链路到T-new,删除旧表。但一套方案增加了建表、切流、删表等操作,业务运维起来非常麻烦。
影响在线系统稳定性
这里我们不讨论导入系统本身的稳定性,你可能使用Sqoop、Spark、Hive等来完成数据导出,这些系统自身的稳定性不在讨论范围内,我们探讨数据导入对在线系统的稳定性影响。
通过API大量的数据导入会直接争抢系统资源,造成查询性能下降。回想我们开篇提到的明细事实数据和维度汇总数据,他们通常应用在推荐、风控、广告等在线场景,查询的波动或超时就意味着资损。我们用Bulkload替换API导入,因为SSTable是用外部资源生成的,因此不会出现CPU、IO等资源的争抢,稳定性直接提升一个数量级。但Bulkload加载引发缓存命中率下降和缓存置换会造成一定程度的抖动,新SSTable加入后,新的查询读这个文件产生冷读,同时导致缓存更新。Bulkload是全量更新,此时系统中存在两份数据,但LSM-Tree结构需要读取全部文件进行Merge才能得到最终数据,读的代价增加了。新的文件加入会触发Compaction,Compaction本身又会消耗CPU和IO资源,又会导致缓存的更新。综上所述,距离零在线影响还有很大的差距。
数据倾斜
数据倾斜是分布式系统常见问题之一,数据倾斜的痛在于很难去处理,重新负载均衡是一个耗时长、资源消耗大的过程。如果业务并发比较高就更惨了,因为大分区造成读写热点使问题持续恶化,你可能有必要进行有损恢复来挽回局面。在离线数据同步在线系统的场景里,通常是初始化时存在数据倾斜问题,但长期运行的作业也可能因为变化而出现问题。考虑初始化时第一次同步数据,在线系统的表需要设计合理的分区模式和分区数量,但“合理”不容易做到,现实中经常遇到的是客户采用默认选择,所以这个问题还是要由系统自己来彻底解决。采用Hash分片的方式一般比较均匀,但分片的数量不好定夺,扩容会带来抖动,另外Hash会影响范围扫描性能,不是万金油。而如果采用Range分片,比如像HBase,一旦分区规则和数据分布不匹配就会造成数据倾斜。
Lindorm Bulkload的优势
Lindorm Bulkload是由LTS(Lindorm Tunnel Service)提供的低成本、高可靠、高性能的企业级批量导入服务,支持将MaxCompute、Hive等数据源中的数据导入Lindorm/HBase。
Lindorm Bulkload的优势
- 低成本:Bulklaod模式天然比API模式节省资源,无需日志、事务、RPC等方面的开销。同时利用外部生成SSTable的特殊性,我们对SSTable Writer进行了优化,使其性能提升2倍以上
- 一致性提升:我们把表切换的逻辑做到系统内部,对客户透明,支持强一致覆盖写(即将上线)。对于同城多活的实例,我们支持多个Zone同时Load数据。
- 防导入抖动:我们提供了多级限速、本地化率、缓存更新优化等多种手段减少导入时的性能波动
- 反数据倾斜:可以自动检测数据分布,实时调节目标表的分区,并做到分布式导入下的负载均衡
- 易用性:白屏化接入
- 可靠性:系统高可用,有完善的监控报警体系
Lindorm Bulkload的流程图
同城多活导入
在同城多活场景下,数据需要导出到每一个Zone实现本地访问。N个Zone对应创建N个导出任务是一种解决方法,但这些任务之间很难协同在同一时间完成,造成数据不一致问题。另外N个任务重复了N次计算浪费资源。Lindorm Bulkload支持在一个任务里并发导出多个集群,会先复制数据,确认所有集群数据复制成功后再一起执行Load操作,可以把不一致的窗口控制在秒级。另外Lindorm Bulkload实现了一个MultiClusterDataOutputFormat,把SSTable Writer编码压缩后的数据流复制到所有集群,从而减少重复的SSTable计算。
强一致覆盖更新(即将上线)
强一致覆盖更新是指新导入的数据完全覆盖旧数据,用户不会读到部分更新的数据。我们通过新表旧表切换的方式来实现强一致,新数据写入新表,切换后新请求访问新表,旧表在无访问后删除。整体逻辑内置到系统对客户透明。对于一些AI算法类的场景,可能希望数据回退到上一个版本,可以在回收站直接恢复。
防导入抖动
客户希望数据导入尽可能减少对在线访问的影响,这个方面我们做了一些针对优化。数据导入保障100%的本地化率,找到SSTable的所属分区,进而找到其当前的计算节点,将一份数据复制到该节点的DN上。数据导入提供多级限速,第一层是网络流量限速,第二层是SSTable加载数量限速,降低对读请求延迟的影响。优化缓存汰换,加载新的文件一定会导致缓存变化,可能造成一个集中的汰换,我们在内核层面使这个汰换更加平滑和高效。
反数据倾斜
在HBase社区Bulkload方案中,源数据要先做分区排序,排序是为了更高效的生成SSTable,SSTable内部的数据是按主键排序的。分区一般采用和HBase表的分区对齐,这样SSTable可以恰好的“插入”的分区内,如果SSTable跨越了两个分区,那么需要进行Split,这是一个耗时耗力的工作。Lindorm Bulkload在很长时间也采用的同样方案,如下图所示,一旦源数据的分布与目标表不一致就会产生数据倾斜,导入任务会出现长尾,目标集群也可能会出现大的分区,我们在上面章节已经说明了大分区的危害。
为解决这个问题Lindorm推出了Anti-DataSkew Bulkload,首先利用Quantiles和Mergeable Summaries算法对源数据进行均匀切分(MaxCompute的RangeCluster表已经支持),排序过程中的分区与目标表的分区解耦,消灭导入长尾。主动识别源数据与目标表分布不一致,自动化调整目标表分区。假如目标表已经有大量数据,那么调整过程的Split、Compaction耗时很长,此时利用Lindorm的级联Split能力快速对分区进行Split。
性能优化
Lindorm Bulkload是LTS服务中的一项功能,LTS是独立于Lindorm/HBase集群之外的一个分布式系统,因此Lindorm Bulkload中最核心的SSTable Writer可专项优化。原生的SSTable Writer是面向KV的,每一个KV的写入都会有很多次的比较、编码。但Bulkload的上游数据是以行为单位的,每一行由多个KV组成,并且有相同的时间戳,我们利用这一特性开发了RowAwareWriter,复用行内kv的可用结果,对于大宽表型的导入任务有成倍的优化效果。同时我们利用CPU的缓存来优化编码压缩中数据的复制过程。
总结
Lindorm Bulkload是由LTS(Lindorm Tunnel Service)提供的低成本、高可靠、高性能的企业级批量导入服务,支持将MaxCompute、Hive等数据源中的数据导入Lindorm/HBase。欢迎新用户使用,也欢迎新老用户提意见、提需求,您的鞭策是我们前进的动力:)