表的设计
在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)
参考: