中的rowkey 获取hbase_HBase 顺序序列rowkey和预分区规则的思考和设计

本文探讨了HBase中由于使用自增序列作为rowkey导致的数据热点问题,以及如何通过预分区和设计合适的rowkey策略解决。提出了两种rowkey设计方案:一种基于时间戳和分区的64位设计,另一种简化版的反向时间戳加序列号方案,最终选择了后者,因为它在存储空间、可读性和维护性上具有优势。
摘要由CSDN通过智能技术生成

以往工作中HBase存储海量数据时候,因为历史原因主键使用自增长序列,数据迁移到HBase中时,并没有改变主键策略。导致数据全部写入一个region,造成数据热点。当时也没有采用预分区,数据增长过快,region过大时,在系统低负载时段需手动切分region。这些原因导致集群整体效率低。

现在要求

目前正好有一个新的数据存储场景开始设计,对数据需要增量顺序读(如:增量构建全文索引等),中等规模随机读。能按照区间范围顺序查询,并能控制查询的开始和结束。以方便增量或重新构建索引或控制区间化数据读取需求等等。总结历史经验和教训, 现在需要考虑的2种场景:

海量文本数据A,天入库数据量在30-200W左右,文本格式长,需要保存原文。(数量现对小,单文本容量比较大,需要保存原文)

海量文本数据B,天数据量在500w-5000w之间,文本格式短小。(数量现对多,单文本容量相对小 )

HBase基本情况

表和索引组成

HBase一般由行键(row key)、列键(column qualifier )、列族(column family)组成。行键对应关键数据库中主键,HBase为行键建立了索引,列键归属列族。通过行键/列键/列族定位到一个唯一记录。

HBase中使用.META内部表存储region的分布情况以及每个region的详细信息。region中记录了rowkey范围,region分散在不同服务器中。通过region server提供访问数据访问服务,region server可服务多个region,来自不同region server上的region组合成表格的整体逻辑视图。

获取记录方式

通过get方式,指定rowkey获取唯一记录

通过scan方式,设置startRow和stopRow参数进行范围匹配

通过scan方式,全表扫描,并通过RowFilter过滤出数据

基本可归纳为顺序读和随机读。

rowkey原则

长度原则

行键长度尽量短和合理,因为持久化文件HFile中使用KeyValue形式保存数据,column family/column qualifier/rowkey会记录到每一条数据中,导致存储文件过大,也会导MemStore内存有效利用率降低。rowkey使用byte[]保存数据,使用数值(long)比字符(String)占用更小空间。64为系统内存8字节对齐,控制在16个字节,尽量使用8为倍数。

散列原则避免热点

加盐

在rowkey前按规则加随机数,使数据分散到不同region上避免热点。也可通过业务规则分段比如:userid-service-timestamp,把不同userid或不同业务数据切分到不同region。

哈希

生成哈希或使用UUID散列它。

反转

如果是顺序序列可以反转它,让他经常改变的部分排到前面避免数据集中。时间反转也可以考虑Long.Max_Value – timestamp。

唯一原则

主键都必须保证唯一。

分布式主键算法

分布式主键算法要求

毫秒级的快速响应

可用性强

prefix有连续性方便DB顺序存储

体积小,8字节为佳

目前分布式主键算法比较

UUID

16字节,JAVA自带,好用,占用空间大。

Twitter Snowflake

Snowflake: timestamp + work id + seq number

YBBRfy.png 图引用自其他Blog

8字节,可用性强,速度快。占用空间小,如果考虑复杂环境work id需要更好处理。twitter默认实现需要引入zookeeper 和独立的snowflake专用服务器,UC实现通过配置文件确定work id。

MongoDB ObjectId

ObjectId:timestamp + machine + pid + seq number

ema6fa.png 图引用自MongoDB 官方文档

12字节,可用性强,速度快。占用空间中等,用空间降低实现复杂度,基本没有其他依赖。

业务rowkey设计

业务的需求

大文本A每分钟最多1388条,每秒23条。

小文本B每分钟最多34722条,每秒578条。

考虑到大量顺序读,需要做到局部连续,全局分散。每秒极端情况写数据量不多,可考虑按照分钟分区。一共60个分区。获取一天数据时候通过60*24=1440 按照1440个局部连续批来获取数据。

rowkey规则1

参考snowflake和ObjectId原理,感觉它并不好直观分区,所以:

partition + timestamp + work id+ seq number 增加分区,减少时间和work id范围。

0-000000-01111111 10111111 11111000 00111100-00000000 0-00000000 00000000

1bit 不用

6bit 分区,可支持63个分区,可使用秒或分钟做分区

32bit 时间 System.currentTimeMillis()到秒(可以用到2099年)

9bit 区分机器和进程,需要在存储空间和复杂度上找平衡

16bit seq(最大65535)

全长64Bit,8Byte可做到不依赖其他服务。

分区可表示为:

0-000000-0000000 000000000 00000000 00000000-00000000 0-00000000 00000000

0-000001-0000000 000000000 00000000 00000000-00000000 0-00000000 00000000

0-000010-0000000 000000000 00000000 00000000-00000000 0-00000000 00000000

0-000011-0000000 000000000 00000000 00000000-00000000 0-00000000 00000000

rowkey规则2

规则1太复杂实现和可读性低简,简化下

reverse timestamp(mmHHddMMyyyy)+ seq number(0-99999)

seq number使用redis保证全局唯一,每个客户端使用步长减少redis访问频次。

例如:rowkey=20170719213012345表示为:30211907201712345使用分钟做分区30表示分区。

分区可表示为:

1000000000000000L:11100011010111111010100100110001101000000000000000

3000000000000000L:111000110101111110101001001100011010000000000000000

59000000000000000L:11010001100111000010111111111001101111111000000000000000

56Bit,7Byte 依赖redis服务 简单直观可读性好。

rowkey规则2顺序读取数据方式

假如我需要scan查询2017/07/19数据,我需要从201707190000~201707192359

60*24=1440做循环。2017/07/19/ 21:35为例子, StartRow和StopRow设置如下:

StartRow=35211907201700000L

StopRow=35211907201799999L

最终选择规则2作为rowkey规则,随然依赖redis服务,但是存储空间小、可读性高、可理解性好、方便使用和维护。

其他

不考虑HBase RowFilter方式,希望的效果就是直接利用rowkey内部索引和.META表。对于其他复杂组合查询,我倾向使用全文索引ES或Solr。

可以参考算法源码

UC Snowflake

MaongoDB ObjectID

Email:wei.liu@qq.com

刘威 2017年7月19日 长沙

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值