文章目录
HBase的表结构设计
1 名称空间
1.1 说明
-
在一个项目中,需要使用HBase保存多张表,这些表会按照业务域来划分
-
为了方便管理,不同的业务域以名称空间
namespace
来划分,这样管理起来会更加容易 -
类似于Hive中的数据库,不同的数据库下可以放不同类型的表
-
HBase默认的名称空间是
default
,默认情况下,创建表时表都将创建在 default 名称空间下 -
HBase中还有一个命名空间
hbase
,用于存放系统的内建表(namespace、meta)
1.2 语法
创建命名空间
create_namespace 'MOMO_CHAT'
查看命名空间列表
list_namespace
查看命名空间
describe_namespace 'MOMO_CHAT'
命名空间创建表
注意:带有命名空间的表,使用冒号将命名空间和表名连接到一起。
#语法
create '名称空间:表名','列簇'...
#示例:
#在MOMO_CHAT命名空间下创建名为:MSG的表,该表包含一个名为C1的列蔟。
create 'MOMO_CHAT:MSG','C1'
删除命名空间
- 删除命名空间,命名空间中必须没有表,如果命名空间中有表,是无法删除的
drop_namespace 'MOMO_CHAT'
2 列蔟设计
-
HBase列蔟的数量应该越少越好
-
两个及以上的列蔟HBase性能并不是很好
-
一个列蔟所存储的数据达到flush的阈值时,表中所有列蔟将同时进行flush操作
-
这将带来不必要的I/O开销,列蔟越多,对性能影响越大
-
项目中我们一般只设计一个列蔟
3 版本设计
- HBase 通过 HColumnDescriptor 为每个列族配置要存储的最大行数版本。最大版本的默认值为1。这是一个重要的参数,因为如数据模型部分所述,HBase 也没有覆盖行的值,而是按时间(和限定符)存储不同的值。在重要的压缩过程中删除多余的版本。最大版本的数量可能需要根据应用程序需求增加或减少。
- 不建议将最高版本数设置为极高的级别(例如,数百个或更多),除非这些旧值对您非常重要,因为这会大大增加 StoreFile 大小
我们可以通过命令来查看一下版本的信息:
通过以下输出可以看到:
-
版本是相对于列蔟而言
-
默认列蔟的版本数为1
hbase(main):015:0> describe "MOMO_CHAT:MSG"
Table MOMO_CHAT:MSG is ENABLED
MOMO_CHAT:MSG
COLUMN FAMILIES DESCRIPTION
{NAME => 'C1', VERSIONS => '1', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLI
CATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}
1 row(s)
4 数据压缩
4.1 压缩算法
在HBase可以使用多种压缩编码,包括LZO、SNAPPY、GZIP。只在硬盘压缩,内存中或者网络传输中没有压缩。
压缩算法 | 压缩后占比 | 压缩 | 解压缩 |
---|---|---|---|
GZIP | 13.4% | 21 MB/s | 118 MB/s |
LZO | 20.5% | 135 MB/s | 410 MB/s |
Zippy/Snappy | 22.2% | 172 MB/s | 409 MB/s |
-
GZIP的压缩率最高,但是其实CPU密集型的,对CPU的消耗比其他算法要多,压缩和解压速度也慢;
-
LZO的压缩率居中,比GZIP要低一些,但是压缩和解压速度明显要比GZIP快很多,其中解压速度快的更多;
-
Zippy/Snappy的压缩率最低,而压缩和解压速度要稍微比LZO要快一些
4.2 查看表数据压缩方式
通过以下输出可以看出COMPRESSION => 'NONE'
,HBase创建表默认是没有指定压缩算法的
hbase(main):015:0> describe "MOMO_CHAT:MSG"
Table MOMO_CHAT:MSG is ENABLED
MOMO_CHAT:MSG
COLUMN FAMILIES DESCRIPTION
{NAME => 'C1', VERSIONS => '1', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLI
CATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}
1 row(s)
4.3 设置数据压缩
创建新的表,并指定数据压缩算法
create "MOMO_CHAT:MSG", {NAME => "C1", COMPRESSION => "GZ"}
修改已有的表,并指定数据压缩算法
alter "MOMO_CHAT:MSG", {NAME => "C1", COMPRESSION => "GZ"}
5 ROWKEY设计原则
5.1 HBase官方的设计原则
1. 避免使用递增行键/时序数据
如果ROWKEY设计的都是按照顺序递增(例如:时间戳),这样会有很多的数据写入时,负载都在一台机器上。我们尽量应当将写入大压力均衡到各个RegionServer
2. 避免ROWKEY和列的长度过大
-
在HBase中,要访问一个Cell(单元格),需要有ROWKEY、列蔟、列名,如果ROWKEY、列名太大,就会占用较大内存空间。所以ROWKEY和列的长度应该尽量短小
-
ROWKEY的最大长度是64KB,建议越短越好
3. 使用long等类型比String类型更省空间
long类型为8个字节,8个字节可以保存非常大的无符号整数,例如:18446744073709551615。如果是字符串,是按照一个字节一个字符方式保存,需要快3倍的字节数存储。
4. ROWKEY唯一性
-
l设计ROWKEY时,必须保证RowKey的唯一性
-
由于在HBase中数据存储是Key-Value形式,若向HBase中同一张表插入相同RowKey的数据,则原先存在的数据会被新的数据覆盖。
5.2 避免数据热点
-
热点是指大量的客户端(client)直接访问集群的一个或者几个节点(可能是读、也可能是写)
-
大量地访问量可能会使得某个服务器节点超出承受能力,导致整个RegionServer的性能下降,其他的Region也会受影响
1、 预分区
默认情况,一个HBase的表只有一个Region,被托管在一个RegionServer中
每个Region有两个重要的属性:Start Key、End Key,表示这个Region维护的ROWKEY范围
如果只有一个Region,那么Start Key、End Key都是空的,没有边界。所有的数据都会放在这个Region中,但当数据越来越大时,会将Region分裂,取一个Mid Key来分裂成两个Region
预分区个数 = 节点的倍数。默认Region的大小为10G,假设我们预估1年下来的大小为10T,则10000G / 10G = 1000个Region,所以,我们可以预设为1000个Region,这样,1000个Region将均衡地分布在各个节点上
指定 start key、end key来分区
#语法
create ‘表名‘,’列簇‘,SPLITS=>[分区区间]
#示例,第一个shell命令 创建表的时候,定义了5个分区
hbase> create 'ns1:t1', 'f1', SPLITS => ['10', '20', '30', '40']
hbase> create 't1', 'f1', SPLITS => ['10', '20', '30', '40']
hbase> create 't1', 'f1', SPLITS_FILE => 'splits.txt', OWNER => 'johndoe'
指定分区数量、分区策略
分区策略:
HexStringSplit | ROWKEY是十六进制的字符串作为前缀的 |
---|---|
DecimalStringSplit | ROWKEY是10进制数字字符串作为前缀的 |
UniformSplit | ROWKEY前缀完全随机 |
#语法
hbase> create 't1', 'f1', {NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
2、 ROWKEY避免热点设计
- 反转策略
-
如果设计出的ROWKEY在数据分布上不均匀,但ROWKEY尾部的数据却呈现出了良好的随机性,可以考虑将ROWKEY的翻转,或者直接将尾部的bytes提前到ROWKEY的开头。
-
反转策略可以使ROWKEY随机分布,但是牺牲了ROWKEY的有序性
-
缺点:利于Get操作,但不利于Scan操作,因为数据在原ROWKEY上的自然顺序已经被打乱
- 加盐策略
-
Salting(加盐)的原理是在原ROWKEY的前面添加固定长度的随机数,也就是给ROWKEY分配一个随机前缀使它和之间的ROWKEY的开头不同
-
随机数能保障数据在所有Regions间的负载均衡
-
缺点:因为添加的是随机数,基于原ROWKEY查询时无法知道随机数是什么,那样在查询的时候就需要去各个可能的Regions中查找,加盐对比读取是无力的
- 哈希策略
-
基于 ROWKEY的完整或部分数据进行 Hash,而后将Hashing后的值完整替换或部分替换原ROWKEY的前缀部分
-
这里说的 hash 包含 MD5、sha1、sha256 或 sha512 等算法
-
缺点:Hashing 也不利于 Scan,因为打乱了原RowKey的自然顺序