HBase学习笔记-入门(6)-HBase表结构设计

HBase表结构设计

命名空间:NameSpace

在一个项目中,需要使用HBase来保存多张表,不同的表可能有不同的业务关系,可以按照某种业务域来划分这些表。为了方便管理,不同的业务域以命名空间(NameSpace)来划分。

HBase初始存在两个命名空间,defaluthbase

  • hbase中存放了系统的内建表:meta和namespace
  • default是默认的命名空间,在不指定的情况下,表将创建在该命名空间下

命名空间相关语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 创建命名空间
create_namespace "MY_NAMESPACE"

# 查看命名空间列表
list_namespace

# 查看某张表的命名空间
describe_namespace "MY_NAMESPACE"

# 在创建表的时候指定命名空间
create "MY_NAMESPACE:MY_TABLE", "C1"

# 删除命名空间
drop_namespace "MY_NAMESPACE"
  • 这里需要注意,如果命名空间中还有表的话,是无法删除的。删除之前需要保证该命名空间中已经没有表了,否则会报错

列族设计

在HBase中,列族的数量应该越少越好

  • 过多的列族会影响HBase的性能
  • 列族按照stroe存储,当一个store存储的数据达到阈值之后,会进行flush。而这个flush操作会引起表中其他列族的flush操作,即表中所有的列族同时进行flush操作。这样会带来不必要的I/O开销,列族越多,对性能的影响越大
  • 一般来说,我们只设置一个列族即可

版本设置

版本VERIONS属性,表示在HBase中,一个单元格会存放几份历史版本的数据,该值的设计需要考虑到具体的业务逻辑。HBase默认创建表的版本为1,即不保留历史版本的数据。

数据压缩

在HBase中可以是使用多种压缩编码,包括LZO、SNAPPY、GZIP等。根据压缩算法的不同特点进行选择

  • GZIP:压缩率最高,是CPU密集型,对CPU的消耗算法比其他算法要多,压缩和解压速度也慢
  • LZO:压缩率居中,压缩和解压速度明显快于GZIP
  • Zippy / Snappy:压缩率最低,压缩和解压速度快于LZO

压缩方式的设置是以列族为单位的,即每个列族可以设置一个压缩方式。具体修改属性为COMPRESSION,举例如下:

1
alter "MY_NAMESPACE:MY_TABLE", {NAEM => "C1", COMPRESSION => "GZ"}

行键Row Key设计原则

行键唯一标识了一个逻辑行的数据,在HBase中具有相当的重要性。因此在设计行键的时候也应当遵循一定的设计原则。

官方提供的行键设计原则:

  1. 避免使用递增或者时序数据作为行键

    由于在存储的过程中,数据会按照行键进行顺序存储,如果使用的是递增或者时序数据,会导致负载都在一台或某几台机器上,不利于负载均衡。

  2. 避免行键和列的长度过大

    在HBase中,如果要访问一个单元格,需要有对应的行键、列族名、列标识符,如果这些名称的长度过大,则会占用较大的内存空间。因此行键和列的长度应该尽可能短,其中行键的最大长度支持64KB。

  3. 需要保证行键的唯一性

    在设计行键的时候,必须保证它的唯一性。在HBase中数据的存储是键值对形式,如果向HBase中同一张表插入相同行键的数据,原先的数据会被覆盖。

避免数据热点

HBase原生支持分布式存储,并且它的应用场景也多是分布式的,因此避免数据热点是一个非常重要的方面。数据热点指的是大量的客户端直接访问集群中的一个或者某几个节点(进行读写操作),而其他节点一直处于空闲状态。大量的访问操作可能会导致某个服务器节点超出承受能力,导致整个RegionServer的性能下降,其他的Region也会受影响。

应对数据热点问题,可以从分区和行键设计两个方面着手。

预分区

HBase中存在自动分区的机制,当然我们也可以手动进行预分区。默认情况,一个HBase的表只有一个Region,由一个RegionServer进行管理。

  • 每个Region存在两个重要的属性:start keyend key。这两个属性表示这个Region维护的行键范围,在存储的时候,根据行键的不同存储到不同的Region中
  • 如果只有一个Region,那么start key和end key都为空,表示均可存储。当数据量越来越大的时候,HBase会进行Region分裂
  • 我们也可以预先设置预分区,建议预分区的个数设置为节点的倍数,让Region均匀地分布在各个节点上
1
2
3
4
5
6
# 指定预分区
# 1. 指定start key和end key
create "MY_TABLE", "C1", {SPLITS => ['10', '20', '30', '40']}

# 2. 指定分区数量和分区策略
create "MY_TABLE", "C1", {NUMREGIONS => 5, SPLITALGO => 'HexStringSplit'}

第一种方式通过指定start key和end key进行预分区,给定SPLITS列表表示指定划分界限,如示例中所示,切分4处,形成了5个分区。

第二种方式通过指定分区数量和分区策略来进行预分区,支持的分区策略有以下几种:

  • HexStringSplit: 适用于形式为十六进制的字符串的行键,预定义范围为00000000->FFFFFFFF。对于位数不够的行键,则使用0在填充高位。
  • DecimalStringSplit: 适用于形式为十进制的字符串的行键。
  • UniformSplit: 适用于形式为随机字节数组的行键。

行键设计

可以通过一些常见策略来设计行键,避免热点

  • 反转策略

如果设计的行键在数据分布上不均匀,但是行键的尾部数据呈现出良好的随机性,则可以考虑将行键反转,或者直接将尾部的bytes提前到行键的开头(例如手机号码、时间戳等)

优点:使得Row Key随机分布,利于数据均匀分布在不同Region中,利于Get操作

缺点:不利于Scan操作,牺牲了Row Key的有序性,因为数据在原Row Key上的自然顺序已经被打乱

  • 加盐策略

加盐的原理是在原Row Key的前面添加固定长度的随机数,即给Row Key分配一个随机的前缀

优点:随机数使得Row Key随机分布,利于负载均衡

缺点:由于添加的是随机数,因此基于Row Key进行查询的时候无法得知随机数是什么,最后只能进行全表扫描,效率低

  • 哈希策略

对原Row Key进行哈希,得到哈希值完整或部分替换原Row Key的前缀部分。可以使用的哈希算法包括MD5、sha1、sha256、sha512等算法

优点:利于负载均衡

缺点:不利于Scan操作,因为打乱了原Row Key的自然顺序

补充阅读:

HBase默认只支持对行键的索引,如果要针对其他的列进行查询,那么只能全表扫描,这样的效率是不高的,尤其在表存储数据量很大的情况下表现更加明显。

Apache Phoenix可以帮助解决这样的问题。(Overview | Apache Phoenix

Apache Phoenix可以很好地和其他Hadoop组件整合在一起,包括Spark、Hive、MapReduce等,使得支持低延迟OLTP和业务操作分析。简单来说,它提供标准的SQL以及完备的ACID事务分析;通过利用HBase作为存储,通过SQL进行操作,如创建表、对数据增删改查等。

参考文章

  1. Apache HBase ™ Reference Guide

HBase学习笔记-入门(6)-HBase表结构设计
http://example.com/2022/04/18/HBase学习笔记-入门-6-HBase表结构设计/
作者
EverNorif
发布于
2022年4月18日
许可协议