HBase的RowKey设计原则含案例(全)

本文介绍了HBase的RowKey设计原则,包括防止hotspotting、确保唯一性、控制长度和排序顺序。通过案例分析了如何利用散列、时间戳和预分区来优化并发度,强调了RowKey设计在数据分布和查询效率中的重要性。

前言

HBase的RowKey的行由行键按字典顺序排序,这样的设计优化了扫描,允许存储相关的行或者那些将被一起读的邻近的行。然而,设计不好的行键是导致 hotspotting 的常见原因。当大量的客户端流量( traffic )被定向在集群上的一个或几个节点时,就会发生 hotspotting。这些流量可能代表着读、写或其他操作。流量超过了承载该地域的单个机器所能负荷的量,这就会导致性能下降并有可能造成地域的不可用。在同一 RegionServer 上的其他地域也可能会受到其不良影响,因为主机无法提供服务所请求的负载。设计使集群能被充分均匀地使用的数据访问模式是至关重要的。

为了防止在写操作时出现hotspotting,设计行键时应该使得数据尽量同时往多个RegionServer 上写,而避免只向一个RegionServer 写,除非那些行真的有必要写在一个RegionServer 里。HBase的排序问题按字典序rowkey排序,从小到大,如果任务为了获得最新的时间数据可用用时间戳反转,或者用long.maxvalue - timestamp

设计原则

按照  散列 唯一  长度 顺序

散列

散列和预分区可以放在一起 比如预先分10个region  减少热点问题

可以将id %10 做开头 但是如果id规律性强的话 可以用(id+年月日)%10

唯一

必须在设计上保证其唯一性,rowkey是按照字典顺序排序存储的,

因此,设计rowkey的时候,要充分利用这个排序的特点,可以将经常读取的数据存储到一块,将最近可能会被访问的数据放到一块。

可以用时间戳放在其中 hbase是按字典序排序 要考虑一直单分区写情况

长度

rowkey作为二进制码流最大为64kb 最好不要超过16个字节 设计过长会占memstore空间

顺序 

HBase的rowkey安装字典序顺序排列 从小到大,所以需要最新数据时需要翻转时间戳或者自增id    

可枚举属性值较少的属性放在rowkey前面

rowkey是由多个字段组合而成,这多个字段的先后次序和访问的效率有直接的关系。一个普遍的规则是:数量较少,可控的字段放在rowkey靠前位置(如eengine_type,provinc等);反之放在后面(如vin,timestamp等)。这样做的原因是可控属性放在前面,对各种不同查询需求的平衡性强一些,反之平衡性较差。

案例1:

YCK09360-60-1638290481900-9011D6L00124    434.7

YCK09360-60-1638290482900-9011D6L00124    76.1

YCK09360-60-1638290483900-9011D6L00124    18.6

YCK09360-60-1638290484900-9011D6L00124    150.1

YCK09360-60-1638290485900-9011D6L00124    96.1

YCK09360-60-1638290586900-9011D6L00124    35.7

ENGINE_TYPE 可枚举,并且数量较少,放在前面;而vin确很多,因此放在后面。这样的设计能够适应如下两种需求,复杂度都比较小:

1) 查询,某段时间内 YCK09360-60的所有数据。这种需求设置scan的startrow=‘YCK09360-60_时间戳’,endrow=‘YCK09360-60_时间戳’,即可。

2) 查询某段时间内的所有9011D6L00124 的数据。这种需求下,根据scan rowkey连续的原则,这种需求设置scan的startrow=‘YCK09360-60_时间戳_9011D6L00124 ’,endrow=‘YCK09360-60_时间戳_9011D6L00124 ’,即可。

但是,如果将vin放在前面,如下所示,适应性就差一些,如下所示案例2:

9011D6L00124-YCK09360-60-1638290481900    434.7

9011D6L00124-YCK09360-60-1638290482900    76.1

9011D6L00124-YCK09360-60-1638290483900    18.6

9011D6L00124-YCK09360-60-1638290484900    150.1

9011D6L00124-YCK09360-60-1638290485900    96.1

9011D6L00124-YCK09360-60-1638290586900    35.7

1)查询某段时间内的所有9011D6L00124 的数据。这种需求下,设置scan的startrow=‘9011D6L00124-YCK09360-60-时间戳,endrow=‘9011D6L00124-YCK09360-60-时间戳’,即可。

2) 查询某段时间内 YCK09360-60的所有数据。这种需求设置scan是要取的YCK09360-60小所有的vin号并放在rowkey的最前段,启动多个scan 去扫描多组数据

HBase的rowkey安装字典序顺序排列 从小到大,所以需要最新数据时需要翻转时间戳或者自增id  ,所以在使用时间戳字段是最好是使用long.maxvalue-timestamp/1000 ,这样保证最新的数据总是在较新的位置,方便读取

预分区的设置也是由必要的,防止数据热点问题.,可以使用hash取余的方式去关键分区字段,保证了分区均匀性

所以最后设计的方案

分区字段-标识字段-标识字段-long.maxvalue-timestamp

其他我觉得有意义的点

减少列族及减少数据存储的开销,必要时减少限定符 ,直接将数据写成单独一条,其余处理代码中进行分割. 

在HBase中,value永远和它的key一起传输的。当具体的值在系统间传输时,它的rowkey,列名,时间戳也会一起传输。如果你的rowkey和列名很大,HBase storefiles中的索引(有助于随机访问)会占据HBase分配的大量内存,因为具体的值和它的key很大。可以增加block大小使得storefiles索引再更大的时间间隔增加,或者修改表的模式以减小rowkey和列名的大小。压缩也有助于更大的索引。

控制rowkey在16个字节以下 并维持在8的整数倍,符合64位系统的8字节对齐 ,最大不要超过64位

业务访问中权重高的key放在前面

例如URLRecords表的主要用途是用来计算当天的URL访问排名。根据业务需求,需要访问某天的所有URL,因此date是主键,权重更高,放在前面,而URL则放在后面。

构造冗余数据

例如,percontent的数据包含了URL Records的数据,URL Records的数据是冗余存储的,区别在于percontent的URL放在date前面,而URL Records表的URL放在date后面。这就是由于URL在满足不同需求的时候,权重不同,由于URL Records需要的数据量不大,因此采用冗余的机制解决该矛盾。权衡需求的重要性和系统忍受度选择一种方案当两种需求有矛盾,但其中一方属于次要需求,并且在系统忍受度范围之内的话,可以舍弃一种方案。优先满足需求更强的一方

Rowkey的时间属性问题

循环key使用(1)存在问题如果rowkey中有时间属性,并且随着时间的增加,rowkey会不断的增大下去的话,会造成region数量不断地增加。如果使用TTL来控制数据的生命周期,一些老的数据就会过期,进而导致老的region数据量会逐渐减少甚至成为空的region。这样一方面region总数在不断增加,另外一方面老的region在不断的成为空的region,而空的region不会自动合并,进而造成过多空的region占用负载和内存消耗的情况。(2)解决办法这种情况下,可以使用循环key的方法来解决。思路是根据数据的生命周期设定rowkey的循环周期,当一个周期过去以后,通过时间映射的方法,继续使用老的过期数据的rowkey。例如,key的格式如下:YY-MM-DD-URL。如果数据的生命周期是一年,则可以使用MM-DD-URL的格式。这样当前一年过去以后,数据已经老化,后一年的数据可以继续写入前一年的位置,使用前一年数据的rowkey。这样可以避免空的region占用资源的情况。

根据hbase的原理,key的周期需要至少比TTL大2* hbase.hregion.majorcompaction(默认24小时)的时间,才能够保证过期的数据能够在key循环回来之前得到完全清理。按照时间周期进行建表的方式也可以解决空region的问题,和循环key方法相比较,循环key的优点如下:

操作简单,不需要重复建表,系统自动处理

同样,循环key具有如下劣势:

需要使用TTL来老化数据,可能会增加compact负担

需要保证查询操作不会查询到过期数据,否则会影响系统性能。

如果在系统压力不是特别大,需要长期运行,能够控制查询不会查询到过期数据的场景下,建议使用TTL+循环key的方式,否则建议使用按照时间周期进行建表的方式。

通过rowkey设计来控制并发度

在相同业务模式下,不同的rowkey设计系统的并发度不一样。和按天建表的思路类似,通过rowkey控制并发度的原则是激活的region总数适中,每个regionserver的激活Region数大于1,小于(写操作内存/flushsize)为宜。

为了实现这一点,可以将可枚举、数量有限的属性放在rowkey的前面,时间放在后面的方式来提高并发度;通过将大粒度的时间属性(如天、小时等)放在rowkey前面,数量很大的可枚举属性(如电话号码、URL等)放在后面的方法来控制激活的region数。

 电信手机行业案例

案例一

xx_yy_zz_时间戳

xx为分区字段 必须散列

yy 和zz为标识查询字段  若可枚举按照字段枚举个数从小到大 方便查询

时间戳为最好查询字段标识

若表的用途是为了统计当天数据 可以将年月日放入rowkey 并排在yy之前

场景题

使用场景:

电信案例:查询某个人(手机号)某年[某月某日](时间)的通话详情。

1) 预分区

(1) 评估未来半年到一年的数据增长,不让其自动分区(10G)

    (2) 确定分区键

    00| 01| 02| ...

    000| 001| ...

2) 设计RowKey

(1) 确定分区号   (散列性)

    00_ 01_ 02_...

​

手机号%分区数            不够散列

(手机号+年月日)%分区数   按照月份、年进行查询  不方便

    (手机号+年月)%分区数

(2) 拼接字段     (唯一性、长度)

    XX_手机号_时间戳

    XX_手机号_年月日 时分秒

    XX_时间戳_手机号

    XX_年月日 时分秒_手机号

(3) 校验

    13412341234 2021-09-07

    XX_手机号_年月日 时分秒

    startRow:05_13412341234_2021-09-07

    stopRow :05_13412341234_2021-09-08

            05_13412341234_2021-09-07|

    XX_年月日 时分秒_手机号

    startRow:05_2021-09-07 00:00:00_13412341234

    stopRow :05_2021-09-08 00:00:00_13412341234

​

13412341234 2021-09  2021-11

XX_手机号_年月日 时分秒

    startRow:05_13412341234_2021-09

    stopRow :05_13412341234_2021-09|

            05_13412341234_2021-10

​

    startRow:03_13412341234_2021-10

    stopRow :03_13412341234_2021-11

​

    startRow:04_13412341234_2021-11

    stopRow :04_13412341234_2021-12      

<think>首先,用户的问题是:“我想了解HBaseRowKey设计原则 请问HBase RowKey 设计最佳实践”。这基于系统指令,我需要用中文回答,并遵守所有格式要求。 系统指令包括: - 行内数学表达式用$...$格式。 - 独立公式用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,如[^1]。 - 回答结构清晰,帮助用户逐步解决问题,保证真实可靠。 参考的引用内容: - [^1]:关于HBase RowKey设计原理和最佳实践,包括关键点如选择合适的字段、哈希函数、数据持久化等。 - [^2]:RowKey在数据检索和存储中的作用,设计法则如唯一性。 - [^3]:RowKey设计原则,如唯一性、利用排序特性。 - [^4]:RowKey范围划定RegionServer,建议以hash或md5开头来均匀分布。 这些引用需要整合到回答中,自然地添加引用标识。 回答结构: - 由于用户询问RowKey设计原则和最佳实践,我应该逐步解释。 - 结构清晰:可能分为原则、最佳实践、示例等。 - 最后生成相关问题。 生成相关问题:在回答结束时,添加“§§相关问题§§”部分,列出几个相关问题,如示例所示。 LaTeX使用: - 用户问题中没有明显的数学表达式,但如果有公式,如描述哈希函数或分布,可能需要用LaTeX。 - 例如,描述rowkey的哈希函数时,可以用$hash(key)$。 - 独立公式如果有,用$$...$$。 内容要点基于引用: 1. **设计原则**: - 唯一性:RowKey必须唯一,避免数据覆盖[^3]。 - 利用排序特性:RowKey按字典顺序排序,设计时应将相关数据放在一起以提高查询效率[^3]。 - 均匀分布:使用hash或md5前缀来确保数据在RegionServer间均匀分布,避免热点问题[^4]。 - 选择合适的字段:基于业务需求选择RowKey字段[^1]。 2. **最佳实践**: - 哈希前缀:如$md5(key) + timestamp$ 来分散数据[^4]。 - 组合键:使用复合键如$userID + timestamp$ 来组织数据[^1][^3]。 - 长度优化:保持RowKey长度合理,避免过长影响性能[^2]。 - 版本控制:考虑数据版本,在RowKey中嵌入时间戳[^1]。 3. **示例**:可以给出一个简单的代码或伪代码示例,如RowKey设计。 引用添加: - 在相关句子末尾添加[^n],如“RowKey必须唯一,以避免数据覆盖[^3]。” 生成相关问题: - 如“1. 如何避免HBase中的热点问题?” - “2. RowKey长度对性能的影响是什么?” - “3. 实际应用中如何设计RowKey?” 回答结构: 1. 引言:简要介绍RowKey的重要性。 2. 设计原则:列出关键原则。 3. 最佳实践:详细解释实践方法。 4. 示例:给出一个设计示例。 5. 总结。 6. 相关问题。 确保中文回答。</think>### HBase RowKey 设计原则与最佳实践 HBase 是一个分布式、面向列的 NoSQL 数据库,其性能高度依赖于 RowKey设计RowKey 作为数据的主键,决定了数据的存储位置、访问效率和系统扩展性。设计不当会导致热点问题(即某些 RegionServer 负载过高)、查询性能下降或数据分布不均。下面我将基于设计原则和最佳实践,逐步为您解析如何优化 RowKey 设计,确保内容真实可靠,并引用权威资料。 #### 1. **RowKey 设计原则** RowKey 设计需遵循以下核心原则,以确保数据高效存储和访问: - **唯一性原则**:RowKey 必须局唯一,否则插入相同 RowKey 的数据会覆盖原有数据。这源于 HBase 的 Key-Value 存储模型,其中 RowKey 是数据的唯一标识[^3]。 - **排序利用原则**:RowKey 按字典顺序排序存储,设计时应将经常一起访问的数据(如时间序列数据)放在相邻位置。例如,将用户 ID 和时间戳组合为 $userID + timestamp$,可加速范围查询[^3]。 - **均匀分布原则**:数据在 RegionServer 间应均匀分布,避免热点。RowKey 的范围决定了数据分区,因此需使用哈希或散列函数来分散数据。例如,以 $hash(key)$ 作为前缀,确保负载均衡[^4]。 - **字段选择原则**:RowKey 应基于业务需求选择关键字段(如用户 ID、时间戳),并考虑数据持久化和版本控制。例如,在时序数据中,嵌入时间戳可支持版本回滚[^1]。 这些原则共同作用:唯一性防止数据丢失,排序特性提升查询效率,均匀分布优化系统性能。数学上,RowKey 的分布可用均匀分布模型描述: $$ P(\text{rowkey}) = \frac{1}{N} $$ 其中 $N$ 是 RegionServer 数量,确保每个服务器负载均衡[^1]。 #### 2. **RowKey 设计最佳实践** 基于上述原则,以下是经过验证的最佳实践方法,结合实际场景优化性能: - **使用哈希前缀分散数据**:在 RowKey 开头添加哈希值(如 MD5 或 SHA-1),避免连续 RowKey 导致热点。例如,设计为 $md5(userID) + timestamp$。这能确保数据均匀分布在 RegionServer 上,减少单点压力[^4]。代码示例如下: ```java // Java 示例:生成带哈希前缀的 RowKey String userId = "user123"; String timestamp = "20231010"; String rowKey = MD5.hash(userId) + "_" + timestamp; // 例如 "a1b2c3d4_20231010" ``` 此方法适用于高写入场景,如日志系统,能将负载分散到多个 Region。 - **组合键设计**:将多个业务字段组合成复合键,提升查询效率。例如,在电商系统中,使用 $categoryID + productID + timestamp$,便于按类别或时间范围扫描数据。键长度应控制在 10-100 字节,过长会占用过多存储并降低扫描速度[^2][^3]。 - **嵌入时间戳优化版本控制**:在 RowKey 中包时间戳(如 $eventID + reverseTimestamp$,其中 reverseTimestamp 是倒序时间),支持高效检索最新数据。HBase 默认按时间戳降序存储,这能加速时间范围查询[^1]。 - **避免单调递增键**:不要使用纯自增 ID(如 1, 2, 3),这会导致所有新数据写入同一 Region,引发热点。改用随机前缀或哈希函数,如 $randPrefix + orderID$[^4]。 - **长度和编码优化**:使用二进制编码(如 Base64)压缩 RowKey,减少存储开销。同时,确保 RowKey 可读性强,便于调试[^1]。 #### 3. **实际应用场景示例** 以下是一个完整的最佳实践案例,展示如何设计 RowKey: - **场景**:物联网设备数据存储,设备每秒生成数据点。 - **问题**:直接使用 $deviceID + timestamp$ 可能导致热点,因为同一设备数据集中。 - **优化方案**:添加哈希前缀,设计为 $hash(deviceID) + deviceID + timestamp$。 - 数学推导:哈希函数 $h(key)$ 将 deviceID 映射到均匀分布区间,确保数据分散。 $$ h(\text{deviceID}) \mod N \approx \text{uniform} $$ 其中 $N$ 是 Region 数量,实现负载均衡[^1][^4]。 - **代码实现**: ```python # Python 示例:生成带哈希的 RowKey import hashlib def generate_rowkey(device_id, timestamp): hash_prefix = hashlib.md5(device_id.encode()).hexdigest()[:4] # 取 MD5 前4位 return f"{hash_prefix}_{device_id}_{timestamp}" # 例如 "a1b2_device001_20231010" ``` 此设计在查询时,可通过 Scan 操作高效检索设备历史数据,同时避免写入热点[^3]。 #### 总结 HBase RowKey 设计是系统性能的关键:原则聚焦唯一性、排序利用和均匀分布;最佳实践通过哈希前缀、组合键和长度优化提升效率。实际中,需根据业务需求(如查询模式和数据量)调整设计。例如,在金融风控系统,RowKey 设计优先考虑快速范围查询[^1][^3]。始终测试设计,使用工具如 HBase Shell 监控负载分布。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值