HBase2.0重新定义小对象实时存取

小对象,特别指1K~10MB范围的数据,比如图片,短视频,文档等。这些数据广泛的存在于人工智能,医疗,教育,生活分享,电子商务等领域。目前对象存储典型技术方案为AWS的S3以及阿里云的OSS,还有一些基于MySQL+对象存储的二次开发方案。这些方案解决了对象存储的可靠性和扩展性问题,但是存在一些缺陷:两个方案都存在访问延时问题,因为访问数据至少要2次查询,一次索引访问+一次数据访问,特别的当用户想查询一组有关联数据的时候,需要N次调用;对象存储很难将对象和一些维度、指标数据混合存储,而这些是查询时过滤数据的条件,导致对象存储的方案在检索能力上不足;MySQL+对象存储方案将维度、指标数据存储在Mysql解决了检索能力,但是引入了数据不一致问题,实现主备双活也非常困难,造成可用性上的缺陷,同时这个方案对用户代码侵入大,对运维也不友好。本文将介绍一种全新的小对象实时存取解决方案:HBase2.0,在MOB技术的加持下,HBase2.0将重新定义小对象实时存取,消除以上两个方案的缺陷,具有低延迟,读写强一致,检索能力强,水平易扩展等关键能力

本文将以一条sql展开小对象实时存取的方案演进,介绍不同架构的优缺点。然后提供人工智能和医疗方面两个采用HBase2.0的案例分析。最后总结小对象实时存取的最佳实践。

查询场景定义

用户表T:包含三个属性S1、S2、S3,属性的大小为50bytes左右,包含一个对象数据Object从100KB~10MB

查询:以S1、S2、S3为条件查询Object

select Object from T where S1 = xxx and S2 > yyy and S3 < zzz

对象存储解决方案

对象存储的模型是KV,设计逻辑表为:S1+S2+S3 => Object,将S1、S2、S3组合成一个key

查询实现

// Bucket是对象存储概念,可以理解为对象的一个集合或者表
// 对象存储支持前缀检索,下面表示按“S1 = xxx and S2 > yyy”查找元数据
List keys = GetBucket(key >= xxx+yyy)

// 用户需要自己写代码实现对S3属性列的条件过滤
filterByS3(keys, zzz)

// 依次读取出Object
for(key : keys) {
   GetObject(key);
   // do something
}

对象存储总结:

优势:

  • 读写强一致
  • 支持水平扩展

劣势:

  • 实时性差,一次请求要查询N次服务器(一次检索对象列表+n次对象读取)
  • 检索能力不足,仅支持key的前缀检索,还需要用户端增加检索逻辑;
  • 当属性列增多,特别是动态增加的情况下,对象存储很难支持(key会变得非常复杂)

MySQL+对象存储解决方案

将属性列存储在MySQL,Object存储在对象存储,通过一个引用列进行关联

HBase2.0重新定义小对象实时存取

查询实现:

List references = select reference from T where S1 = xxx and S2 > yyy and S3 < zzz

// 依次读取出Object
for(Key : references) {
   oss.GetObject(key);
   // do something
}

MySQL+对象存储总结:

优势:

  • 检索能力强
  • 支持索引

劣势:

  • 实时性差,一次请求要查询N次服务器(1次查MySQL+n次查对象存储)
  • 读写存在不一致问题,需要额外的开发保证写MySQL和写对象存储的事务一致性
  • 不支持动态属性列的增加
  • 运维复杂

HBase解决方案

S1+S2组合作为Rowkey存储,S3属性和Object作为普通列

Rowkey 普通列 普通列
S1+S2 S3 Object

查询实现

Scan scan = new Scan();
scan.setStartKey(S1+S2); // 按S1+S2前缀查询
scan.setFilter(S3); // 利用Filter过滤S3
scan.addColumn(Object); // 返回Object列
List objects = T.scan(scan); // 一次查询即返回所有结果

HBase方案总结:

优势

  • 实时查询,延迟低
  • 读写强一致
  • 检索能力强:支持过滤器Filter;支持索引(通过Phoenix提供索引能力)
  • 业务代码简洁
  • 支持动态列,可以随意增加属性列
  • 支持水平扩展
  • 易于维护

HBase2.0 小对象存储经典案例

下面通过两个案例介绍HBase2.0给业务带来的改变与价值

案例1:某人工智能公司,利用HBase实现查询延迟从10秒提升到20ms,性能提升500倍,满足了业务对实时查询的强需求。

场景:存储人脸图片数据,每个脸有一个ID,人脸按一定逻辑进行分组;请求场景为读取每个组的全量数据
1、其中 45% 左右的组含有1张人脸
2、45%左右的组含有 2 ~ 9张人脸数据
3、其余的组人脸数范围为 10 ~ 10019
4、其中每个脸2.4k

优化前架构为MySQL+OSS,当一个组有1000张人脸时,查询大概在10s

HBase2.0重新定义小对象实时存取

使用HBase作为存储,将每个组的数据存储在同一行,设计如下:

Rowkey 普通列 普通列 普通列 普通列
组ID 人脸ID1 人脸ID2 ... 人脸IDn

查询时通过一次请求即可获取所有人脸:Table.get(组ID),1000张脸耗时20ms

HBase2.0重新定义小对象实时存取

案例2:某医疗公司,HBase解决10亿规模,200TB检验报告数据的实时存取需求

场景:查询某个用户最近的一次检查报告;或者查询某个用户的多个历史检查报告
1、用户规模10亿+
2、每一个用户会有一个或多个检查报告,每一个报告会有一个时间戳
3、其中每一个报告在200KB左右
4、要求存储可以水平扩展

HBase表设计

rowkey 普通列
用户ID +(Long.max-时间戳) 检查报告

查询

  • 查询某个用户最近的一次检查报告:Scan(用户ID).Limit(1)
  • 查询某个用户的多个历史检查报告:Scan(用户ID).Limit(N)

HBase小对象存储最佳实践

  • 20KB一下的小对象直接存储,50KB以上的小对象需要开启MOB特性,20KB~50KB之间的情况按需开启MOB并调整MOB的阈值大小(操作方式在下面)
  • 对于逻辑上关联的对象,特别是查询时经常一起返回的,可以考虑存储在同行不同列;或者保持相同的rowkey前缀,这样可以通过一个Scan来查询数据
  • 对于属性列非常多,查询条件复杂的场景,可以利用Phoenix构建索引
  • 把属性列和对象列放在不同的column family下

MOB特性介绍

MOB在HBase2.0版本引入,解决了HBase在存储小对象上的写放大问题,关于MOB技术原理已经有文章介绍:MOB技术原理

MOB是服务端功能,如果用户使用云HBase则不需要关心细节,只需要提出需求即可;如果是自己运维,那么开启MOB功能仅需要一条命令:alter 't1', {NAME => 'f1', IS_MOB => true, MOB_THRESHOLD => 
102400} 其中IS_MOB表示开启功能,MOB_THRESHOLD表示多大的数据被认为是MOB对象,单位是byte

总结

HBase2.0重新定义了小对象实时存取的业务访问方式,不再是索引+对象的多次查询,提供简洁的一体化解决方案。具有低延迟,读写强一致,检索能力强,水平易扩展等关键能力;并且具备动态列,多版本等灵活的功能;最后一体化的解决方案简化了用户端的代码,也减少了服务端的运维成本。

未来展望:数据的快速响应能力是支持实时业务的核心,但是同时成本也是业务保持竞争优势的关键。对象存储通常具有较大规模,几百TB是常见。数据天然具有冷热之分,利用这一点,HBase可以内部对数据进行划分,将访问频率低的数据转移到OSS以获得更低的存储成本。同时,在对象数据的大小分布不均的场景中,HBase可以将超大数据存储在OSS,降低长尾数据的影响。以上的工作都会对业务透明。

最后欢迎大家体验HBase2.0,点击进入阿里云HBase2.0官网

上一篇:支付宝分层与端到端回归平台建设实践


下一篇:Docker在蚂蚁金融云平台中的探索与实践