运维开发网

Hbase rowKey 最佳实践 和 mysql id 对比

运维开发网 https://www.qedev.com 2020-04-13 10:47 出处:网络
http://geek.csdn.net/news/detail/202994 先说下为什么 rowKey 那么重要. mysql:    innodb , 最好要有自增 id ,这样确保存储的时候叠加,而不是节点分裂.              分表最好是 hash, 而且 id 还是自增.              不采用 按id (日期) 进行归档的方式. 自增归档比较麻烦. hbase:

http://geek.csdn.net/news/detail/202994

先说下为什么 rowKey 那么重要.

mysql:    innodb , 最好要有自增 id ,这样确保存储的时候叠加,而不是节点分裂.

             分表最好是 hash, 而且 id 还是自增.

             不采用 按id (日期) 进行归档的方式. 自增归档比较麻烦.

hbase: 

             1.由于采用 split 的方式, 范围 region split 分裂的方式.

              2. 采用 lsm log struct merge 的方式,内存中乱序合并. 不介意 rowKey 乱序.

           故 hase 正常情况下反而希望 rowKey 非自增,而且是乱序的.

            6个拆分策略 ,都是和范围相关的. 可以预分配.

如 nosql 的前世今生中所说,

附: rowKey 设计实践

HBase在滴滴出行的应用场景和最佳实践

HBase在滴滴主要存放了以下四种数据类型:

  1. 统计结果、报表类数据:主要是运营、运力情况、收入等结果,通常需要配合Phoenix进行SQL查询。数据量较小,对查询的灵活性要求高,延迟要求一般。
  2. 原始事实类数据:如订单、司机乘客的GPS轨迹、日志等,主要用作在线和离线的数据供给。数据量大,对一致性和可用性要求高,延迟敏感,实时写入,单点或批量查询。
  3. 中间结果数据:指模型训练所需要的数据等。数据量大,可用性和一致性要求一般,对批量查询时的吞吐量要求高。
  4. 线上系统的备份数据:用户把原始数据存在了其他关系数据库或文件服务,把HBase作为一个异地容灾的方案。

使用场景介绍

场景一:订单事件

这份数据使用过滴滴产品的用户应该都接触过,就是App上的历史订单。近期订单的查询会落在Redis,超过一定时间范围,或者当Redis不可用时,查询会落在HBase上。业务方的需求如下:

  1. 在线查询订单生命周期的各个状态,包括status、event_type、order_detail等信息。主要的查询来自于客服系统。
  2. 在线历史订单详情查询。上层会有Redis来存储近期的订单,当Redis不可用或者查询范围超出Redis,查询会直接落到HBase。
  3. 离线对订单的状态进行分析。
  4. 写入满足每秒10K的事件,读取满足每秒1K的事件,数据要求在5s内可用。

Hbase rowKey 最佳实践 和 mysql id 对比

图1 订单流数据流程

按照这些要求,我们对Rowkey做出了下面的设计,都是很典型的scan场景。

订单状态表

Rowkey:reverse(order_id) + (MAX_LONG - TS) 

Columns:该订单各种状态

phil 注: reverse的好处是让 Rowkey 不在自增.

订单历史表

Rowkey:reverse(passenger_id | driver_id) + (MAX_LONG - TS)

Columns:用户在时间范围内的订单及其他信息

场景二:司机乘客轨迹

这也是一份滴滴用户关系密切的数据,线上用户、滴滴的各个业务线和分析人员都会使用。举几个使用场景上的例子:用户查看历史订单时,地图上显示所经过的路线;发生司乘纠纷,客服调用订单轨迹复现场景;地图部门用户分析道路拥堵情况。

Hbase rowKey 最佳实践 和 mysql id 对比

图2 司乘轨迹数据流程

用户们提出的需求:

  1. 满足App用户或者后端分析人员的实时或准实时轨迹坐标查询;
  2. 满足离线大规模的轨迹分析;
  3. 满足给出一个指定的地理范围,取出范围内所有用户的轨迹或范围内出现过的用户。

其中,关于第三个需求,地理位置查询,我们知道MongoDB对于这种地理索引有源生的支持,但是在滴滴这种量级的情况下可能会发生存储瓶颈,HBase存储和扩展性上没有压力但是没有内置类似MongoDB地理位置索引的功能,没有就需要我们自己实现。通过调研,了解到关于地理索引有一套比较通用的GeohHash算法 。

GeoHash是将二维的经纬度转换成字符串,每一个字符串代表了某一矩形区域。也就是说,这个矩形区域内所有的点(经纬度坐标)都共享相同的GeoHash字符串,比如说我在悠唐酒店,我的一个朋友在旁边的悠唐购物广场,我们的经纬度点会得到相同的GeoHash串。这样既可以保护隐私(只表示大概区域位置而不是具体的点),又比较容易做缓存。

Hbase rowKey 最佳实践 和 mysql id 对比

图3 GeoHash示意图

但是我们要查询的范围和GeohHash块可能不会完全重合。以圆形为例,查询时会出现如图4所示的一半在GeoHash块内,一半在外面的情况(如A、B、C、D、E、F、G等点)。这种情况就需要对GeoHash块内每个真实的GPS点进行第二次的过滤,通过原始的GPS点和圆心之间的距离,过滤掉不符合查询条件的数据。

Hbase rowKey 最佳实践 和 mysql id 对比

图4 范围查询时,边界GeoHash块示意图

最后依据这个原理,把GeoHash和其他一些需要被索引的维度拼装成Rowkey,真实的GPS点为Value,在这个基础上封装成客户端,并且在客户端内部对查询逻辑和查询策略做出速度上的大幅优化,这样就把HBase变成了一个MongoDB一样支持地理位置索引的数据库。如果查询范围非常大(比如进行省级别的分析),还额外提供了MR的获取数据的入口。

两种查询场景的Rowkey设计如下:

  1. 单个用户按订单或时间段查询: reverse(user_id) + (Integer.MAX_LONG-TS/1000)
  2. 给定范围内的轨迹查询:reverse(geohash) + ts/1000 + user_id

场景三:ETA

ETA是指每次选好起始和目的地后,提示出的预估时间和价格。提示的预估到达时间和价格,最初版本是离线方式运行,后来改版通过HBase实现实时效果,把HBase当成一个KeyValue缓存,带来了减少训练时间、可多城市并行、减少人工干预的好处。

整个ETA的过程如下:

  1. 模型训练通过Spark Job,每30分钟对各个城市训练一次;
  2. 模型训练第一阶段,在5分钟内,按照设定条件从HBase读取所有城市数据;
  3. 模型训练第二阶段在25分钟内完成ETA的计算;
  4. HBase中的数据每隔一段时间会持久化至HDFS中,供新模型测试和新的特征提取。

Rowkey:salting+cited+type0+type1+type2+TS

Column:order, feature

Hbase rowKey 最佳实践 和 mysql id 对比

图5 ETA数据流程
 

图9 DCM,MR Job运行结果统计

附录: row 设计不合理导致线上问题的调优

调优

首先根据目前17台机器,50000+的QPS,并且观察磁盘的I/O利用率和CPU利用率都相当低来判断:当前的请求数量根本没有达到系统的性能瓶颈,不需要新增机器来提高性能。如果不是硬件资源问题,那么性能的瓶颈究竟是什么?

Rowkey设计问题

现象

打开HBase的Web端,发现HBase下面各个RegionServer的请求数量非常不均匀,第一个想到的就是HBase的热点问题,具体到某个具体表上的请求分布如下:

Hbase rowKey 最佳实践 和 mysql id 对比

HBase表请求分布

上面是HBase下某张表的region请求分布情况,从中我们明显可以看到,部分region的请求数量为0,而部分的请求数量可以上百万,这是一个典型的热点问题。

原因

HBase出现热点问题的主要原因无非就是rowkey设计的合理性,像上面这种问题,如果rowkey设计得不好,很容易出现,比如:用时间戳 生成rowkey,由于时间戳在一段时间内都是连续的,导致在不同的时间段,访问都集中在几个RegionServer上,从而造成热点问题。

解决

知道了问题的原因,对症下药即可,联系应用修改rowkey规则,使rowkey数据随机均匀分布,效果如下:

Hbase rowKey 最佳实践 和 mysql id 对比

Rowkey重定义后请求分布

建议

对于HBase来说,rowkey的范围划定了RegionServer,每一段rowkey区间对应一个RegionServer,我们要保证每段时间内的rowkey访问都是均匀的,所以我们在设计的时候,尽量要以hash或者md5等开头来组织rowkey。

Region重分布

现象

HBase的集群是在不断扩展的,分布式系统的最大好处除了性能外,不停服横向扩展也是其中之一,扩展过程中有一个问题:每次扩展的机器的配置是 不一样的,一般,后面新加入的机器性能会比老的机器好,但是后面加入的机器经常被分配很少的region,这样就造成了资源分布不均匀,随之而来的就是性 能上的损失,如下:

Hbase rowKey 最佳实践 和 mysql id 对比

HBase各个RegionServer请求

上图中我们可以看到,每台RegionServer上的请求极为不均匀,多的好几千,少的只有几十

原因

资源分配不均匀,造成部分机器压力较大,部分机器负载较低,并且部分Region过大过热,导致请求相对较集中。

解决

迁移部分老的RegionServer上的region到新加入的机器上,使每个RegionServer的负载均匀。通过split切分部分较大region,均匀分布热点region到各个RegionServer上。

Hbase rowKey 最佳实践 和 mysql id 对比

HBase region请求分布

对比前后两张截图我们可以看到,Region总数量从1336增加到了1426,而增加的这90个region就是通过split切分大的region得到的。而对region重新分布后,整个HBase的性能有了大幅度提高。

建议

Region迁移的时候不能简单开启自动balance,因为balance主要的问题是不会根据表来进行balance,HBase的自动 balance只会根据每个RegionServer上的Region数量来进行balance,所以自动balance可能会造成同张表的region 会被集中迁移到同一个台RegionServer上,这样就达不到分布式的效果。

基本上,新增RegionServer后的region调整,可以手工进行,尽量使表的Region都平均分配到各个RegionServer上,另外一点,新增的RegionServer机器,配置最好与前面的一致,否则资源无法更好利用。

对于过大,过热的region,可以通过切分的方法生成多个小region后均匀分布(注意:region切分会触发major compact操作,会带来较大的I/O请求,请务必在业务低峰期进行)

HBase Rowkey的散列与预分区设计

扫码领视频副本.gif

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号