HBase 顺序序列rowkey和预分区规则的思考和设计

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

现在要求

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

  1. 海量文本数据A,天入库数据量在30-200W左右,文本格式长,需要保存原文。(数量现对小,单文本容量比较大,需要保存原文)
  2. 海量文本数据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

图引用自其他Blog

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

MongoDB ObjectId

ObjectId:timestamp + machine + pid + seq number

图引用自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

https://github.com/sumory/uc/blob/master/src/com/sumory/uc/id/IdWorker.java

MaongoDB ObjectID

https://github.com/mongodb/mongo-java-driver/blob/master/bson/src/main/org/bson/types/ObjectId.java

Email:wei.liu@qq.com
刘威 2017年7月19日 长沙

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容