回顾·HBase 在贝壳找房的实践经验



转载请注明 AIQ - 最专业的机器学习大数据社区  http://www.6aiq.com

AIQ 机器学习大数据 知乎专栏 点击关注

本文转发于 DataFunTalk 公众号

本文根据贝壳找房邓钫元老师在中国 HBase 技术社区第二届 MeetUp:“HBase 技术解析及应用实践”中分享的《HBase 在贝壳找房的实践经验》编辑整理而成,在未改变原意的基础上稍做整理。

钫元老师

首先给大家介绍一下贝壳,贝壳是链家一个房地产品牌,链家的愿景是把贝壳打造为一个大的互联网房地产入口品牌,也允许第三方品牌接入我们的贝壳平台。类似于京东商城与京东自营的关系。我们的愿景是把链家的数据与行业的品牌结合起来,更好的服务整个行业的消费者。

本次分享从以下四个部分介绍:

第一部分 HBase 简介

(1)数据模型

它的几个程式是一张表,有一个行键,叫做 rowkey,它用来确定每一行,然后每一行里面会分为很多个列族,每一个列族下面有不限量的列。每一个列上可以存储数据,每一个数据都是有版本的,可以通过时间戳来区别。所以我们在一张表中,知道行键,列族,列,版本时间戳可以确定一个唯一的值。

(2)逻辑架构

任何一张表,他的 rowkey 是全局有序的,由于对物理存储上的考虑,我们把它放在多个机器上,我们按照大小或者其他策略,分为多个 region。每一个 region 负责表示一份数据,region 会在物理机器上,保证是一个均衡的状态。

(3)系统架构

首先它也是一套标准的存储架构。他的 Hmaster 主要负责简单的协调服务,比如 region 的转移,均衡,以及错误的恢复,实际上他并不参与查询,真正的查询是发生在 region server。region server 事负责存储的,刚才我们说过,每一个表会分为几个 region。然后存储在 region server。这里最重要的部分是 hlog。为了保证数据一致性,首先会写一份日志文件,这是数据库系统里面以来的一种特性,创建了日志以后,我们才能写入成功。我们刚才提到 HBase 里面有很多 column-family 列族,没个列族在一个 region 里对应一个 store,store 分别包含 storefile 和 menstore。为了后续对 HBase 可以优化,我们首先考虑把文件写入 menstore 里面,随着 menstore 里面的数据满了之后,会把数据分发到磁盘里,然后 storefile 和 memstore 整体的话,依赖一个数据模型,叫做 lmstree。然后,数据是采用 append 方式写入的,无论是插入,修改,删除。实际上都是不断的 append 的。比如说你的更新,删除的操作方式,都是以打标记方式写入,所以它避免了磁盘的随机 io,提高了写入性能,当然的话,它的底层的话是建立在 hdfs 之上。

第二部分 HBase 在贝壳中的使用经验

(1)总体介绍

我们贝壳使用 HBase 是非常多的。这边也搭建了一套系统,部署在我们机器上,目前我们应用在房客源轨迹跟踪,我们制订了一个指标平台,这个平台依赖于另外一个引擎—kylin。借助 HBase 实现一套多维分析,比如用户的画像,还有日志统计分析等这几个方面。

(2)房客源轨迹产品介绍

这就是我们房客源轨迹的一个产品,其实这个产品主要使用者是经纪人,这个产品可以根据客户的一套房子去跟踪它的所有信息,包括它挂牌成交后的电话以及房源状态的跟新。实际上这个状态量是非常大。我们使用 HBase 来存储这些数据。

其实这个模型是非常适合 HBase 存储的模型,比如说,对于房源轨迹的存储我们做了如下设计,因为它就依赖于房价来做索引,我们把它的 row-key 设计成房源 Id+ 时间 + 操作类型。value 的话,就是具体操作的值。为了保证 HBase 序列的均衡性,我们对 Id 加一个 md5, 做一个哈希,这样可以保证数据的分配均衡。这是一个非常典型的使用场景。

(3)指标平台

第二个场景叫做指标平台,首先给大家展示一下平台。这是一个可视化的分析平台,在这上面可以选择我们在 HBase 存储好的数据,可以选择哪些维度,去查询哪些指标。比如这个成交数据,可以选择时间,城市。就会形成一张图,进而创建一张报表。然后这张报表可以分享给其他人使用。

这张表,应该从时间和组织架构这两个维度,我们现在看到的是一个大区的情况,这是每个大区的成交量。

多维分析的话,包括上卷和下钻。刚才我么刚到大区的情况,我想往上一层,看全国的区域,比如说东北区域或者华南区域的情况。

第二个是下钻,这就是看某个区域的情况,比如说我们想看某个区域作业的经纪人或者作业的店,可以往下查询,分析到每一个人。

(4)引擎选型

指标平台的话,前面是从一个产品方面展示。他后台依赖的一个引擎是 Kylin。为什么会选择 Kylin 呢,因为 Kylin 是一个 molap 引擎,他是一个运算模型,他满足我们的需求,对页面的相应的话,需要亚秒级的响应。第二,他对并发有一定的要求,原始的数据达到了百亿的规模。另外需要具有一定的灵活性,最好有 sql 接口,以离线为主。综合考虑,我们使用的是 Kylin。

(5)Kylin 简介

Kylin 给他家简单介绍一下,Apache Kylin™是一个开源的分布式分析引擎,提供 Hadoop 之上的 SQL 查询接口及多维分析(OLAP)能力以支持超大规模数据,最初由 eBay Inc. 开发并贡献至开源社区。它能在亚秒内查询巨大的 Hive 表。他的原理比较简单,基于一个并运算模型,我预先知道,我要从那几个维度去查询某个指标。在预定好的指标和维度的情况下去把所有的情况遍历一遍。利用 molap 把所有的结果都算出来,在存储到 HBase 中。然后根据 sql 查询的维度和指标直接到 HBase 中扫描就行了。为什么能够实现亚秒级的查询,这就依赖于 HBase 的计算。

(6)kylin 架构

和刚才讲的逻辑是一致的,左边是数据仓库。所有的数据都在数据仓库中存储。中间是计算引擎,把每天的调度做好,转化为 HBase 的 KY 结构存储在 HBase 中,对外提供 sql 接口,提供路由功能,解析 sql 语句,转化为具体的 HBase 命令。

Kylin 中有一个概念叫 Cube 和 Cubold,其实这个逻辑也非常简单,比如已经知道查询的维度有 A,b,c,d 四个。那 abcd 查询的时候,可以取也可以不取。一共有 16 种组合,整体叫做,cube。其中每一种组合叫做 Cuboid。

(7)Kylin 如何在 Hbase 中进行物理存储的

首先定义一张原始表,有两个维度,year 和 city。在定义一个指标,比如总价。下面是所有的组合,刚才说到 Kylin 里面有很多 cuboid 组合,比如说前面三行有一个 cuboid:00000011 组合,他们在 HBase 中的 RowKey 就是 cuboid 加上各维度的取值。这里面会做一点小技巧,对维度的值做一些编码,如果把程式的原始值放到 Rowkey 里,会比较长。Rowkey 也会存在一个 sell 里任何一个版本的值都会存在里面。如果 rowkey 变的非常长,对 HBase 的压力会非常大,所有通过一个字典编码去减少长度,通过这种方法,就可以把 kylin 中的计算数据存储到 HBase 中。

(7)平台架构

这就是我们链家多维分析的平台架构,左边是一套主集群,数据仓库和离线计算在上面,右边是 Kylin 的系统,分为查询和构件两部分,通过一个调度系统,定时去调度计算任务,把数据导入独立的 HBase 中,这套独立的 HBase 是为了保证线上查询不会受影响。最右边,我们做了一套 Kylin 中间件,因为 Kylin 本身会有很多问题,比如任务数有限制,任务之间没有优先级,错误重试等问题,通过自研了一套中间件来解决这些问题。最右边是使用这些系统和集群的平台。它底层就依赖于 kylin 的数据查询服务,同时也会做一些数据缓存。使用系统中的通用模块,比如权限管理,监控管理,元数据管理,调度系统。

(8)Kylin 自研中间件

这就是我们自研的 Kylin 中间件,原生的话,本身有很多问题,我们做的工作支持如下功能:无限容量队列,针对特定 cube 的优先调度,权限的管理, 重试任务执行,实现任务的并发控制。

这是中间件的另一个功能:cube 管理,Kylin 原生也有一套 cube 管理,但是会有很多问题,比如说性能问题,cube 会很卡,另外一个是权限问题,我们希望可以对接到链家的权限,优化前端页面,简化配置,提升管理效率。右边是我们自主创建 cube 的过程。

这里是我们 Kylin 使用的情况。

这里是我们链家对 Kylin 的定制改造。

第三部分 HBase 的优化经验

(1)HBase 部署上的链路

我们优化也是针对这个链路做优化,第一步:Kylin 及各类应用,由 HBase 调用,HBase 数据又存储在 HDFS 上。同时 HBase 和 HDFS 又运行在操作系统上。我们会从这几层做优化。

(2)性能优化工具

首先给大家介绍一个性能优化的工具,是 Profile 利器 - 火焰图。首先看右边的图,表示的是代码在我们 CPU 上运行的逻辑。AB 就是两个函数,A 会调用 B,然后时序会从左到右。上面的每个箭头,是每个堆栈的切片,on-CPU 表示你的代码正在运行,off-cpu 表示你的代码调用其他读写功能,让出线程。把这些切片信息累加起来,就可以得到在右下角的一张图,叫做火焰图。

这是一张火焰图的样例,这张图是对 Firefox 进行采样的,一般分析火焰图,是从下往上的。我们可以看到非常多的“山峰”。一般我们关注的是,“山峰”从下往上一直没有缩减。

(3)优化实例

这里讲一个优化实例,之前某一个查询服务,当时发现,并发的时候 CPU 满载,用火焰图做一个采样分析,左上角是一个认证模块加密算法,他消耗百分之七八十的 CPU 资源,下面的弹窗显示的是具体处理过程,也是占用百分之八九十的 CPU 资源,他为了实现长加密,使用了一套 Base 的加密算法,这套算法对 CPU 的消费非常高,实际上在内网环境上,对安全的要求没有那么高,所以换成了 MD5 加密算法,大家可以看到,优化之后,处理的时间大大降低了。通过对比,提升了四五倍的 qps。右边是火焰图的创造者。

(4)GC 算法

HBase 是内存消耗的大户,它分为两部分,一个是写入的时候,里那个一个是 cache。他们对内存的消耗是非常大,我们这边对 Java 的堆开到了将近 100g 左右,如果使用 CMS 算法的话,对其进行调优。都会出现暂停时间比较长的情况,满足线上的重调业务,后期的话,我们改成了 G1,G1 就是把内存的堆分成 region,然后每一个 region 有各自的功能,G1 有预测算法,比如,我指定一个暂停时间 100ms,他能够通过预测算法,能够预算到在这 100ms 内能够搜集多少垃圾,然后他会收集指定的 region,这样就可以做到时间可控。

这是我们线上使用 java 的执行参数。第一个框我们指定暂停时间 100ms,有几个重要的参数,比如有几个并行数,另外 alwayspretouch 这个参数比较有意思,当我们启动 java 程序的时候,他向操作系统申请 96M 的空间,实际上这个空间是虚拟内存,当你有反馈的时候,操作系统才会把虚拟内存映射到物理内存上,这样一个操作叫做区域中断,这一个负载比较高的系统中,会对你的处理有一些延迟,你开启这个参数,就会把所有的清零。因为每次写入新数据,会产生一个回升带,回升带设置比较大的话,造成暂停的时间比较长,所以建议设置一个比较小的数,百分之 1 就可以。右边是一个优化的图,上边是使用 GMS 的时间,下面是 G1 算法的。可以看到得到了很好的缩短。

(5)HBase 在 IO 上优化

我们也了解到 HBase 在 IO 上可以做很多优化,但是对 IO 最好的优化,还是基于硬件。左边的话,最上方主要依赖 Hbase 对多存储的支持,叫做异构存储,可以标记存储页,同时,HBase 上也会有有一些策略,比如 ALL_SSD,(F 副本全部放在 SSD 上)ONE_SSD(一个副本放在 SSD 上)。目前,我们把 HBase 核心业务和预写日志用 ALL_SSD 这种策略上,一些重要业务会使用 ONE_SSD,普通的话,使用村纯硬盘就可以了。

下面说一下,在 ONE_SSD 测策略下,会有一些性能会出现问题,比如第一个文件块,会有三个副本,其中某一块 SSD,当我发起读取的请求时,根据原生 hadoop 的策略,会优先去读本地,但实际上,在目前的架构下,远程去读 SSD 的性能是远超读取本地硬盘的。所以在架构上增加了 SSD-FIRST 策略,优先读取远程 SSD。

另外就是 HBase 的读写分离,原生 HBase 可以生成队列树和线程树,想很多读写请求和 scan 请求都会统一并发,这样的话会出现一个问题,当我有一个 scan 请求,会扫面很多行,把队列都堵住了,后续的请求可能会无法响应,上边是 1.2 里的功能,我可以把队列和线程分组,比如 80% 负责读,15% 负责写,5% 负责 scan。

这是 HBase 基于 wasd 的性能测试,左边是测试的设备和配置。可以看硬盘与 SSD 的 IO 差别很大,绿色的这条线,代表的是 qps 这条线。大家可以看到,最开始全部使用硬盘的时候,qps 大概在一万左右,当我们使用 ONE_SSD 的时候,大概能够提升一倍的 qps。最后引入 SSDFIRST 策略优化之后,提升大概一点五倍,开启读写分离以后,性能有提升了大概一点五倍。整体提升了 10 倍的 qps,同时延迟也是大幅度下降。

下面是优化的一些杂项。

下面说下,在操作系统层面上做的优化。下面的图运行之后,可以看到机器内存使用状况。Cached 主要做文件系统缓存使用的。下面是解决问题的一个流程。

我们是基于一个 systemptap 分析的,它是一个内核注入工具。它的原理其实就是操作在内核的函数上,比如说你操作一个函数,在里面可以加入计数的代码等,下面是定义的 cache 命中率的公式。右边是 systemtap 在几个函数操作的数据统计。右边减去的部分,是当 page 发生写入的时候,它也会进入到缓存。剪掉的主要是在写入过程中的造成的 page 情况。左下角是计算结果。如果硬盘是 SSD 的话,效果会更好一点。

另外一个就是 HBase 在多处理器上的问题,这是大多数情况下都会遇到的问题,传统的多处理器引入的架构中,所有内存通过总线连接到 CPU 上。实际上所有内存对 CPU 都是对称的,这种模式叫做对称处理器。随着内存的规模越来越大,总线成为了瓶颈。目前大多使用 NUMA 架构,它的原理是每个 CPU 对内存访问的数量是有限的。所以会把内存分为多组,每个组附加在一个 CPU 上,只有当这一块不够的时候,才会通过总线去访问另外一个 CPU,这种设计的话,在内存应用的话,比较友好。但是对 HBase 和 redis 这种大的架构下,一个内存会远远超过一个 cpu 所能支持本地内存。默认的话,是亲和模式。不积极的使用远程内存,会出现本地内存满了之后,导致内存分配失败。所以可以设置一个 zone_reclaim_mode=0 参数。

最后是操作系统的优化杂项。

——END


更多高质资源 尽在AIQ 机器学习大数据 知乎专栏 点击关注

转载请注明 AIQ - 最专业的机器学习大数据社区  http://www.6aiq.com