网站首页 > 博客文章 正文
一、热点:某一段时间内客户端的并发读写集中在某一个region或者某一台regionserver上,某一个region或regionserver的负载过大,是其他的好几倍。读写都在一个region,如果都在一台上就造成压力过大,浪费集群资源。所以hbase表一开始通过预分区都会创建多个region。
例子:比如一个region存储最多2000个记录,当region:超过2000时,就会触发split操作,分裂成两个。
region01:region02
那么2001就会放到region02。
当继续存放超过2000后,又会触发split,分裂成两个
region0201,region0202。
这样前一个region不会再存放数据,都是往下一个region的存放,就导致region的浪费。
避免写热点在更多数据情况下,数据应该被写入集群的多个region,而不是一个。
二、解决:hbase预分区
预分区创建可以控制数据往哪个region上放,startkey和endkey不是指存的rowkey,而是一个前缀。region rowkey的划分:一开始是没有开始和结束的,也就是说一开始是没有上限和下限
创建方式一:
create 't1', 'f1', SPLITS => ['10', '20', '30', '40'] 这个是划分区域,可以看成一个前缀,如果是字母的话,就会用ASCII转换实现。
10
10-20
20-30
30-40
40
创建方式二
也可以通过文件进行创建预分区
create 't2', 'f1', SPLITS_FILE
创建方式三
通过类进行加载的方式,设置region的个数,没有指定前缀,他也是有一定的逻辑进行指定,不是很适合业务上的使用
create 't3', 'f1', {NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}。
java代码
byte[][] splitKeys = new byte[][] { Bytes.toBytes("100000"),
Bytes.toBytes("200000"), Bytes.toBytes("400000"),
Bytes.toBytes("500000") };
三、rowkey设计
rowkey的长度原则,Rowkey是一个二进制码流,可以是任意字符串,最多给到64KB,建议10-100个字节,越短越好,不要超过16个字节。
原因一数据的持久化文件HFile中是按照KeyValue存储的,如果Rowkey过长比如100个字节,1000万列数据光Rowkey就要占用100*1000万=10亿个字节,将近1G数据,这会极大影响HFile的存储效率;
原因二MemStore将缓存部分数据到内存,如果Rowkey字段过长内存的有效利用率会降低,系统将无法缓存更多的数据,这会降低检索效率。
rowkey的散列原则(Hash散列):将顺序打乱,由多个字段进行组合[比较常见的组合]
比如timestamp + uuid uuid唯一标识,有可能是用户信息
首字段直接是时间信息就会将产生所有新数据都在一个RegionServer上堆积,热点现象
适合根据时间查询的业务
也可以是uuid + timestamp
例如:网站日志为例
-》登录网页-》后台生成日志-》存入hbase
不建议使用timestamp作为前缀,(首先是时间是连续的,就会造成热点问题)作为前缀适合根据时间查询的业务
可以使用uuid + timestamp组合的形式实现散列
时间戳在前的组合:比如去移动营业厅查话单
-》移动营业厅-》打印话单-》输入手机号码、服务密码-》选择打印的日期-》选择一个时间范围
比如:前面是一个手机号码,后面的是时间,20180320到20180520
rowkey
20180320142330
|
20180520142330
真的需要以时间戳和uuid的组合可以使用索引表
索引表(那么针对时间戳的使用可以设置索引表)
索引表的前缀是时间戳,后缀是uuid
timestamp + uuid
20180320142330_110
原来的rowkey
110_20180320142330
比如索引表为index -> 他的rowkey跟他相反
20180320142330_110
先检查索引表,从表中查符合时间段的值,得到符合的源表的rowkey对应的值,再到源表查到所要的数据
先检查索引表,从表中查符合时间段的值,得到符合的源表的rowkey对应的值,再到源表查到所要的数据
-》这种设计的难点:将源表和索引表数据进行同步,数据插入源表,同步到索引表
-》可以借助hbase协处理器来实现,或者借助其他的框架来处理,比如Phoenix,elasticsearch
二级索引:源表 rowkey 20180320+uuid -》基于源表create 二级索引表 20180320+uuid(一级),在源表数据基础上再以某个字段作为二级
-》加随机数
给rowkey分配一个随机前缀以使得它和之前排序不同,分配的前缀种类数量应该和你想使数据分散到不同的 region 的数量一致,写请求就会分散到多个 RegionServers,但是对读造成了一些负面影响
因为分配是随机的,所以如果你想要以字典序取回数据,你需要做更多工作
还有个反转字段
-》反转字段
20180320142330_110
反转:将时间戳反转,当然也可以加上uuid
03324102308102
13324102308102
23324102308102
读取的时候再反转过来,代码实现
-》Rowkey唯一原则,必须在设计上保证其唯一性
还有一种就是通过编码的格式,编码可以设置长度,固定编码的长度
-》使用编码:MD5或者CRC32进行编码方式的编码,实现散列原则和长度原则,编码固定每个字段的长度
和它的唯一性
-》列簇的个数建议不要太多,名称不要过长(集群规模非常大,硬件要求非常高,资源充沛,三四个左右)
Hbase目前对俩个或3个列族的处理不是很好,所以我们应尽可以保持列族数量少,尽可能只使用一个列族
目前 flushing 和 compactions 操作是以每一个 region 为基础的,所以如果一个列族大部分数据进行 flush 操作,将导致临近的列族也会 flush,即使它的数据量很小。当许多列族存在 flush 和 compaction操作时,会导致大量的 I/O 请求,消耗集群资源
四、hbase优化
1、垃圾回收参数配置(java里面的高级优化,就牵扯到jvm)
Java本身提供了垃圾回收机制,依靠JRE对程序行为的各种假设进行垃圾回收,但是HBase支持海量数据持续入库,非常占用内存,因此繁重的负载会迫使内存分配策略无法安全地依赖于JRE的判断:需要调整JRE的参数来调整垃圾回收策略
Java 中的堆是 JVM 所管理的最大的一块内存空间,主要用于存放各种类的实例对象。
在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old ) (永生代)
新生代几乎是所有 Java 对象出生的地方,新生代是 GC 收集垃圾的频繁区域
老年代:空间表较大、主要存储应用程序中生命周期较长的对象
-》GC的回收
-》新生代:刚刚new出来的对象,会放到新生代中存储,可能刚刚用完就不用了,那么GC就会回收掉,清理内存
-》如果清理过程中还在使用,经历了几次survivor(幸存者),发现这个对象还存在
-》 就会被放到老生代,存储生命周期比较长的
不同的GC会有不同的算法
Parrallel New Collector垃圾回收策略 【如果数据大的话就会GC停顿】
-》回收速度快
-》不适合数据集较大的场景
-》适合新生代
Concurrent Mark-Sweep Collector
-》速度相对较慢
-》适合数据集较大的场景
-》调优语句,设置大小和算法,在工作中放到hbase-env.sh文件中,设置环境变量,默认的也有
注意:HBASE_HEAPSIZE这个的大小
HBASE_OPTS/HBASE_REGIONSERVER_OPTS
export HBASE_REGIONSERVER_OPTS="-Xmx8g -Xms8G -Xmn128m -XX:UseParNewGC -XX:UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:$HBASE_HOME/logs/gc-${hostname}-hbase.log"
-》参数说明(相关资料):
配置说明:
-Xms 初始堆大小。如:-Xms256m
-Xmx 最大堆大小。如:-Xmx512m
-Xmn 新生代大小。通常为 Xmx 的 1/3 或 1/4
-Xmx8g -Xms8g –Xmn128m :最大堆内存8G,最小堆内存8G,新生代内存-Xmn128m
-XX:+UseParNewGC : 设置对于新生代的垃圾回收器类型,这种类型是会停止JAVA进程,然后再进行回收的,但由于新生代体积比较小,持续时间通常只有几毫秒
-XX:+UseConcMarkSweepGC :
设置老生代的垃圾回收类型,如果用新生代的那个会不合适,即会导致JAVA进程停止的时间太长,用这种不会停止JAVA进程,而是在JAVA进程运行的同时,并行的进行回收
-XX:CMSInitiatingOccupancyFraction :
设置CMS回收器运行的频率,避免前两个参数引起JAVA进程长时间停止,设置了这个之后,不需要停止JAVA进程,但是会提高CPU使用率
2、memstore & blockcache
HBase上RegionServer的cache主要分为两个部分,分别是memstore&blockcache,其中memstore主要用于写缓存,而blockcache用于读缓存
当数据写入hbase时,会先写入memstore,RegionServer会给每个region提供一个memstore,memstore中的数据达到系统设置的水位值后,会触发flush将memstore中的数据刷写到磁盘
在写的时候可以避过Hlog,直接写到内存中,速度会快点,但是也会造成数据丢失后不能再恢复
-》决定了满多少后触发flush进程,可以考虑调大这个值,在到达这个值之前,手动flush
【如果每个memstore没有达到这个限制,他还会有个限制,就是regionserver总的memstore达到40%,也会flush,他也有个刷新的低水位线,到达38%,就会停止,如果说达到这个条件同时集群还在跑很多任务,那么IO消耗可以说是非常大的,所以一般不会让他自动触发,可以考虑手动干预,可以flush一张表也可以一个region ->help 'flush',一般是在集群空闲的时候,job少
溢写:两个限制】128M
<property>
<name>hbase.hregion.memstore.flush.size</name>
<value>134217728</value>
<description>
Memstore will be flushed to disk if size of the memstore
exceeds this number of bytes. Value is checked by a thread that runs
every hbase.server.thread.wakefrequency.</description>
</property>
-》如果要禁用可以设置为0,占内存堆的40%
【结果要放到blockcache,首先有个前提你要开启他,LRU是个算法,上限是85%,告诉我们不要将一些不经常使用的数据放进去,会淘汰】
<property>
<name>hfile.block.cache.size</name>
<value>0.4</value>
<description>Percentage of maximum heap (-Xmx setting) to allocate to block cache
used by HFile/StoreFile. Default of 0.4 means allocate 40%.
Set to 0 to disable but it's not recommended; you need at least
enough cache to hold the storefile indices.</description>
</property>
cache的内存大小限制,在偏向读的业务中,可以适当调大该值,需要注意的是hbase.regionserver.global.memstore.upperLimit的值和hfile.block.cache.size的值之和必须小于0.8。
BlockCache: 基于客户端对数据的访问频率,定义了三个不同的优先级
##Single第一次被访问,加入到这个队列中
##Multi表示这个block被多次访问,由single转到Multi队列中
##inmemory优先级是最高的,一般比如meta表元数据信息都会放到这个队列中
如果表的family中有定义IN_MEMORY=true则该family下的块会设置为in-memory,一般访问特别频繁的数据可以这样设置
create 'stu_ibf',{NAME => 'f1',IN_MEMORY => true}
alter 'stu_info',{NAME => 'degree',IN_MEMORY => false},{NAME => 'info',IN_MEMORY => true},{NAME => 'work',IN_MEMORY => false}
以上将cache分级的好处在于:
首先,通过Memory类型的cache,可以将重要的数据放到RegionServer内存中常驻,例如Meta或者namespace的元数据信息
其次,通过区分single和multi类型cache,可以防止由于scan操作带来的cache频繁颠簸,将最少使用的block加入到淘汰算法中
默认配置下,对于整个blockcache的内存,按照以下百分比分配给single、multi和inMemory使用:0.25、0.5和0.25
3、本地memstore缓存
hbase.hregion.memstore.mslab.enabled
-》如果不是频繁new对象的场景,可以考虑默认开启
4、compact和split
在hbase中删除数据时并没有直接删除,而是打了标记,存到内存中,查看是看不到,当发生major compaction时才删除
client delete data -> data(flag) ->memory -> compaction -> delete data
过期失效的数据:比如字段里的年龄18,然后put一个20,18是不是就失效了,就是指被更新的数据
major会产生大量的IO操作,对HBase的读写性能产生影响。minor则只会选择数个HFile文件compact为一个HFile,minor的过程一般较快,而且IO相对较低。在日常任务时间,都会禁止mjaor操作,只在空闲的时段手动触发。
-》考虑到触发时的IO消耗问题,通常参数值设置为0,表示禁用,适合写不太频繁的场景,一周两三次手动触发,可以考虑禁用(单位毫秒)
<property>
<name>hbase.hregion.majorcompaction</name>
<value>604800000</value>
<description>The time (in miliseconds) between 'major' compactions of all
HStoreFiles in a region. Default: Set to 7 days. Major compactions tend to
happen exactly when you need them least so enable them such that they run at
off-peak for your deploy; or, since this setting is on a periodicity that is
unlikely to match your loading, run the compactions via an external
invocation out of a cron job or some such.</description>
</property>
手动触发:major_compact 't1'表 major_compact 'r1'行
-》当region的大小达到这个参数指定的值以后,单位:字节,触发split操作,10G
-》可以调大数值,线上配置100G左右,然后在到达数值之前人工手动split,尽量不要自动触发
<property>
<name>hbase.hregion.max.filesize</name>
<value>10737418240</value>
<description>
Maximum HStoreFile size. If any one of a column families' HStoreFiles has
grown to exceed this value, the hosting HRegion is split in two.</description>
</property>
0.9x的版本:1.x的又可能参数不一致
服务端
1.hbase.regionserver.handler.count:rpc请求的线程数量,默认值是30,生产环境建议使用100,也不是越大越好,特别是当请求内容很大的时候,比如scan/put几M的数据,会占用过多的内存,有可能导致频繁的GC,甚至出现内存溢出。
2.hbase.master.distributed.log.splitting:默认值为true,建议设为false。关闭hbase的分布式日志切割,在log需要replay时,由master来负责重放
3.hbase.regionserver.hlog.splitlog.writer.threads:默认值是3,建议设为10,日志切割所用的线程数
4.hbase.snapshot.enabled:快照功能,默认是false(不开启),建议设为true,特别是对某些关键的表,定时用快照做备份是一个不错的选择。
5.hbase.hregion.max.filesize:默认是10G, 如果任何一个column familiy里的StoreFile超过这个值, 那么这个Region会一分为二,因为region分裂会有短暂的region下线时间(通常在5s以内),为减少对业务端的影响,建议手动定时分裂,可以设置为60G。
6.hbase.hregion.majorcompaction:hbase的region主合并的间隔时间,默认为7天,建议设置为0,禁止自动的major主合并,major合并会把一个store下所有的storefile重写为一个storefile文件,在合并过程中还会把有删除标识的数据删除,在生产集群中,主合并能持续数小时之久,为减少对业务的影响,建议在业务低峰期进行手动或者通过脚本或者api定期进行major合并。
7.hbase.hregion.memstore.flush.size:默认值128M,单位字节,一旦有memstore超过该值将被flush,如果regionserver的jvm内存比较充足(16G以上),可以调整为256M。
8.hbase.hregion.memstore.block.multiplier:默认值4,如果一个memstore的内存大小已经超过hbase.hregion.memstore.flush.size * hbase.hregion.memstore.block.multiplier,则会阻塞该memstore的写操作,为避免阻塞,建议设置为5,如果太大,则会有OOM的风险。如果在regionserver日志中出现"Blocking updates for '<threadName>' on region <regionName> : memstore size <多少M> is >= than blocking <多少M> size"的信息时,说明这个值该调整了。
9.hbase.hstore.compaction.min:默认值为3,如果任何一个store里的storefile总数超过该值,会触发默认的合并操作,可以设置5~8,在手动的定期major compact中进行storefile文件的合并,减少合并的次数,不过这会延长合并的时间,以前的对应参数为hbase.hstore.compactionThreshold。
10.hbase.hstore.compaction.max:默认值为10,一次最多合并多少个storefile,避免OOM。
11.hbase.hstore.blockingStoreFiles:默认为7,如果任何一个store(非.META.表里的store)的storefile的文件数大于该值,则在flush memstore前先进行split或者compact,同时把该region添加到flushQueue,延时刷新,这期间会阻塞写操作直到compact完成或者超过hbase.hstore.blockingWaitTime(默认90s)配置的时间,可以设置为30,避免memstore不及时flush。当regionserver运行日志中出现大量的"Region <regionName> has too many store files; delaying flush up to 90000ms"时,说明这个值需要调整了
12.hbase.regionserver.global.memstore.upperLimit:默认值0.4,regionserver所有memstore占用内存在总内存中的upper比例,当达到该值,则会从整个regionserver中找出最需要flush的memstore进行flush,直到总内存比例降到该数以下,采用默认值即可。
13.hbase.regionserver.global.memstore.lowerLimit:默认值0.35,采用默认值即可。
14.hbase.regionserver.thread.compaction.small:默认值为1,regionserver做Minor Compaction时线程池里线程数目,可以设置为5。
15.hbase.regionserver.thread.compaction.large:默认值为1,regionserver做Major Compaction时线程池里线程数目,可以设置为8。
16.hbase.regionserver.lease.period:默认值60000(60s),客户端连接regionserver的租约超时时间,客户端必须在这个时间内汇报,否则则认为客户端已死掉。这个最好根据实际业务情况进行调整
17.hfile.block.cache.size:默认值0.4,regionserver的block cache的内存大小限制,在偏向读的业务中,可以适当调大该值,需要注意的是hbase.regionserver.global.memstore.upperLimit的值和hfile.block.cache.size的值之和必须小于0.8。
18.dfs.socket.timeout:默认值60000(60s),建议根据实际regionserver的日志监控发现了异常进行合理的设置,比如我们设为900000,这个参数的修改需要同时更改hdfs-site.xml
19.dfs.datanode.socket.write.timeout:默认480000(480s),有时regionserver做合并时,可能会出现datanode写超时的情况,480000 millis timeout while waiting for channel to be ready for write,这个参数的修改需要同时更改hdfs-site.xml
jvm和垃圾收集参数:
export HBASE_REGIONSERVER_OPTS="-Xms36g -Xmx36g -Xmn1g -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=15 -XX:CMSInitiatingOccupancyFraction=70 -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/data/logs/gc-$(hostname)-hbase.log"
Client端
1.hbase.client.write.buffer:默认为2M,写缓存大小,推荐设置为5M,单位是字节,当然越大占用的内存越多,此外测试过设为10M下的入库性能,反而没有5M好
2.hbase.client.pause:默认是1000(1s),如果你希望低延时的读或者写,建议设为200,这个值通常用于失败重试,region寻找等
3.hbase.client.retries.number:默认值是10,客户端最多重试次数,可以设为11,结合上面的参数,共重试时间71s
4.hbase.ipc.client.tcpnodelay:默认是false,建议设为true,关闭消息缓冲
5.hbase.client.scanner.caching:scan缓存,默认为1,避免占用过多的client和rs的内存,一般1000以内合理,如果一条数据太大,则应该设置一个较小的值,通常是设置业务需求的一次查询的数据条数
如果是扫描数据对下次查询没有帮助,则可以设置scan的setCacheBlocks为false,避免使用缓存;
6.table用完需关闭,关闭scanner(api)
7.限定扫描范围:指定列簇或者指定要查询的列,指定startRow和endRow(条件更加细粒化)
8.使用Filter可大量减少网络消耗(过滤器实在rs上直接运行)
9.通过Java多线程入库和查询,并控制超时时间。
10.建表注意事项:
开启压缩(snappy)
合理的设计rowkey
进行预分区
ZooKeeper调优
1.zookeeper.session.timeout:默认值3分钟,不可配置太短,避免session超时,hbase停止服务,线上生产环境由于配置为1分钟,如果太长,当regionserver挂掉,zk还得等待这个超时时间(已有patch修复),从而导致master不能及时对region进行迁移。
2.zookeeper数量:建议5个或者7个节点。给每个zookeeper 4G左右的内存,最好有独立的磁盘。
3.hbase.zookeeper.property.maxClientCnxns:zk的最大连接数,默认为300,无需调整。
4.设置操作系统的swappiness为0,则在物理内存不够的情况下才会使用交换分区,避免GC回收时会花费更多的时间,当超过zk的session超时时间则会出现regionserver宕机的误报
HDFS调优
1.dfs.name.dir:namenode的数据存放地址,可以配置多个,位于不同的磁盘并配置一个nfs远程文件系统,这样namenode的数据可以有多个备份
2.dfs.namenode.handler.count:namenode节点RPC的处理线程数,默认为10,可以设置为60
3.dfs.datanode.handler.count:datanode节点RPC的处理线程数,默认为3,可以设置为30
4.dfs.datanode.max.xcievers:datanode同时处理文件的上限,默认为256,可以设置为8192
猜你喜欢
- 2024-10-07 元数据与数据治理实战|Zookeeper在大型分布式系统中的应用(3)
- 2024-10-07 HCIE2020__路由交换专家__配置 IPSec VPN
- 2024-10-07 【0基础学爬虫】爬虫基础之抓包工具的使用
- 2024-10-07 Fiddler 窗口布局如何操作详解(fiddler界面)
- 2024-10-07 Zookeeper在大型分布式系统中的应用
- 2024-10-07 Linux下系统 I/O 性能分析的套路(linux ion)
- 2024-10-07 案例|如何定位无线访问互联网故障问题?
- 2024-10-07 「分享」非常全面的CentOS7系统安全检测和加固脚本
- 2024-10-07 Illustrate Asset UPnP Premium 2024.08.07音频设计的 UPnP 服务器软件
- 2024-10-07 IBM MQ---配置Explorer远程连接(ibm mq创建远程队列)
你 发表评论:
欢迎- 07-08Google Cloud Platform 加入支持 Docker 的容器引擎
- 07-08日本KDDI与Google Cloud 签署合作备忘录,共探AI未来
- 07-08美国Infoblox与Google Cloud合作推出云原生网络和安全解决方案
- 07-08GoogleCloud为Spanner数据库引入HDD层,将冷存储成本降低80%
- 07-08谷歌推出Cloud Dataproc,缩短集群启动时间
- 07-08Infovista与Google Cloud携手推进射频网络规划革新
- 07-08比利时Odoo与Google Cloud建立增强合作,扩大全球影响力
- 07-08BT 和 Google Cloud 通过 Global Fabric 加速 AI 网络
- 最近发表
-
- Google Cloud Platform 加入支持 Docker 的容器引擎
- 日本KDDI与Google Cloud 签署合作备忘录,共探AI未来
- 美国Infoblox与Google Cloud合作推出云原生网络和安全解决方案
- GoogleCloud为Spanner数据库引入HDD层,将冷存储成本降低80%
- 谷歌推出Cloud Dataproc,缩短集群启动时间
- Infovista与Google Cloud携手推进射频网络规划革新
- 比利时Odoo与Google Cloud建立增强合作,扩大全球影响力
- BT 和 Google Cloud 通过 Global Fabric 加速 AI 网络
- NCSA和Google Cloud合作开发AI驱动的网络防御系统,加强泰国网络空间的安全性
- SAP将在沙特阿拉伯 Google Cloud 上推出BTP服务
- 标签列表
-
- ifneq (61)
- 字符串长度在线 (61)
- googlecloud (64)
- messagesource (56)
- promise.race (63)
- 2019cad序列号和密钥激活码 (62)
- window.performance (66)
- qt删除文件夹 (72)
- mysqlcaching_sha2_password (64)
- ubuntu升级gcc (58)
- nacos启动失败 (64)
- ssh-add (70)
- jwt漏洞 (58)
- macos14下载 (58)
- yarnnode (62)
- abstractqueuedsynchronizer (64)
- source~/.bashrc没有那个文件或目录 (65)
- springboot整合activiti工作流 (70)
- jmeter插件下载 (61)
- 抓包分析 (60)
- idea创建mavenweb项目 (65)
- vue回到顶部 (57)
- qcombobox样式表 (68)
- tomcatundertow (58)
- pastemac (61)
本文暂时没有评论,来添加一个吧(●'◡'●)