HBase-04行键(Rowkey)设计

表的设计

在HBase中表的概念并不是那么重要, 数据的物理存储是基于列族的.

Row Key设计

设计合理的 Row Key设计可以加速scan操作, 还可以在大量写入数据时让多个RegionServer分担负载.

Region是 HBase中扩展和负载均衡的基本单元, Region本质上是以 Row Key的字典顺序连续存储的, 这种设计优化了 scan的操作,
一个 Table最初只有一个 Region, 当一个 Region过大时, 系统会在中间键(middle key)将这个 Region拆分成两个大致相等的子 Region。
Row Key设计不好就会造成读写热点问题,造成大量客户端直接访问集群某一个或者极少数的节点,造成节点性能下降或者Region不可用

Rowkey 会占用 HBase 存储空间(包括在内存中的 MemStore,和 HFile 文件),所以 Rowkey 应尽量简短。

Rowkey 设计需要考虑的几点:

  • Rowkey 的设计要避免写入热点,即短时间内大量的写入落到一个Region
  • 业务数据查询场景是什么? 只是常规的 K-V get 操作?是否需要 scan?是否需要经常查询最新数据?

几种 Rowkey 设计方案:

  • Rowkey 前缀加盐, 通常做法是对 Region 进行预分区指定 startKey,然后对 Rowkey 某部分进行 Hash + 取余操作,可得到 salt 前缀,通过 slat 前缀将 rowkey 分散到各个 Region;
  • Rowkey Hash, 具体做法是使用 Rowkey 里一部分(例如 uid) 进行 hash, 截取 hash 的一部分, 作为 rowkey 前缀, 缺点是无法 scan
  • Rowkey 反转, 对于前缀几乎相同, 仅后缀变化的 rowkey (时间戳, 手机号), 缺点也是无法 scan
  • 另一种”反转”, 例如时间戳, rowkey 设计为 Long.MAX- timestamp, 并不具备”打散”的作用, 而是让最后的(时间戳最大)的排序最靠前, 适用于经常取“当前最新”的场景:
    例如 [key][reverse_timestamp], 最新值可以通过 scan [key] 获得最新记录,因为 HBase 中 RowKey 是有序的,第一条记录是最后录入的数据

案例:

  • 电商:
    • 查询需求:展示卖家最近一段时间的订单(scan)
    • Rowkey 设计: salt + sellerID + timestamp + orderId
  • 监控:
    • 查询需求:展示最新 or 最近一段时间(scan)
    • Rowkey 设计:参考 「OpenTSDB 的 Rowkey 设计」
  • 推荐feed case:
    • 查询需求:某用户某一天进行的 feed 请求,也需要scan
    • Rowkey 设计: uid + timestamp
  • 用户画像:
    • 查询需求:根据 uid 查询,无需 scan
    • Rowkey 设计:prefix + uid,其中 prefix = substr(md5(uid), 0 ,x)

参考: