HBase 预分区 & Phoenix 加盐
HBase 热点问题
刚创建 HBase 表的时候默认只有一个 Region 由一个 Region Server 管理,在数据量达到一定值的时候会触发分裂 split,这样会不断的分裂出更多的 Region,由不同的 Region Server 管理,每个 Region 管理的是一段连续的 row key,由 start row key 和 end row key 表示,这样会出现两个问题
- 无法充分利用分布式并发处理的优势,必须等待 Region 自动分裂成多个,这个过程可能会很久
- 由于每个 Region 管理一段连续的 row key,这样如果数据的读写不够随机,比如有自增 ID,比如大量操作集中在某段 row key,这样有可能导致压力都在同一个 Region 上
Region 分裂策略
定义在 hbase-site.xml 文件
<name>hbase.regionserver.region.split.policy</name>
<value>org.apache.hadoop.hbase.regionserver.IncreasingToUpperBoundRegionSplitPolicy</value>
<description>
A split policy determines when a region should be split. The various other split policies that
are available currently are ConstantSizeRegionSplitPolicy, DisabledRegionSplitPolicy,
DelimitedKeyPrefixRegionSplitPolicy, KeyPrefixRegionSplitPolicy etc.
</description>
默认策略是 IncreasingToUpperBoundRegionSplitPolicy,在 HBase 1.2 中,这个策略默认表示,当 Region 的大小达到 Region 个数的立方乘以 hbase.hregion.memstore.flush.size(默认 128 MB)再乘以 2 ,或是达到 hbase.hregion.max.filesize (默认 10 GB)时,就对该 Region 做分裂操作
第一次分裂的大小:1^3 * 128MB * 2 = 256MB
第二次分裂的大小:2^3 * 128MB * 2 = 2048MB
第三次分裂的大小:3^3 * 128MB * 2 = 6912MB
第四次分裂的大小:4^3 * 128MB * 2 = 16384MB,超过了 10GB,因此只取 10GB
后面的分裂大小都是 10GB
可以看到如果可以利用的节点比较多的话,那么可能得等很久才能充分利用
预分区
第一种预分区的方法
hbase org.apache.hadoop.hbase.util.RegionSplitter tablename HexStringSplit -c 10 -f f1:f2:f3
上面的命令表示创建一张名为 tablename 的表,这张表预先分配了 10 个 Region,有三个 CF,分别是 f1、f2、f3,预分区算法是 HexStringSplit,也可以选择 UniformSplit,其中 HexStringSplit 适合 row key 的前缀是十六进制的字符串的,UniformSplit 适合 row key 前缀完全随机的,预分区后,哪怕连续的 row key, HBase 也会通过算法将其分到不同的 Region,实现均匀分布,避免热点
第二种预分区的方法
hbase shell > create 'tablename', 'f1', SPLITS=> ['10', '20', '30', '40']
当可以提前知道 row key 的分布的时候,可以指定每个预分区的 region 的分割点,上面命令创建的表中,有 5 个 Region
Region 1 : row key 的前两位是 min~10
Region 2 : row key 的前两位是 10~20
Region 3 : row key 的前两位是 20~30
Region 4 : row key 的前两位是 30~40
Region 5 : row key 的前两位是 40~max
注意这里不单指数字字符,比如 1a 就会落在 Region 2
对已存在的表可以做强制分裂
hbase shell > split 'table', 'split point'
此外也可以设计自己的分裂方法
Phoenix 加盐
CREATE TABLE IF NOT EXISTS Product (
id VARCHAR not null,
time VARCHAR not null,
price FLOAT,
sale INTEGER,
inventory INTEGER,
CONSTRAINT pk PRIMARY KEY (id, time)
) COMPRESSION = 'GZ', SALT_BUCKETS = 6
本质上是对 HBase 表的 row key 做了哈希后,对 SALT_BUCKETS 取余数,并将结果(上面的例子中是 0~5)作为 byte 插入到 row key 的第一位,根据这个数值将数据分到不同 Region 中,由于是作为 byte 存储,所以 SALT_BUCKETS 能取的最大值是 256,拥有相同 salt byte 的 row 会被分到相同的 region server,所以通常取 region server 的数量作为 SALT_BUCKETS
由于加了盐的数据最前面多了一位,这样默认情况下,从不同 region server 取出来的数据无法按原来的 row key 排序,如果需要保证排序,需要改一个配置
phoenix.query.force.rowkeyorder = true