目录#
目标#
Region自动split有数据倾斜问题,所以要预分区。
热的Region会有更多的读/写请求流入, 造成RegionServer的不稳定和更多的资源消耗(eg. gc 时间长)。
Rowkey设计的原则#
- 散列性
- 集中性
- 唯一性
Row_key设计 防止热点的三种方法#
Hash (通用)#
1 | 十进制_年月日小时分秒_订单号 |
Reversing#
倒序时间戳
一个数据库处理的通常问题是找到最近版本的值。采用倒序时间戳作为键的一部分可以对此特定情况有很大帮助。该技术包含追加( Long.MAX_VALUE - timestamp ) 到key的后面,如 [key][reverse_timestamp] 。
表内[key]的最近的值可以用[key]进行Scan,找到并获取第一个记录。由于HBase行键是排序的,该键排在任何比它老的行键的前面,所以是第一个。
该技术可以用于代替版本数,其目的是保存所有版本到“永远”(或一段很长时间) 。同时,采用同样的Scan技术,可以很快获取其他版本。
1 | Rowkey: reverse(order_id) + (MAX_LONG – timestamp) |
1 | 2021010101010101 -> 101010101010101202 |
Salt#
对get、put友好,对scan操作并不友好
有热点就要打散,但打散就难以做范围查询。因此,要同时满足这对相互矛盾的需求,必须有一种折中的方案:既能在一定程度上打散,又能保证一定程度的有序。
这个解决方案就是加盐,其实叫分桶(salt buckets)更准确。数据在桶内保序,桶之间随机。
写入时按桶个数取模,数据随机落在某个桶里,保证写请求在桶之间是均衡的。查询时读取所有的桶来保证结果集的有序和完备。
1 | 加盐的过程就是在原来key的基础上增加一个byte作为前缀,计算公式如下: |
HBase SDK与预分区 相关的参数#
- 其中 NUMREGIONS 为 region的个数,一般按每个region 6~8GB左右来计算region数量,集群规模大,region数量可以适当取大一些
- SPLITALGO 为 rowkey分割的算法:Hbase自带了三种pre-split的算法,分别是 HexStringSplit、DecimalStringSplit 和 UniformSplit。
各种Split算法适用场景:
- HexStringSplit: rowkey是十六进制的字符串作为前缀的 (MD5编码为16进制的rowkey)
- DecimalStringSplit: rowkey是10进制数字字符串作为前缀的
- UniformSplit: rowkey前缀完全随机 (hash算法转换为字节数组的rowkey)
分区数建议#
| 表 | 分区数建议,表大小 |
|---|---|
| 小表(<50G) | 5~10个分区 |
| 大表(500G~10T) | 表的大小/10G, 最大120个分区 eg. 1T/10G = 100个分区 |
| 吞吐 | 有sacn需求 |
|---|---|
| 吞吐高, 分区数相应多 吞吐低, 分区数相应低 |
分区要控制在一定范围 |
可配置的分区数#
- Portal里可以配置hbase 表的分区数, 配置的值存储在zk中。
- 在建立表的时候读取预先存储好的分区数, 做到建表时自动分区。
- sdk 里读取预先存储好的分区数,保证负载均匀的写入Region。
已有表(负载不均衡的表)迁移到新的表-暂时不做#
老的表的rowkey清洗一遍存入新的表中。
总结#
| put,get | scan | 通用性 | 结果 | |
|---|---|---|---|---|
| Salt | 友好 | 不友好 多个Region的多路归并算法 |
通用 | SDK(方式2-salt) |
| Reversing | 友好 | 友好 相同rowkey的会存储在一起 |
和业务相关 | SDK(方式3-自定义) |