title: HBase Schema 设计
date: 2023-03-15 20:28:32
categories:
- 数据库
- 列式数据库
- HBase
tags:
- 大数据
- HBase
permalink: /pages/a69528/
在 HBase 中,所有对表的访问都要通过 Row Key,有三种访问方式:
get
命令,查询指定的 Row Key,即精确查找。此外,在 HBase 中,表中的行,是按照 Row Key 的字典序进行排序的。
由此,可见,Row Key 的良好设计对于 HBase CRUD 的性能至关重要。
长度原则
RowKey 是一个二进制码流,可以是任意字符串,最大长度为 64kb,实际应用中一般为 10-100byte,以 byte[]形式保存,一般设计成定长。建议越短越好,不要超过 16 个字节,原因如下:
唯一原则
必须在设计上保证 RowKey 的唯一性。由于在 HBase 中数据存储是 Key-Value 形式,若向 HBase 中同一张表插入相同 RowKey 的数据,则原先存在的数据会被新的数据覆盖。
排序原则
HBase 的 RowKey 是按照 ASCII 有序排序的,因此我们在设计 RowKey 的时候要充分利用这点。
散列原则
设计的 RowKey 应均匀的分布在各个 HBase 节点上。
Region 是在 HBase 集群上分布数据的最小单位。每个 Region 由它所属的表的起始范围来表示(即起始 Row Key 和结束 Row Key)。
如果,Row Key 使用单调递增的整数或时间戳,就会产生一个问题:因为 Hbase 的 Row Key 是就近存储的,这会导致一段时间内大部分读写集中在某一个 Region 或少数 Region 上(根据二八原则,最近产生的数据,往往是读写频率最高的数据),即所谓 热点问题。
第一种咱们要分析的方法是反转,顾名思义它就是把固定长度或者数字格式的 RowKey 进行反转,反转分为一般数据反转和时间戳反转,其中以时间戳反转较常见。
152、185
等),但后半部分变化很多。如果将它反转过来,可以有效地避免热点。不过其缺点就是失去了有序性。timestamp = Long.MAX_VALUE – timestamp
),这样有利于扫描最近的数据。这里的“加盐”与密码学中的“加盐”不是一回事。它是指在 RowKey 的前面增加一些前缀,加盐的前缀种类越多,RowKey 就被打得越散。
需要注意的是分配的随机前缀的种类数量应该和我们想把数据分散到的那些 region 的数量一致。只有这样,加盐之后的 rowkey 才会根据随机生成的前缀分散到各个 region 中,避免了热点现象。
其实哈希和加盐的适用场景类似,但我们前缀不可以是随机的,因为必须要让客户端能够完整地重构 RowKey。所以一般会拿原 RowKey 或其一部分计算 Hash 值,然后再对 Hash 值做运算作为前缀。
HBase 不能很好处理 2 ~ 3 个以上的 Column Family,所以 HBase 表应尽可能减少 Column Family 数。如果可以,请只使用一个列族,只有需要经常执行 Column 范围查询时,才引入多列族。也就是说,尽量避免同时查询多个列族。
Column Family 名尽量简短,最好是一个字符。Column Family 会在列限定符中被频繁使用,缩短长度有利于节省空间并提升效率。
HBase 中的 Row 按 Row Key 的字典顺序排序。
不要将 Row Key 设计为单调递增的,例如:递增的整数或时间戳
问题:因为 Hbase 的 Row Key 是就近存储的,这样会导致一段时间内大部分写入集中在某一个 Region 上,即所谓热点问题。
解决方法一、加盐:这里的不是指密码学的加盐,而是指将随机分配的前缀添加到行键的开头。这么做是为了避免相同前缀的 Row Key 数据被存储在相邻位置,从而导致热点问题。示例如下:
foo0001
foo0002
foo0003
foo0004
改为
a-foo0003
b-foo0001
c-foo0003
c-foo0004
d-foo0002
解决方法二、Hash:Row Key 的前缀使用 Hash
尽量减少行和列的长度
反向时间戳:反向时间戳可以极大地帮助快速找到值的最新版本。
行健不能改变:唯一可以改变的方式是先删除后插入。
Row Key 和 Column Family:Row Key 从属于 Column Family,因此,相同的 Row Key 可以存在每一个 Column Family 中而不会出现冲突。
最大、最小 Row 版本号:表示 HBase 会保留的版本号数的上下限。均可以通过 HColumnDescriptor 对每个列族进行配置
Row 版本号过大,会大大增加 StoreFile 的大小;所以,最大 Row 版本号应按需设置。HBase 会在主要压缩时,删除多余的版本。
Column Family 会设置一个以秒为单位的 TTL,一旦达到 TTL 时,HBase 会自动删除行记录。
仅包含过期行的存储文件在次要压缩时被删除。 将 hbase.store.delete.expired.storefile 设置为 false 会禁用此功能。将最小版本数设置为 0 以外的值也会禁用此功能。
在较新版本的 HBase 上,还支持在 Cell 上设置 TTL,与 Column Family 的 TTL 不同的是,单位是毫秒。
> create 'mytable',{NAME => 'cf1', BLOCKSIZE => '65536'}
复制代码
> create 'mytable',{NAME => 'cf1', BLOCKCACHE => 'FALSE'}
复制代码
> create 'mytable',{NAME => 'cf1', COMPRESSION => 'SNAPPY'}
复制代码
Hbase 表设计是和需求相关的,但是遵守表设计的一些硬性指标对性能的提升还是很有帮助的,这里整理了一些设计时用到的要点。
假设采集以下数据
应该如何设计 Row Key?
(1)Timestamp 在 Row Key 头部
如果 Row Key 设计为 [timestamp][hostname][log-event]
形式,会出现热点问题。
如果针对时间的扫描很重要,可以采用时间戳分桶策略,即
bucket = timestamp % bucketNum
计算出桶号后,将 Row Key 指定为:[bucket][timestamp][hostname][log-event]
如上所述,要为特定时间范围选择数据,需要对每个桶执行扫描。 例如,100 个桶将在键空间中提供广泛的分布,但需要 100 次扫描才能获取单个时间戳的数据,因此需要权衡取舍。
(2)Hostname 在 Row Key 头部
如果主机样本量很大,将 Row Key 设计为 [hostname][log-event][timestamp]
,这样有利于扫描 hostname。
(3)Timestamp 还是反向 Timestamp
如果数据访问以查找最近的数据为主,可以将时间戳存储为反向时间戳(例如: timestamp = Long.MAX_VALUE – timestamp
),这样有利于扫描最近的数据。
(4)Row Key 是可变长度还是固定长度
拼接 Row Key 的关键字长度不一定是固定的,例如 hostname 有可能很长,也有可能很短。如果想要统一长度,可以参考以下做法:
(5)时间分片
[hostname][log-event][timestamp1]
[hostname][log-event][timestamp2]
[hostname][log-event][timestamp3]
上面的例子中,每个详细事件都有单独的行键,可以重写如下,即每个时间段存储一次:
[hostname][log-event][timerange]
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。