【HBase使用】HBase rowkey设计原则

一、背景

  • HBase表设计主要要注意两点,一是rowkey设计,而是列簇设计;

1.一分钟简介

  • HBase是三维有序存储的,通过rowkey(行键),Column Family(列簇/列族),Column Qualifier(列) 这个三个维度可以对HBase中的数据进行快速定位。针对一个前面三个维度可以确定一个Cell,一个Cell可以有多个version,通过 TimeStamp(时间戳)确定,最新的时间戳排在最前最先取到;
  • HBase中rowkey可以唯一标识一行记录,在HBase查询的时候,常见有以下2种方式
    • 通过get方式,指定rowkey获取唯一一条记录
                   
       
    • 通过scan方式,设置startRow和stopRow参数进行范围匹配(需要控制流量)
    •  
  • 针对这两种方式,在第五项中,我们简要介绍了几种常见的rowkey设计思路(充分利用HBase的分布式特性,将请求分散到不同region上)

二、rowkey长度原则——短小精悍

  • 通过rowkey能定位到具体数据,越短越好。不必要的数据不要存在rowkey中

  • 原因如下:
    • 节省磁盘、内存空间
    • 从rowkey了解业务设计更清晰

三、rowkey顺序原则——精确&必选在前、尽量定位小范围

  • 查询必填的精确的字段必须拼到rowkey最前面;(比如要实现类似  where A='xxx' and B='xx' and C like 'Pre%' 的需求 ,那么A,B应该在rowkey前面,C拼在A和B后面)
  • 能过滤越多的字段,放到越靠前;能减少查询扫描量;

四、rowkey唯一原则——小心被覆盖

  • rowkey必须保证业务上唯一,防止更新被覆盖
    • 比如存城市下所有用户信息,rowkey不能只存城市id,得加上用户id,最终格式为:  01_driver001 ;如果要按时间查询,还要加上时间戳或者yyyy-MM-dd  ,格式为: 01_driver001_2019-08-01

五、rowkey散列原则——形散神不散

  • HBase 通过一个表分多个region,不同region放在不同机器上,将数据请求分散到多台机器。如果不散列,就有产生热点(参见第五条)

  • rowkey前缀是散列的即可!不必须把整个rowkey打散,比如可以:[活动id]_beijing_01  ,这里后面的beijing  , 01 可以按正常格式。

  • DHS上可以选择常见的region split方式,以下是常见的散列规则:

    • 十六进制字符串表达——非常通用MD5

      • md5就是一个散列的结果

      • 写入HBase之后通过hbase shell命令查看结果为:47bce5c74f589f4867dbd57e9ca9f808     ——是一串0~9  a~f的字符串,不是/x0a的格式哦

    • 十进制字符串表达——id反转

      • 例如手机号逆序;  递增的订单号逆序,逆序也是为了散列;

      • 写入HBase之后通过hbase shell命令查看结果为:95962300581

      • 注意:有些业务id逆序之后并不能散列;比如一些社交账号id可能有“靓号” ,好多账号最后几位都是6,8,9,66,88,99,可能逆序之后依然散列不了;那么请用前面的MD5方式!

    • 二进制——高级用法,传说中的\x00

      • 散列,然后再序列化之后的rowkey 

      • 写入HBase之后通过hbase shell命令查看结果为:\x0E\xA0\xEA\x0E\xA0\xEA\x0E\xA0

六、为啥要散列,减少热点

          

  • HBase中的行是按照rowkey的字典顺序排序的,这种设计非常适应scan操作,可以将相关的行以及会被一起读取的行存取在临近位置,便于scan。
  • 然而糟糕的rowkey设计会使得过多的数据前缀都是一样的,从而形成热点;
    • HBase建表可以预先划分数据的分布,我们期望这样程序刚启动的时候,数据就能自动分布到不同机器上充分利用资源。
      • 赶羊的故事:好比羊圈管理员先盖好了N个羊圈,往N个羊圈赶羊,如果几个管理员预先协商的“策略”是按业务逻辑的“按羊的颜色区分”,估计最后结果是1个羊圈挤满了白色的羊,另外一个有几只黑羊,剩下8个没有羊(资源不均衡)
      • 羊圈管理员思考之后:选择一个随机的策略,给羊加上编号 Sheep01...100,并且给编号加了一个MD5的前缀 md5(sheep01,0,3)_sheep01_其他查询用到字段,前缀是0000的去第一个羊圈,前缀是0001的去第2个...这样每个羊圈的羊数基本持平~~
  • 大量访问会使热点region所在的单个机器超出自身承受能力,引起性能下降甚至region不可用
  • 这也会影响同一个RegionServer上的其他region,由于主机无法服务其他region的请求。 
  • 设计良好的数据访问模式以使集群被充分,均衡的利用。

 

比如某业务id都是10开头的,如果不做散列的情况:

散列前,因为业务id不规律,不够分散,导致有热点

 

散列后,分散均匀

 

 

 

下面是一些常见的避免热点的方法以及它们的优缺点:

 

进阶的散列,加盐(scan扫描操作经常用到)

这里所说的加盐不是密码学中的加盐,而是增加salt_bucket机制,将一次请求分散到N个不同的region上。

常见场景:

  • 比如要扫描   userId01开头的所有数据,可能有10w行
  • 如果原来rowkey设计是:md5(userId),那么我们将要扫描10w行放在同一个机器的数据了,可能对这个机器压力非常大
  • 修改一下rowkey,前面“加盐”,如下面表格:
00_userId01开头的数据
10_userId01开头的数据
20_userId01开头的数据
...
90_userId01开头的数据

  • 然后在客户端开10个线程,扫秒00开头_userId01的数据,10开头_userId01的数据...90开头_userId01的数据
  • 收集多个线程扫描结果,并进行处理

 

 

七、常见的rowkey设计问题

1.使用md5(业务id1)_业务id1_userId  还是  md5(业务id1)_userId

业务id1 是查询参数

如果已使用MD5 散列了业务id1  那在查询时就已知业务id了,没有必要在后面再写一遍业务id1了 可以参考上面【rowkey长度原则】

 

2.想将最新的数据先查出来

时间戳反转,一个特殊用法,按时间段查询,并且经常查询的数据是最新数据

想要根据用户id查询用户最近从start time到end time的行为,或者用户近一个小时的行为,并且将最新的数据先加载

  • 1.用户id肯定要放到rowkey
  • 2.时间肯定要放到rowkey

初步思路:md5(用户ID)_行为timestamp

进阶,要查“此用户近一个小时行为”,并且将最新的数据先加载

  • 优化前:md5(用户ID)_行为timestamp
  • 优化后:md5(用户ID)_【Long.Max_Value - 行为timestamp】
  • 查询方法:startRow是md5(用户ID)_[Long.Max_Value - 结束时间],stopRow是md5(用户ID)_[Long.Max_Value - 起始时间],这样会先扫描到最新数据

 

 

八、列簇设计——减少IO影响

  • 经常一起查的:可以合并到一个列簇下面避免扫过多的文件;
  • 不经常一起查询出来的,就不要放到一个列簇了。否则扫5个字段的数据,只用3个字段。
  • 例子:比如存用户信息。性别,年龄,地域经常一起被查询。这三个可以放一个列簇下面。
  • 另外的列簇可以放:标签,用户级别等。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值