HBase笔记4(调优)
Master/Region Server调优
JVM调优
默认的RegionServer内存是1G,而Memstore默认占40%,即400M,实在是太小了,可以通过HBASE_HEAPSIZE参数修改(CDH界面也可以修改)
1)通用调整,同时调整MASTER Region Server
vim $HBASE_HOME/conf/hbase-env.sh
export HBASE_HEAPSIZE=8G ===> 会生效HBASE的所有实例,MASTER和Region Server
2)Permsize调整
hbase-env.sh中含有配置如下:
意思为配置MAster Region Server的永久对象区(Permanent Generation,这个区域在非堆内存里面)占用128M的内存,这个配置在JDK7下运行,JDK8无需设置,设置也无用
3)设置JVM使用的最大内存
export HBASE_MASTER_OPTS="$HBASE_MASTER_OPTS -Xms4g -Xmx4g" 设置MASTER的JVM内存为4G 可以设置为其他大小
export HBASE_REGIONSERVER_OPTS="$HBASE_MASTER_OPTS -Xms8g -Xmx8g" 设置Region server内存为8G
注:系统需要至少10%内存进行必要操作
如何根据机器内存大小设置合适的Master或Region Server内存大小
官方参考:16G内存机器。同时包含MR ,Region Server ,DataNode服务
建议配置: 2G 系统
8G MR(1G=6*map+2*reduce)
4G HBase RegionServer
1G TaskTracker
1G DataNode
若同时运行MR任务,Region Server将是除MR以外使用内存最大的服务,若无MR,Region Server可以调到大概一半的服务器内存
4)Full GC问题
JVM越大,Full GC时间越长,Full GC时,JVM会停止响应任何请求,所以这种暂停叫Stop-The-World(STW),这会导致Region Server不响应Zookeeper的心跳,让Zookeeper误认自己宕机
,为防止脑裂,Region Server会叫停自己(自杀)
GC回收策略优化
JVM提供4种GC回收器:串行回收器 并行回收器(针对年轻代优化,JDK8默认) 并发回收器(CMS,针对年老带) G1GC回收器(针对大内存32G上优化)
组合方案1:并行GC+CMS
年轻代使用并行回收器 年老代使用CMS
$HBASE_HOME/conf/hbase-env.sh 中 HBASE_REGIONSERVER_OPTS内加入 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
-XX:+UseParNewGC 并行GC回收器
-XX:+UseConcMarkSweepGC CMS并发回收器
组合方案2:G1GC
内存小于4G,直接使用方案1
内存大于32G且JDK版本大于1.7,可考虑G1GC方案
引入原因:如下2中情况还是会触发Full GC
A. CMS工作时,一些对象从年轻代移动到年老代,但是年老代空间不足,此时只能触发Full Gc
B. 当回收掉的内存空间太碎太细小,导致新加入年老代的对象放不进去,只能触发Full GC来整理空间
G1GC策略通过把堆内存划分为多个Region,然后对各个Region单独进行GC,这样整体的Full GC可以被最大限度避免,该策略可以指定MaxGCPauseMillis参数来控制一旦发生
Full GC时的最大暂停时间,避免长时间暂停造成Region Server自杀,设置方式为 HBASE_REGIONSERVER_OPTS中添加参数 -XX:MaxGCPauseMillis=100
调试时加上调试参数: -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintAdaptiveSizePolicy
参考参数: 32G heap时 -XX:G1NewSizePercent=3
64G heap时 -XX:G1NewSizePercent=2
100G+ -XX:G1NewSizePercent=1
其他参数:
-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+parallelRefProcEnabled -XX:-ResizePLAB -XX:ParallelGCThreads=8+(40-8)(5/8)=28 -XX:G1NewSizePercent=1
5)memstore专属JVM策略MSLAB
100G内存使用CMS策略还发生Full GC原因
A. 同步模式失败
新生代过快的转化为老生代对象时发现老生代可用空间不够,此时会停止并发收集过程,转为单线程的STW(stop the world)暂停,这回到Full GC过程
可以设置 -XX:CMSInitiating0ccupancyFraction=N ,N代表当JVM启动来及回收时堆内存的占用百分比,设置越小,JVM越早启动垃圾回收,一般设置为70
B. 碎片化造成失败
memstore 定期刷写成一个HFile,memstore 占用的内存空间就会被回收,但memstore 占用的空间不是连续的且可能很小,就会出现碎片化
JVM提出了一个TLAB解决方案:当使用TLAB时,每个线程都会分配一个固定大小的内存空间,这样就不会出现特别小的碎片空间,但是会导致内存空间利用率低
HBASE实现了一个MSLAB方案,继承了TLAB,引入如下概念:
chunk: 一块内存,大小2M
RegionServer维护一个全局的MemStoreChunkPool实例,是一个chunk池
每个Memstore实例里又一个MemStoreLAB实例
当MemStore接收到keyvalue数据时,先从chunk pool中申请一个chunk,然后放到这个chunk里
如果这个chunk满了,再申请一个
memstore因为刷写而释放内存,则按chunk来清空内存
设置参数: hbase.hregion.memstore.mslab.enabled=true 打开MSLAB,默认为true
hbase.hregion.memstore.mslab.chunksize=2M 设置每个chunk大小,默认2M
hbase.hregion.memstore.mslab.max.allocation=256K 能放入chunk的最大单元格大小,默认256K
hbase.hregion.memstore.chunkpool.maxsize=0.0 在整个memstore可以占用的堆内存中,chunkpool可以占用的比例(0.0~1.0),默认0.0
hbase.hregion.memstore.chunkpool.initialsize=0.0. regionserver启动时可以预分配一些空的chunk出来到chunk pool里面待使用,该值代表了预分配chunk占总chunkpoll的比例
Region性能优化
Region的自动拆分
1)按照固定大小拆分
0.94版本的HBase只有一种拆分策略,按照固定大小拆分,参数 hbase.hregion.max.filesize.region=10G ,当region超过10G则HBase拆分成2个Region
2)限制不断增长的文件尺寸策略
文件的尺寸限制是动态的,依赖以下公式计算: Math.min(tableRegionsCount^3*initialSize,defaultRegionMaxFileSize)
tableRegionCount : 表在region server上拥有的region数量总和
initialize: 若定义了 hbase.increasing.policy.initial.size,则使用这个数值,否是就用memstore 刷写的2倍,即hbase.hregion.memstore.flush.size*2
defaultRegionMaxFileSize: region得最大大小,即hbase.hregion.max.filesize
math.min 取这2个数值得最小值
3)自定义拆分点(依据行键前缀)
保证拥有相同前缀得rowkey不会被拆分到2个不同得region里面 ,涉及参数keyPrefixRegionSplitPolicy.prefix_length 前缀得长度
该策略会依据前缀长度参数截取rowkey最为分组依据,同一个组得数据不会被划分到不同region上
4)依据行键得分隔符(也是根据行键前缀进行切分)
这个策略需要表定义以下属性:DelimitedKeyPrefixRegionSplitPolicy.delimiter 前缀分隔符
例: 定义分隔符 _, host1_001得前缀为host1
5)访问频率拆分(热点拆分)
涉及参数: hbase.busy.policy.blockRequests:请求阻塞率,即请求被阻塞得严重程度,取值0.0~1.0,默认0.2,20%请求被阻塞
hbase.busy.policy.minAge:拆分得最小年龄,防止在判断是否拆分时出现短时间得访问频率波峰,结果没必要拆分Region被拆
hbase.busy.policy.aggWindow 计算是否繁忙得时间窗口,单位毫秒,默认5min,用以控制计算频率
计算Region是否繁忙得计算方法:
当前时间-上次检测时间=>hbase.busy.policy.aggWindow,则进行如下计算:这段时间被阻塞得请求/这段时间得总请求 = 请求得被阻塞率,如果请求得被阻塞率 > hbase.busy.policy.blockRequests ,则该Region为繁忙
6)永不拆分策略
Region的手动拆分
手动拆分有2种情况,预拆分和强制拆分
Region的预拆分
建表时定义拆分点的算法,叫预拆分,使用org.apache.hadoop.hbase.util.RegionSplitter类创建表,并传入拆分点算法就可以在建表同时定义拆分点算法
ex:hbase org.apache.hadoop.hbase.util.RegionSplitter my_split_table HexStringSplit -c 10 -f mycf
新建表my_split_table,并根据HexStringSplit拆分点算法预拆分为10个Region,同时建立一个列族mycf
拆分点算法:
HexStringSplit 算法参数 n:要拆分的region数量
把数据从00000000到FFFFFFFF之间的数据长度按照n等分之后,算出每一段的起始rowkey和结束rowkey,以此作为拆分点
UniformSplit
起始rowkey是ArrayUtils.EMPTY_BYTE_ARRAY
结束rowkey是new byte[] {xFF,xFF,xFF,xFF,xFF,xFF,xFF,xFF}
最后调用Bytes.split把起始行键和结束行键之间长度n等分,然后取每一段的起始和结束作为拆分点
手动指定拆分点:建表时指定SPLITS参数
create 'table1','mycf2',SPLITS=>['aaa','bbb']
Region强制拆分
ex: split 't1,c,14764.96dd8‘,’999' 把t1,c,14764.96dd8这个region从新的拆分点999处进行拆分
split方法的调用方式:
split ‘表名’
split ‘namespace:tablename’
split 'regionName' #format: 'tableName,startKey,id'
split 'tableName','splitKey'
split 'regionName','splitKey'
推荐方案:
预拆分导入初始数据。然后使用自动拆分让HBase管理Region
Region的合并
通过merge进行合并(冷合并)
通过使用org.apache.hadoop.hbase.util.Merge类实现
ex: hbase org.apache.hadoop.hbase.util.Merge tablename regionname1 regionname2 (需要先停止Hmaster,Hregionserver)
热合并
hbase shell提供了online_merge 来实现热合并,参数是region的hash值
ex: merge_region region1hash值 region2hash值 ====》2个region都需要上线
WAL优化
WAL文件的数量当增长到一定程度时会出现滚动,清除旧日志
WAL优化参数:hbase.regionserver.maxlogs: Region种最大WAL文件数量,默认32
新版HBase会自动设置,公式
hbase.regionserver.hlog.blocksize: HDFS块大小,不设定则使用HDFS的设置值
hbase.regionserver.logroll.multiplier WAL 文件大小因子,默认0.95
每个WAL文件大小=HDFS块大小*WAL文件大小因子
读请求到达HBase之后,先查询Block Cache,如果获取不到就去HFile和Memstore中获取,如果获取到了则在返回数据的同时把Block块缓存到Block Cache中
Block Cache默认开启,若要某个列族禁止使用Block Cache,执行alter 't1',CONFIGURATION=> {NAME=> 'cf',BLOCKCACHE=>'false'}
Block Cache实现方案:
1)LRUBlock Cache (基于JVM heap 近期最少使用算法缩写, 一级缓存)
LRUBlock Cache的3个区域
hfile.block.cache.size.LRUBlockCache 占用内存比例,默认0.4
设置hfile.block.cache.size时需要注意Memstore+BlockCache内存占用比例不能超过0.8,即80%
hfile.regionserver.global.memstore.size+hfile.block.cache.size <=0.8
2)SlabCache (堆外内存尝试方案)
调用nio的DirectByteBuffers,把堆外内存按照80%,20%比例划分为2个区域: 存放大小约1个blocksize默认值的Block;存放大小约2个blocksize默认值的block
该方案可用性不大,放弃
3)Bucket Cache (二级缓存)
BucketCache分配了14种区域,存放大小分别为4K,8K,16K,32K,40K,48K,56K,64K,96K,128K,192K,256K,384K,512K的Block
可以手动配置 hbase.bucketcache.bucket.sizes属性来定义
Bucket Cache存储可以通过hbase.bucketcache.ioengine进行设置,为堆heap,堆外offheap,文件file
每个Bucket的大小上限为最大尺寸的Block*4,每种类型的Bucket至少要一个Bucket
BucketCache可以自己划分内存空间,自己管理内存空间,所以内存碎片少
配置项:默认开启
列族关闭BucketCache:
alter 'table',CONFIGURATION => {CACHE_DATA_IN_L1=>'true'}
hbase.bucketcache.ioengine:使用的存储介质 ,可选heap,offheap(默认),file
hbase.bucketcache.combinedcache.enabled 是否打开组合模式,默认true
hbase.bucketcache.size bucketcache所占大小 ,0关闭 0.0~1.0 代表占堆内存百分比 ,大于1,代表实际的大小,单位MB
hbase.bucketcache.bucket.sizes 定义所有block种类,默认14种
-XX:MaxDirectMemorySize JVM启动参数,定义JVM可获取堆外内存的上限,必须比hbase.bucketcache.size大
4)组合模式
具体来说:把不同类型的Block分别存放到LRUCache BucketCache中
合理介质:一级缓存->二级缓存->硬盘 数据从小到大 存储介质由快到慢
LRUCache使用内存-> BucketCache 使用SSD-> HFile使用机械硬盘
查看缓存命中率: hbase.regionserver.blockCacheHitRatio 取值范围0.0~1.0
MemStore优化
读写中的memstore
memstore实现的目的是维持数据结构
开启Block Cache,读取数据先查询Block Cache,Block Cache查询失败后,则查询memstore+HFile数据,由于有些数据还未刷写HFile,
所以memstore+HFile才是所有数据集合
memstore的刷写
memstore的优化核心在于理解memstore的刷写flush,大部分性能问题是写操作被block,无法写入Hbase
memstore触发刷写机制:
1. 大小达到刷写阈值 hbase.hregion.memstore.flush.size
由于刷写是定期检查的,所以无法及时触发阈值刷写,若数据增长太快,提前超出阈值,会触发阻塞机制,此时数据无法写入memstore
阻塞机制的阈值=hbase.hregion.memstore.flush.size*hbase.hregion.memstore.block.multiplier
=刷写阈值(默认128M)*倍数
数据到达阻塞阈值,直接触发刷写并阻塞所有写入该store的写请求
2. 整个Region Server的memstore总和达到阈值
触发刷写阈值=globalMemstoreLimitLowMarkPercent*globalMemStoreSize
=全局memstore刷写下限 (百分比0.0~1.0) * 全局memstore容量
全局memstore容量=hbase_heapsize(RegionServer占用堆内存大小)*hbase.regionserver.global.memstore.size(默认0.4)
触发阻塞阈值= globalMemStoreSize
4. memstore达到刷写时间间隔
hbase.regionserver.optionalcacheflushinterval memstore刷写时间间隔,默认1个小时,设置为0,则关闭自动定时刷写
5. 手动触发flush
flush 'table_name' 刷写单个表
flush ‘region_name’ 刷写单个region
memstore性能优化主要是防止触发阻塞,避免对业务造成灾难性性后果
HFile性能优化(合并) compaction
Hfile文件过多,读取数据寻址就多了,为了防止过多的寻址动作,适当减少碎片文件,需进行合并操作
合并分为2种操作:
Minor Compaction: 将多个HFile合并为一个HFile.在这个过程中达到TTL的数据会被移除,但是手动删除的数据不会被移除,触发频率高
Major Compaction:合并一个store所有的HFile为1个,手动删除的数据会被移除,同时删除单元格内版本超过maxversion版本数据,触发频率低,7天一次,但是消耗的性能较大
0.96版本后提出一个合并算法 ExploringCompactionPolicy算法
该算法修改合并文件挑选条件: 该文件 < (所有文件大小总和 - 该文件大小) * 比例因子
若果该文件的大小小于最小合并大小(minCompactionSize),则直接进入待合并列表
最小合并大小: hbase.hstore.compaction.min.size 若没有设置则使用hbase.hregion.memstore.flush.size
以组合作为计算单元: 被挑选的文件必须通过筛选条件,并且组合内含有文件数必须大于hbase.hstore.compaction.min
小于hbase.hstore.compaction.max (就是选择一个组合内几个文件进行合并)
FIFOCompactionPolicy
本质是一种删除策略,最终效果是 : 过期的块被整个删除,没过期的块完全没有操作
不能应用的环境: 1)表没有设置TTL,或TTL=forever
2) 表设置了MIN_VERSION,并且MIN_VERSION > 0
DateTiredCompactionPolicy
配置项:hbase.hstore.compaction.date.tiered.base.window.millis 基本的时间窗口时长,默认6小时,即从现在开始的6个小时内HFile都在同一个时间窗口里面
hbase.hstore.compaction.data.tiered.window.per.tier 层次的增长倍数,分层时越老的时间窗口越宽
同一个窗口里面的文件如果达到最小合并数量(hbase.hstore.compaction.min)就会进行合并,根据hbase.hstore.compaction.date.tiered.window.policy.(所定义的合并规则来合并),默认ExploringCompactionPolicy
hbase.hstore.compaction.date.tiered.max.tier.age.millis 最老的层次时间,当文件老到超出定义的时间,就不进行合并
如果一个文件跨时间线的时候,该文件计入下一个时间窗口(更老更长的时间窗口)
StripeCompactionPolicy 适合读性能
每一层文件大概是上一层的10倍
合并时会把keyvalue从level 0(L0) 读取出来,然后插到level1 的FILE文件中,level1是根据键位范围(rowkey range)来划分
适用场景: Region很大,Row Key具有统一的格式
Compaction的吞吐量限制参数:
compaction机制会造成IO突然降低问题
相关可设置参数: hbase.regionserver.throughtput.controller 限制的类型对应类名,可选 PressureAwareCompactionThroughtController控制合并相关指标 和 PressureAwareFlushThroughtputController 控制刷写相关指标,默认为PressureAwareCompactionThroughtputController
hbase.hstore.blockingStoreFiles 当StoreFile数量达到该值,阻塞刷写动作,默认7
该参数设定不当可能造成memstore占用内存数量急剧上升,到达上限,集群就会挂掉,可设置20-50
hbase.hstore.compaction.throughtput.lower.bound 合并占用吞吐量下限
hbase.hstore.compaction.throughtput.higher.bound 合并占用吞吐量上限
合并/刷写吞吐量限制机制:
HBase 计算 合并/刷写 占用的吞吐量,当吞吐量过大时会适当休眠
hbase.offpeak.start.hour 每天的非高峰起始时间,取值0-23
hbase.offpeak.end.hour 每天非高峰结束时间,取值0-23
休眠吞吐量的阈值=lowerBound + (upperBound - lowerBound) * pressureRatio
lowerBound :
hbase.hstore.compaction.throughput.lower.bound 合并占用吞吐量下限 默认10MB/s
upperBound:
hbase.hstore.compaction.throughtput.higher.bound 合并占用的吞吐量下限 默认20MB/S
pressureRatio 压力比 限制合并时,该参数就是合并压力,显示刷写时,该参数就是刷写压力,取值0-1.0
压力比越大,HFile堆积的越多,或即将产生越多的HFile
压力比分为 : 合并压力 刷写压力
合并压力计算:=(storefilecount - minFilesToCompact)/(blockingFileCount - minFilesToCompact)
storefilecount 当前storeFile数量
minFilesToCompact:单次合并文件数量下限,即hbase.hstore.compaction.min
blockingFileCount = hbase.hstore.blockingStoreFiles
刷写压力计算:= globalMemstoreSize/memstoreLowerLimitSize
globalMemstoreSize 当前memstore大小
memstoreLowerLimitSize Memstore刷写下限,当全局memstore达到这个内存占用数量时开始刷写
HBase 合并文件流程:
1)获取需要合并的Hfile列表
获取列表时需要除掉带锁的HFile,锁分为读锁 写锁
以下操作会上锁: 用户scan查询,上Region锁 ;Region切分,region先关闭上写锁;Region关闭,写锁;Region导入,写锁
2)由列表创建StoreFileScanner
HRegion 会创建一个Scanner,读取本次要合并的所有StoreFile上的数据
3)把数据从HFile中读出,并放到tmp目录(临时文件夹)
HBase 会在临时目录创建新的HFile,并使用建立的Scanner从旧HFile上读取数据,放入新HFile
以下数据不会读取: 数据过期了 带墓碑标记的数据
4)用合并后的HFile来替换合并前的HFile
Major compaction:
hbase.hregion.majorcompaction majorcompaction发生的周期,默认7天 ,等于0则为关闭
hbase.hregion.majorcompaction.jitter 周期抖动参数 0-1.0,可以让major compaction发生时间更灵活,默认0.5
major compaction对系统的压力还是很大,建议关闭自动选择手动触发方式