降本30%,酷家乐海量数据冷热分离设计与实
发布时间: 2023-07-11

作者 | 王小波

编辑 | 李忠良

降本增效一直是研发团队追求的目标之一,面对不断上涨的数据量,研发侧开始思考如何在不降低用户体验的情况下进行成本压减,冷热数据分离的架构思想引起了我们的注意。

背 景

定制家具业务是酷家乐最早的业务之一,定制家具的方案数据也同样沉淀了多年的数据;数据库从早期的 MongoDB 到切换到现在的 HBase;存储逻辑也从原来的全量保存演进到现在的分片增量保存。

随着数据量不断增大,带来的是巨大的成本压力与运维难度,目前定制 HBase 集群仅单副本数据量接近 150TB,再考虑到多副本和灾备集群,相关设备成本不是一个小数。近几年来,随着酷家乐的高速发展,每年用户创建的方案数量都在快速增长,单方案的复杂度也在不断提升,但在产品层面,除了回收站功能,暂时没有增加更多的方案生命周期管理能力。

降本增效一直是研发团队追求的目标之一,面对不断上涨的数据量,研发侧开始思考如何在不降低用户体验的情况下进行成本压减,冷热数据分离的架构思想引起了我们的注意。

通过实施数据的冷热分离,可以大幅降低 HBase 相关的使用成本,使得其数据量仅与热数据期限时间内的用户活跃度有关,不会大规模增长,而冷数据成本则可以随着时间的推移线性增长。

一句话概括就是:成本可控,长期可持续。

业务背景 - 增量分片式方案存储架构简要介绍

方案数据是一个非结构化的数据,里面包含了参数化模型的数据,也包含了一些其他有关设计方案的元数据。最早期阶段,我们的做法是将整个方案 JSON 序列化、压缩后,直接扔到存储中。后来随着单方案复杂度的不断提升,一部分巨型方案的数据量很快触及服务端应用的极限,使得接口传输时间变长、接口容易超时、应用 FullGC 频繁等,这种设计无法继续承载大方案的设计。

我们开始尝试拆分,由于方案数据中,参数化模型所占的比例最大,我们对其采用分片保存的处理,将部分模型组成一个 Packet 一同保存。这种方式由于单个模型的复杂度不一,单 Packet 内含有的模型数量不好估计,容易导致部分 Packet 仍然很大,且无法实现修改一个模型时,只修改一部分数据的目的,需要覆写较多的无效数据,最终灰度一段时间后暂停。

最终我们将分片粒度拆分到最小,实现一个模型保存一条记录,做到了比较极致的增量保存。

整个方案数据由 1 条元数据 + N 条分片数据组成,元数据(MetaData)持有引用分片数据的 ID。方案保存时,仅需保存修改过的模型数据,然后在保存完整的元数据即可。避免了一次性序列化一整个大方案带来的性能问题。

展开全文

调研分析

冷数据的定义

俗话说的好,“If you can’t measure it, you can’t manage it。“

我们要做冷热分离,首先要了解用户的使用情况,再来做针对性的分析和处理。我们先在接口中添加数据埋点,统计用户获取方案距离上次保存的时间间隔,得到一段时间内的统计数据。

该表格清楚的展示了用户操作方案的使用习惯:大部分方案的使用、获取都会在 2 个月内完成,之后可能只有偶尔的打开。

考虑产品上给用户约定的可恢复历史方案的时限是 90 天,为避免恢复历史方案后历史元数据对当前数据冷热状态的混淆,并综合考虑上述的统计数据,我们把冷热方案的分界线定义为 100 天,100 天以上未修改的方案定义为冷方案。

冷热分离技术调研

由于酷家乐的基础设施运行在公有云上,云上解决方案本应是我们优先考虑的。但各家云厂商提供的技术解决方案不尽相同,考虑到兼容性和云中立性,这里不适宜选择云厂商提供的服务,不展开过多,感兴趣的读者可以自行搜索。

可使用 HDFS Archival Storage + HBase CF-level Storage Policy 技术方案。

该方案是以表为最小粒度,支持将不同的表存储到不同的存储介质中。如果我们同时使用了 SSD 和 HDD,则可以将不同性能要求的表存储到不同的介质中。

该方案也不能满足我们的业务要求,理由如下:当前定制方案各个表对读写性能的需求一致,无法接受一些表性能差,一些表性能好,以表粒度区分冷热数据,粒度太粗。

定制方案 HBase 集群因为数据量大,已经在使用全 HDD 集群,无法在存储介质上进一步降本。

3. 自研冷热数据分离方案

使用定时任务将冷方案数据逐步迁移到对象存储,同时在业务层与数据层之间增加分层,用于隔离冷热数据获取的细节。这个方案的优点是:基于对业务数据的理解,自研方案可以更好的做到数据一致性。细节可控,进度可控。方案存取已经作为一个单独的微服务应用,改造对业务方的透明;缺点是需要代码改造,有开发成本。迁移需要避开业务高峰期,无法持续高负载迁移,需要较长时间才能完成。考虑到以上种种条件及限制,我们最终采用自研冷热数据分离的方案。

方案设计

基本原则、目标

架构图

结合公司当前已有的基础设施与中间件,设计的整体架构如下(micro-task 是酷家乐内部开发的一款分布式任务框架):

用户保存方案时,元数据直接保存进 HBase;

分片数据保存时,根据元数据保存的路由信息,决定保存至 HBase 或对象存储;

取数据时,元数据直接从 HBase 中获取,同时提供冷热的路由信息决定如何获取分片数据;

每日低峰期由定时任务触发处理最后修改时间为 100 天前的方案,将其分片数据迁移到对象存储中;

迁移任务完成后,触发失败任务重试,减少人工关注;

数据操作原则

在总体架构的设计下,拆分出每日 3 个定时任务:

下面逐一来看各个任务流程细节:

【冷方案迁移的任务】:

主要分为 5 大流程,分别是:

下面的流程图更加细致的展示了整个过程。其中主状态 0 表示任务初始化未开始,主状态 1 表示任务迁移中,自状态的 1,2,3,4 分别表示了迁移中的各个关键状态。

【失败任务重试的任务】:

考虑到线上可能出现的各种异常状况,对于失败的任务需要重试机制,来减少人工介入。以下重试任务会在当日全部迁移任务完成后触发,用于重试失败的任务。

【检查失败任务并报警的任务】:

失败的任务不能无限次的重试,对于重试一次仍然失败的任务,需要提醒研发人员介入处理,人工判断异常原因,并决定忽略该失败任务,还是手动再次触发处理,亦或是修复 bug。

读写逻辑改造

方案应用代码结构原来大致分为 4 层, 从 Controller 层到 Service 层到 DB 层再到 HBaseClient 层。

为了避免对上层逻辑的侵入,使得冷热方案的细节对上层透明,在 DB 层与 HBaseClinet 层中间抽象出一层 Repository 层,用于表示冷热存储的读写逻辑,实现同一套接口(主要包括读、写、删、Exist 及其对应的批量操作),由 DB 层查询冷热路由,并决定调用不同的 Repository 实现,然后由 Repository 层调用更加底层的 HBaseClient 或对象存储 SDK。

大致结构如下:

设计细节分析

如何设计迁移细节才能保证我们定下的原则和目标呢?

下面针对一些问题做重点分析:

场景 1:搜索满足迁移条件的冷方案时,判定某方案为冷方案并分发了迁移任务,此时用户发生了保存操作,然后执行器接收到了任务准备迁移。

解决:需要使用分布式锁,锁级别为单方案,在保存方案分片数据及元数据时,亦或是迁移时,均需要获取锁,保存锁和迁移锁互斥,保存锁可重入。迁移任务开始时,需要锁定方案,锁定后,再次检查方案最后修改时间是否满足时间条件。

场景 2:迁移任务开始后,用户发生方案保存。

解决:同样需要靠锁定方案,用户保存会等待获取锁或超时失败。迁移成功后,用户可保存,此时方案元数据中的冷热路由已切换到冷,分片数据会直接保存对象存储。

场景 3:迁移过程中,发生用户读取方案的操作。

解决:在元数据中增加冷热方案标识,作为读写分片数据的路由,然后决定从 HBase 读还是从 COS 读。迁移过程中,会先把数据保存到对象存储,然后再修改路由开关,最后删除 HBase 中的热数据。如果切换路由开关后才发生数据读取,则直接根据路由去读对象存储;如果读取到一半发生路由切换,用户实际上还在继续读取 HBase,这里需要再删除前等待一小段时间(如 500 毫秒)保证用户读取完剩余数据。

暂不考虑,有如下问题:当前判断冷方案的依据是方案最后修改时间。如果发生读取就将冷数据上浮为热数据,那么该方案再次被认定为冷方案的依据将缺失,该方案永远无法被再次迁移,除非额外使用其他标识作为冷方案判断的依

微信