重要:一条数据的唯一标识就是 rowkey,那么这条数据存储于哪个分区,取决于 rowkey 处于 哪个一个预分区的区间内,设计 rowkey的主要目的 ,就是让数据均匀的分布于所有的 region 中,在一定程度上防止数据倾斜。
设计方案如下:
rowKey 设计格式 => ^A^Auser
hbase 设计 rowKey 使用的特点为: 适用性强 泛用性差 能够完美实现一个需求 但是不能同时完美实现多个需要。
如果想要同时完成两个需求,需要对 rowKey 出现字段的顺序进行调整。
调整的原则为:可枚举的放在前面。其中时间是可以枚举的,用户名称无法枚举,所以 必须把时间放在前面。
rowKey 设计格式 => date(yyyy-MM)^A^Auserdate(-dd hh:mm:ss ms)
每一个 region 维护着 startRow 与 endRowKey,如果加入的数据符合某个 region 维护的 rowKey 范围,则该数据交给这个 region 维护。
预分区的分区号同样需要遵守 rowKey 的 scan 原则。所有必须添加在 rowKey 的最前面, 前缀为最简单的数字。同时使用 hash 算法将用户名和月份拼接决定分区号。(单独使用用 户名会造成单一用户所有数据存储在一个分区)
根据12个月,规划120个分区,每个分区有startKey 和stopKey,采用startKey 作为分区号
添加预分区优化
startKey stopKey
000 001
001 002
002 003
...
119 120
每一个月份对应10个分区
000 到 009 分区 存储的都是 1 月份数据
010 到 019 分区 存储的都是 2 月份数据
...
110 到 119 分区 存储的都是 12 月份数据
rowKey 设计格式 => 分区号date(yyyy-MM)^A^Auserdate(-dd hh:mm:ss ms)
分区号=> hash(user+date(MM)) % 10 + 对应月份初始分区号
分区号填充 如果得到 85 => 085
例:zhagnsan 2022-02-14 12:34:45
分区号=hash(zhagnsan+date(02)) % 10 + 20 = 25
分区号补0 :025
用户名补^A :^A^Azhagnsan
rowKey => 0252022-02^A^Azhagnsan-14 12:34:45
可以穷举的写在前面即可 rowKey 设计格式 => 分区号date(yyyy-MM)^A^Auserdate(-dd
hh:mm:ss ms)
(1)统计张三在 2021 年 12 月份消费的总金额
分区号=> hash(user+date(MM)) % 10 + 110
scan: startRow => 分区号2021-12AAzhangsan stopRow => 2021-12AAzhangsan.
(2)统计所有人在 2021 年 12 月份消费的总金额
分区号=> hash(user+date(MM)) % 10 + 110
scan: startRow => 分区号2021-12 stopRow => 分区号2021-12.
属性:zookeeper.session.timeout
解释:默认值为 90000 毫秒(90s)。当某个 RegionServer 挂掉,90s 之后 Master 才 能察觉到。可适当减小此值,尽可能快地检测 regionserver 故障,可调整至 20-30s。
看你能有都能忍耐超时,同时可以调整重试时间和重试次数
hbase.client.pause(默认值 100ms)
hbase.client.retries.number(默认 15 次)
属性:hbase.regionserver.handler.count
解释:默认值为 30,用于指定 RPC 监听的数量,可以根据客户端的请求数进行调整,读写 请求较多时,增加此值。
属性:hbase.hregion.majorcompaction 解释:默认值:604800000 秒(7 天), Major Compaction 的周期,若关闭自动 Major Compaction,可将其设为 0。如果关闭一定记得自己手动合并,因为大合并非常有意义
属性:hbase.hregion.max.filesize
解释:默认值 10737418240(10GB),如果需要运行 HBase 的 MR 任务,可以减小此值, 因为一个 region 对应一个 map 任务,如果单个 region 过大,会导致 map 任务执行时间过长。该值的意思就是,如果 HFile 的大小达到这个数值,则这个 region 会被切分为两 个 Hfile。
属性:hbase.client.write.buffer
解释:默认值 2097152bytes(2M)用于指定 HBase 客户端缓存,增大该值可以减少 RPC 调用次数,但是会消耗更多内存,反之则反之。一般我们需要设定一定的缓存大小,以达到 减少 RPC 次数的目的。
属性:hbase.client.scanner.caching 解释:用于指定 scan.next 方法获取的默认行数,值越大,消耗内存越大。
属性:hfile.block.cache.size
解释:默认 0.4,读请求比较多的情况下,可适当调大
属性:hbase.regionserver.global.memstore.size
解释:默认 0.4,写请求较多的情况下,可适当调大
Lars Hofhansl(拉斯·霍夫汉斯)大神推荐 Region HStore 文件设置 20G,刷写大小设置 128M,其 它默认。
JVM 调优的思路有两部分:一是内存设置,二是垃圾回收器设置。
垃圾回收的修改是使用并发垃圾回收,默认 PO+PS 是并行垃圾回收,会有大量的暂停。 理由是 HBsae 大量使用内存用于存储数据,容易遭遇数据洪峰造成 OOM,同时写缓存的数 据是不能垃圾回收的,主要回收的就是读缓存,而读缓存垃圾回收不影响性能,所以最终设 置的效果可以总结为:防患于未然,早洗早轻松。
-XX:+UseConcMarkSweepGC
//在内存占用到 70%的时候开启 GC
-XX:CMSInitiatingOccupancyFraction=70
//指定使用 70%,不让 JVM 动态调整
-XX:+UseCMSInitiatingOccupancyOnly
//新生代内存设置为 512m
-Xmn512m
//并行执行新生代垃圾回收
-XX:+UseParNewGC
// 设 置 scanner 扫 描 结 果 占 用 内 存 大 小 , 在 hbase-site.xml 中,设置
hbase.client.scanner.max.result.size(默认值为 2M)为 eden 空间的 1/8(大概在 64M)
// 设置多个与 max.result.size * handler.count 相乘的结果小于 Survivor Space(新生代经过垃圾回收之后存活的对象)
官方给出了权威的使用法则: