HBase row key设计得不好、频度各异的查询类型,会导致热门数据集中坐落在某几个Region上,造成Region热点,集群负载不均衡。
能采取哪些解决方案,首先要明确访问模式,然后针对性优化:
- 牺牲有序性,散列化row key。
如果不需要数据的有序性:
在row key首部增加原始row key的hash code,使数据均匀散列。
或者,将原始row key的MD5作为实际的row key。
对整个row key散列牺牲了有序性和根据前缀匹配进行范围扫描的能力。
为此,我们也可以对row key的各个部分,分别求取MD5,再拼接起来,作为新的row key。这样虽然仍不支持有序查询,但是支持根据前缀匹配进行范围扫描——根据row key前缀的MD5,范围扫描匹配的行,返回的是无序的数据。
- 不同访问模式的数据,不要混杂在一张表里。
一个店铺有哪些商品(row key是store id + product id) 和 一种商品有哪些店铺在出售(row key是product id + store id),这两种行,不要放在一张表里。
因为业务应用的以上两种查询,其执行频率会有很大差别。如果这两种行,在一张表里,其中一种更频繁的查询,自然会导致整张表中的一类row key成为热点数据。
所以,要拆成两张表,让HBase有自由度独立管理两张表的region,独立进行region的拆分,保持负载均衡。
- 二级索引的热点
我们根据查询条件有哪些字段,构建出二级索引,二级索引的值就是数据表的row key。那么对于经常执行的查询条件,会集中访问二级索引的一部分行,就造成了二级索引的热点区域。
举个例子,以时间戳作为二级索引的key,支持时间范围查找,那么写入最新的数据、查询最新的数据,很容易导致最后一个region成为热点。
为此,我们要影响二级索引的分片策略。我学习到了两种方案:
- 方案一:salting
在以时间戳作为二级索引的例子中,计算:
···
salt = timestamp.hashCode() % region server个数
···
将以上salt作为时间戳二级索引的前缀。这样打乱了原先的二级索引分片策略,使得负载均衡。
salt的取值范围是[0, region server个数)。 因此,查询时,我们的应用逻辑,需要对每一个salt的取值,发起一次查询请求,以salt值作为scan的row key的前缀。然后,将这些查询的结果合并返回。
salt技术有一个问题——当region server数量变化时,row key前缀中的salt没有相应更新。
- 方案二 by 360
360公司(赵建博)提出的二级索引方案:http://blog.csdn.net/dhtx_wzgl/article/details/49069081
其核心思想是,索引和数据保证在同一张表的同一个region里。这是通过将region的start row key作为索引row key的首部前缀实现的。索引和数据,在同一行的不同column family中。当region分裂以平衡负载时,索引和数据共同分裂。二级索引的访问负载会和被索引的数据一样均衡。因为数据和它的二级索引总是在同一个region里的。
这种方案能处理region分裂、region server个数发生变化的情形。