hbase节点出问题,一般是ZK认为该hbase节点不可用,主动从ZK中踢出了该hbase节点;
该hbase节点发现ZK上自己被踢出,自己发起shutdown关闭服务;
一般解决问题思路是查看该hbase节点的日志,从日志入手解决问题,目前已知如下状况会导致节点宕机:
1)FULL GC,优化GC设置,修改HBASE_REGIONSERVER_OPTS,采用并发回收机制等;
2)所有分区memstore一起flushing,阻塞一切读写,达到ZK超时时间,归根结底是给的内存太少,加大HBASE_HEAPSIZE;
3)split操作阻塞了读写,达到ZK超时时间,提前做规划,提前预分区,防止后期频繁split;
1.前言
使用hbase有一段时间了,从最开始对hbase读写性能的怀疑,到最后对hbase读写性能的肯定,经历了一个漫长的过程,在此,对hbase相关性能优化写一点个人的总结。
2.官方关于性能优化(最权威)
所有关于技术类的文档,一般官网会有个优化建议,怎么去找呢,一般文档中搜索“Performance Tuning”,意思为性能优化,即可查到。
官方文档其实写的很全面,但点到即止,主要从操作系统、网络、Java、HBase 配置、ZooKeeper、Schema 设计阐述了相关性能优化建议,这里只是贴出文档地址,我在这里不做过多讲解。
3.性能优化关键点
3.1 操作系统优化
3.1.1 机器配置
hbase的机器配置建议2U 2cpu 6cores/cpu 16G*4 12 * 2T SATA;
hbase针对每个列簇每个区分配一个memstore=128MB供写数据,同时提供一个blockcache采用LRU等算法供读取数据,而hbase预分区越多,需要消耗的memstore和blockcache就更多,所以内存越多越好;
hbase的机器优选64位的,不过这都是目前所有机器的标配了。
3.1.2 linux打开文件数和进程数
默认linux打开文件数和打开进程数太低,试想一下,在分布式文件系统HDFS上打开成千上万的文件,原有的linux配置,远远不能满足需求,所以必须调大。
centos7修改/etc/security/limits.conf ,在最后增加如下内容:
* soft nofile 102400
* hard nofile 409600
centos7修改/etc/security/limits.d/20-nproc.conf,在最后增加如下内容:
* soft nproc 409600
* hard nproc 819200
3.1.3 机器时间
安装NTP服务保证hbase集群机器时间时刻同步,最少不要大于30秒(hbase机器间时差默认值),因为hbase的表里默认列timestamp都需要用到机器时间,而作为分布式列式数据库,机器间时间统一很重要。
3.1.4 交换区
建议将 /proc/sys/vm/swappiness 设置为最大值 10或者0。默认值为 60。使用 sysctl 命令在运行时更改该设置并编辑 /etc/sysctl.conf,以在重启后保存该设置。
#vi /etc/sysctl.conf
vm.swappiness = 10
3.1.5 禁用透明大页面压缩
默认启用透明大页面压缩,可能会导致重大性能问题。请运行“echo never > /sys/kernel/mm/transparent_hugepage/defrag”以禁用此设置,然后将同一命令添加到 /etc/rc.local 等初始脚本中,以便在系统重启时予以设置。
#vi /etc/rc.local
echo never > /sys/kernel/mm/transparent_hugepage/defrag
3.2 网络
网络设备最低选择千兆网卡,最好万兆网卡;
性能好的交换机;
跨机房多机架部署hadoop集群;
双电源确保断电故障。
3.3 java
3.3.1 JDK版本
JDK版本,首先要看hadoop对JDK版本要求,在hadoop2.7.1里要求最少JDK1.6+;
hbase里没明确说明,至少也是JDK1.6+;
目前多半是建议JDK1.8;
3.3.2 JVM参数调整
hbase基于HDFS之上,所以首先得优化HDFS内存,而HDFS里namenode节点内存直接决定你HDFS里最多文件个数,datanode里内存也相应要调整,最后是优化GC,在hadoop-env.sh里配置HADOOP_NAMENODE_OPTS和HADOOP_DATANODE_OPTS的内存和GC如下:
export HADOOP_NAMENODE_OPTS="-Xmx5g -Xms5g -Xmn256M -XX:SurvivorRatio=1 -XX:PermSize=128M -XX:MaxPermSize=128M -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv6Addresses=false -XX:MaxTenuringThreshold=15 -XX:+CMSParallelRemarkEnabled -XX:+UseFastAccessorMethods -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+HeapDumpOnOutOfMemoryError -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintTenuringDistribution -Xloggc:/home/hadoop/hadoop-2.7.1/logs/gc-hadoop-namenode.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=1 -XX:GCLogFileSize=512M"
export HADOOP_DATANODE_OPTS="-Xmx5g -Xms5g -Xmn256M -XX:SurvivorRatio=1 -XX:PermSize=128M -XX:MaxPermSize=128M -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv6Addresses=false -XX:MaxTenuringThreshold=15 -XX:+CMSParallelRemarkEnabled -XX:+UseFastAccessorMethods -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+HeapDumpOnOutOfMemoryError -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintTenuringDistribution -Xloggc:/home/hadoop/hadoop-2.7.1/logs/gc-hadoop-datanode.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=1 -XX:GCLogFileSize=512M"
3.4 hadoop优化
3.4.1 hadoop的HADOOP_NAMENODE_OPTS和HADOOP_DATANODE_OPTS优化
上面3.3.2里已经涉及
3.4.2 hadoop的HA
hbase是基于HDFS的,所以hadoop的尽量做hadoop HA部署,保证两个namenode(一个active 一个standby),保证HDFS故障后自动切换,高可用
3.4.3 hadoop的HDFS并发处理能力
hbase其实是HDFS的客户端,hbase的数据最终要落地到HDFS,所以并发处理HDFS的能力必须提升,下面的配置你必须优化:
- hadoop的core-site.xml的io.file.buffer.size
- hadoop的core-site.xml的io.compression.codecs
- hadoop的hdfs-site.xml的dfs.namenode.handler.count
- hadoop的hdfs-site.xml的dfs.datanode.handler.count
- hadoop的hdfs-site.xml的dfs.datanode.max.transfer.threads
- hadoop的hdfs-site.xml的dfs.datanode.balance.bandwidthPerSec
<property>
<!--hadoop访问文件的IO操作都需要通过代码库。因此,在很多情况下,io.file.buffer.size都被用来设置SequenceFile中用到的读/写缓存大小。不论是对硬盘或者是网络操作来讲,较大的缓存都可以提供更高的数据传输,但这也就意味着更大的内存消耗和延迟。这个参数要设置为系统页面大小的倍数,以byte为单位,默认值是4KB,一般情况下,可以设置为64KB(65536byte),这里设置128K-->
<name>io.file.buffer.size</name>
<value>131072</value>
</property>
<property>
<name>io.compression.codecs</name>
<value>org.apache.hadoop.io.compress.SnappyCodec</value>
</property>
<property>
<!--namenode并发线程数-->
<name>dfs.namenode.handler.count</name>
<value>600</value>
<description>The number of server threads for the namenode.</description>
</property>
<property>
<!--datanode并发线程数-->
<name>dfs.datanode.handler.count</name>
<value>600</value>
</property>
<property>
<!--这里设置Hadoop允许打开最大文件数,默认4096,不设置的话会提示xcievers exceeded错误-->
<name>dfs.datanode.max.transfer.threads</name>
<value>409600</value>
</property>
<property>
<!—start-balancer时,hdfs移动数据的速度,默认值为1M/S的速度。一般情况下设置为50M;设置的过大会影响当前job的运行-->
<name>dfs.datanode.balance.bandwidthPerSec</name>
<value>52428800</value>
</property>
3.5 zookeeper
zookeeper至少3台,其次最好奇数台,奇数是由zookeeper的少数服从多数的选举机制决定;
zookeeper.session.timeout默认为180秒,太长,修改zookeeper.session.timeout之前请首先一定要优化hbase的GC配置后才改此项值,hbase团队设置180秒是为了防止hbase初级使用者在不优化hbase GC的情况下,频繁因为GC导致hbase节点与zookeeper之间超时才设置的180秒,所以对于熟练者你改此值之前请确保你已经修改hbase GC。
3.6 hbase优化
3.6.1 hbase客户端优化
hbase客户端的源码我在另一篇博客源码解读--(1)hbase客户端源代码中进行介绍,了解源码只是为了让你能清醒的去优化hbase客户端。hbase客户端优化关键项目如下:
- hbase客户端里传入hbase.client.write.buffer(默认2MB),加到客户端提交的缓存大小;
- hbase客户端提交采用批量提交,批量提交的List<Put>的size计算公式=hbase.client.write.buffer*2/Put大小,Put大小可通过put.heapSize()获取,以hbase.client.write.buffer=2097152,put.heapSize()=1320举例,最佳的批量提交记录大小=2*2097152/1320=3177;
- hbase客户端尽量采用多线程并发写
- hbase客户端所在机器性能要好,不然速度上不去
下面是我当时在调研hbase时候做过的压测记录:
操作hbase你只需在maven里引入如下依赖项:
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.2.1</version>
</dependency>
建议的客户端操作代码如下:
Configuration configuration = HBaseConfiguration.create();
configuration.set("hbase.zookeeper.property.clientPort", "2181");
configuration.set("hbase.client.write.buffer", "2097152");
configuration.set("hbase.zookeeper.quorum","192.168.199.31,192.168.199.32,192.168.199.33,192.168.199.34,192.168.199.35");
Connection connection = ConnectionFactory.createConnection(configuration);
Table table = connection.getTable(TableName.valueOf("tableName"));
try {
// Use the table as needed, for a single operation and a single thread
// construct List<Put> putLists
List<Put> putLists = new ArrayList<Put>();
for(int count=0;count<100000;count++){
Put put = new Put(rowkey.getBytes());
put.addImmutable("columnFamily1".getBytes(), "columnName1".getBytes(), "columnValue1".getBytes());
put.addImmutable("columnFamily1".getBytes(), "columnName2".getBytes(), "columnValue2".getBytes());
put.addImmutable("columnFamily1".getBytes(), "columnName3".getBytes(), "columnValue3".getBytes());
put.setDurability(Durability.SKIP_WAL);
putLists.add(put);
//3177不是我杜撰的,是2*hbase.client.write.buffer/put.heapSize()计算出来的
if(putLists.size()>=3177-1){
//达到最佳大小值了,马上提交一把
table.put(putLists);
putLists.clear();
}
}
//剩下的未提交数据,最后做一次提交
table.put(putLists)
} finally {
table.close();
connection.close();
}
3.6.2 hbase服务端优化
3.6.2.1 hbase服务端源代码对于内存的分配规律
- 1)Hbase内存分配=memstore(写)+blockcache(读)+other(其他)
- 2)Memstore占内存百分比(写)+blockcache占内存百分比(读)<=0.8
- 3)Memstore有两个临界点,第一个临界点是hbase.regionserver.global.memstore.size.lower.limit,默认=0.95,达到这个点,会选择当前region里memstore最大那个flushing;第二个临界点hbase.regionserver.global.memstore.size,默认=0.4,达到这个点,所有region做flushing;
- 4)Blockcache通过hfile.block.cache.size设置,默认=0.4
3.6.2.2 hbase内存配置多大合适
经验公式如下:
hbase.hregion.memstore.flush.size*单机hbase的region个数/hbase.regionserver.global.memstore.size/hbase.regionserver.global.memstore.size.lower.limit
举例如下:我的hbase要求写入快,读取速度在写入速度之后考虑,那么我把内存尽可能多的给到写,所以我调整hbase.regionserver.global.memstore.size=0.6,hbase.regionserver.global.memstore.size.lower.limit=0.6,hfile.block.cache.size=0.1,这样0.6+0.1<0.8首先没有违背hbase的大原则,hbase.hregion.memstore.flush.size=128MB保持不变毕竟HDFS的block刚好也是128MB,我预估每个机器最后单节点上负载hbase100个区,那么我hbase节点的内存要配置的最大值为128MB*100/0.6/0.6=35555MB=35GB,所以修改hbase-env.sh里如下配置:
export HBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS –Xmx35g –Xms35g –Xmn2g -XX:SurvivorRatio=1 -XX:PermSize=128M -XX:MaxPermSize=128M -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv6Addresses=false -XX:MaxTenuringThreshold=15 -XX:+CMSParallelRemarkEnabled -XX:+UseFastAccessorMethods -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+HeapDumpOnOutOfMemoryError -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintTenuringDistribution -Xloggc:/home/hadoop/hbase-1.2.1/logs/gc-hbase-regionserver.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=1 -XX:GCLogFileSize=512M"
3.6.2.3 hbase 服务端GC优化
配置详见3.6.2.2
3.6.2.4 hbase的split和预分区
hbase从设计那一刻起就要尽最大可能规避hbase的split操作,split的意思是hbase单区文件大小过大,需要拆分为两个文件,而避免hbase不做split的最好的办法就是提前预分区,一个预分区建表语句如下:
disable 'habsetest'
drop 'habsetest'
n_splits = 108
create 'hbasetest', {NAME => 'info', TTL=>'15552000', COMPRESSION => 'SNAPPY'}, {SPLITS => (1..n_splits).map {|i| "#{i*999/n_splits}"}}
最好的预分区是做到今后都不会发生split操作,那么预分区多少呢?这里有个逐步计算方法:
- 首先你要知道你这个hbasetest数据表数据保留多久,比如保留半年,也即180天;
- 第二步,你得观察每天数据量,例如每天hbasetest的数据量会产生10GB的数据;
- 第三步,你得知道你配置的单个region文件的大小,比如hbase.hregion.max.filesize=53687091200,意思是单个region最大50GB;
- 第四步,开始计算,180天的数据量=10GB*180*HADOOP备份数3=5400GB,这些数据占用分区数=108,
- 第五步,开始建表时候你就知道你必须在设计时候就得建立108个预分区同时设置数据只保留15552000秒之内的数据,也即保留180天内的数据,这样,只要你的估算准确,永远不会进行split操作,就算做,也只是少数一两个区split做而已,基本不影响hbase读写性能。
3.6.2.5 hbase的compact
hbase的memstore会不断刷小文件,而compact会不断合并小文件和清理过期数据和标记删除的数据,compact又分major compact和minor compact,我们要尽量关闭major compact变成手动在空闲期让它做major compact,
<property>
<name>hbase.hregion.majorcompaction</name>
<value>0</value>
<description>禁止majorcompaction,这里虽然禁止了,但是还是得做,是通过linux定时任务在空闲时间执行</description>
</property>
在hbase空闲期通过设置linux 的crontab定时任务来做major compact
cd /opt/hbase-1.2.1/bin
./hbase shell
major_compact 'hbasetest'
quit
3.6.2.6 合理设计rowkey
rowkey一定要设计合理,关于rowkey,你要理解如下:
- hbase对于rowkey的处理是把rowkey按照ASCII码字典序来处理的,意思是ASCII对应的顺序字符的二进制顺序来处理,例如0-9字符的Byte值<大写字母A-Z<小写字母a-z;
- hbase会按照这种ASCII字典序把rowkey和每个区的start rowkey和end rowkey对比,就知道该把这条记录写到哪个区
所以,rowkey的设计一定要尽量使得记录随机化离散化,不然会导致数据倾斜
3.6.2.7 hbase的split策略
hbase的split策略有2个:
- IncreasingToUpperBoundRegionSplitPolicy策略的意思是,数据表如果预分区为2,配置的memstore flush size=128M,那么下一次分裂大小是2的平方然后乘以128MB,即2*2*128M=512MB;
- ConstantSizeRegionSplitPolicy策略的意思是按照上面指定的region大小超过30G才做分裂
默认的策略是IncreasingToUpperBoundRegionSplitPolicy,很多人向我讨教,为啥设置了hbase.hregion.max.filesize=53687091200,也即50GB一个区,但是还没达到50GB就做split了呢,原因就是这个策略并不是你所认为的策略,可能你压根就没改过split策略的配置。
所以如果你想超过50GB做split,那么首先你得配置hbase.hregion.max.filesize=53687091200,然后配置
<property>
<name>hbase.hregion.max.filesize</name>
<value>53687091200</value>
<description>设置每个数据表中单个region存储的hfile最大值50G,只有超过此值才做split</description>
</property>
<property>
<name>hbase.regionserver.region.split.policy</name>
<value>org.apache.hadoop.hbase.regionserver.ConstantSizeRegionSplitPolicy</value>
<description>这个需要和hbase.hregion.max.filesize结合使用</description>
</property>
hbase服务端优化补充说明
1)参数hbase.regionserver.handler.count的本质是设置一个RegsionServer可以同时处理多少请求。 如果定的太高,吞吐量反而会降低;如果定的太低,请求会被阻塞,得不到响应。你可以打开RPC-level日志读Log,来决定对于你的集群什么值是合适的。(请求队列也是会消耗内存的)。我的配置如下:
<property>
<name>hbase.regionserver.handler.count</name>
<value>300</value>
<description>Count of RPC Listener instances spun up on RegionServers.Same property is used by the Master for count of master handlers.</description>
</property>
2)hbase-env.sh中HEAP_SIZE优化
修改hbase-1.2.1/conf/hbase-env.sh中HBASE_HEAPSIZE,我的配置如下:
export HBASE_HEAPSIZE=4G
3)hbase内存配置,内存配置先要了解hbase内存模型,见下图:
- .每一个Region都有一个Memstore,Memstore默认大小为128MB,可通过hbase.hregion.memstore.flush.size更改;
- Region会随着split操作逐步增多,为了控制Memstore之和导致OOM错误,在hbase老版本中是通过hbase.regionserver.global.memstore.upperLimit和hbase.regionserver.global.memstore.lowerLimit进行控制,新版本中使用hbase.regionserver.global.memstore.size和hbase.regionserver.global.memstore.lowerLimit控制;
- Hbase-env.sh中HEAP_SIZE=4G时,老版本Hbase.regionserver.global.memstore.upperLimit(默认HEAP_SIZE*0.4)=1.6G,hbase.regionserver.global.memstore.lowerLimit(默认HEAP_SIZE*0.35)=1.4G,新版本hbase.regionserver.global.memstore.size(默认HEAP_SIZE*0.4)=1.6G和Hbase.regionserver.global.memstore.lowerLimit(hbase.regionserver.global.memstore.size*HEAP_SIZE*0.95)=1.52G;
- Memstore总和达到第一个临界值,会在所有memstore中选择一个最大的那个进行flushing,此时不会阻塞写;
- Memstore总和达到第二个临界值,会阻塞所有的读写,将当前所有memstore进行flushing。
- 每一个Region都有一个BlockCache,BlockCache总和默认打下为HEAP_SIZE乘以0.4,默认是通过hfile.block.cache.size设置;
- 所有的读请求,先到BlockCache中查找,基本Memstore中有的值在BlockCache中也都有,找不到再去Hfile中找。
- hbase中默认规定Memstore总和最大值(hbase.regionserver.global.memstore.size默认0.4)和BlockCache总和最大值(hfile.block.cache.size默认0.4)之和不能大于0.8,因为要预留0.2的HEAP_SIZE供其他操作使用,这个可详见hbase源代码Org.apache.hadoop.hbase.io.util.HeapMemorySizeUtil.java文件。
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hbase.io.util;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.classification.InterfaceAudience;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HConstants;
@InterfaceAudience.Private
public class HeapMemorySizeUtil {
public static final String MEMSTORE_SIZE_KEY = "hbase.regionserver.global.memstore.size";
public static final String MEMSTORE_SIZE_OLD_KEY =
"hbase.regionserver.global.memstore.upperLimit";
public static final String MEMSTORE_SIZE_LOWER_LIMIT_KEY =
"hbase.regionserver.global.memstore.size.lower.limit";
public static final String MEMSTORE_SIZE_LOWER_LIMIT_OLD_KEY =
"hbase.regionserver.global.memstore.lowerLimit";
public static final float DEFAULT_MEMSTORE_SIZE = 0.4f;
// Default lower water mark limit is 95% size of memstore size.
public static final float DEFAULT_MEMSTORE_SIZE_LOWER_LIMIT = 0.95f;
private static final Log LOG = LogFactory.getLog(HeapMemorySizeUtil.class);
// a constant to convert a fraction to a percentage
private static final int CONVERT_TO_PERCENTAGE = 100;
/**
* Checks whether we have enough heap memory left out after portion for Memstore and Block cache.
* We need atleast 20% of heap left out for other RS functions.
* @param conf
*/
public static void checkForClusterFreeMemoryLimit(Configuration conf) {
if (conf.get(MEMSTORE_SIZE_OLD_KEY) != null) {
LOG.warn(MEMSTORE_SIZE_OLD_KEY + " is deprecated by " + MEMSTORE_SIZE_KEY);
}
float globalMemstoreSize = getGlobalMemStorePercent(conf, false);
int gml = (int)(globalMemstoreSize * CONVERT_TO_PERCENTAGE);
float blockCacheUpperLimit = getBlockCacheHeapPercent(conf);
int bcul = (int)(blockCacheUpperLimit * CONVERT_TO_PERCENTAGE);
if (CONVERT_TO_PERCENTAGE - (gml + bcul)
< (int)(CONVERT_TO_PERCENTAGE *
HConstants.HBASE_CLUSTER_MINIMUM_MEMORY_THRESHOLD)) {
throw new RuntimeException("Current heap configuration for MemStore and BlockCache exceeds "
+ "the threshold required for successful cluster operation. "
+ "The combined value cannot exceed 0.8. Please check "
+ "the settings for hbase.regionserver.global.memstore.size and "
+ "hfile.block.cache.size in your configuration. "
+ "hbase.regionserver.global.memstore.size is " + globalMemstoreSize
+ " hfile.block.cache.size is " + blockCacheUpperLimit);
}
}
/**
* Retrieve global memstore configured size as percentage of total heap.
* @param c
* @param logInvalid
*/
public static float getGlobalMemStorePercent(final Configuration c, final boolean logInvalid) {
float limit = c.getFloat(MEMSTORE_SIZE_KEY,
c.getFloat(MEMSTORE_SIZE_OLD_KEY, DEFAULT_MEMSTORE_SIZE));
if (limit > 0.8f || limit <= 0.0f) {
if (logInvalid) {
LOG.warn("Setting global memstore limit to default of " + DEFAULT_MEMSTORE_SIZE
+ " because supplied value outside allowed range of (0 -> 0.8]");
}
limit = DEFAULT_MEMSTORE_SIZE;
}
return limit;
}
/**
* Retrieve configured size for global memstore lower water mark as percentage of total heap.
* @param c
* @param globalMemStorePercent
*/
public static float getGlobalMemStoreLowerMark(final Configuration c, float globalMemStorePercent) {
String lowMarkPercentStr = c.get(MEMSTORE_SIZE_LOWER_LIMIT_KEY);
if (lowMarkPercentStr != null) {
return Float.parseFloat(lowMarkPercentStr);
}
String lowerWaterMarkOldValStr = c.get(MEMSTORE_SIZE_LOWER_LIMIT_OLD_KEY);
if (lowerWaterMarkOldValStr != null) {
LOG.warn(MEMSTORE_SIZE_LOWER_LIMIT_OLD_KEY + " is deprecated. Instead use "
+ MEMSTORE_SIZE_LOWER_LIMIT_KEY);
float lowerWaterMarkOldVal = Float.parseFloat(lowerWaterMarkOldValStr);
if (lowerWaterMarkOldVal > globalMemStorePercent) {
lowerWaterMarkOldVal = globalMemStorePercent;
LOG.info("Setting globalMemStoreLimitLowMark == globalMemStoreLimit " + "because supplied "
+ MEMSTORE_SIZE_LOWER_LIMIT_OLD_KEY + " was > " + MEMSTORE_SIZE_OLD_KEY);
}
return lowerWaterMarkOldVal / globalMemStorePercent;
}
return DEFAULT_MEMSTORE_SIZE_LOWER_LIMIT;
}
/**
* Retrieve configured size for on heap block cache as percentage of total heap.
* @param conf
*/
public static float getBlockCacheHeapPercent(final Configuration conf) {
// L1 block cache is always on heap
float l1CachePercent = conf.getFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY,
HConstants.HFILE_BLOCK_CACHE_SIZE_DEFAULT);
float l2CachePercent = getL2BlockCacheHeapPercent(conf);
return l1CachePercent + l2CachePercent;
}
/**
* @param conf
* @return The on heap size for L2 block cache.
*/
public static float getL2BlockCacheHeapPercent(Configuration conf) {
float l2CachePercent = 0.0F;
String bucketCacheIOEngineName = conf.get(HConstants.BUCKET_CACHE_IOENGINE_KEY, null);
// L2 block cache can be on heap when IOEngine is "heap"
if (bucketCacheIOEngineName != null && bucketCacheIOEngineName.startsWith("heap")) {
float bucketCachePercentage = conf.getFloat(HConstants.BUCKET_CACHE_SIZE_KEY, 0F);
MemoryUsage mu = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
l2CachePercent = bucketCachePercentage < 1 ? bucketCachePercentage
: (bucketCachePercentage * 1024 * 1024) / mu.getMax();
}
return l2CachePercent;
}
}
综上所述,我在hbase-site.xml中配置信息如下:
<property>
<name>hfile.block.cache.size</name>
<value>0.3</value>
</property>
<property>
<name>hbase.regionserver.global.memstore.size.lower.limit</name>
<value>0.5</value>
</property>
<property>
<name>hbase.regionserver.global.memstore.size</name>
<value>0.5</value>
</property>
这样在HEAP_SIZE=4G时候,
hfile.block.cache.size计算值为4G*0.3=1.2G;
hbase.regionserver.global.memstore.size计算值为4G*0.5=2G;
hbase.regionserver.global.memstore.size.lower.limit计算值为4G*0.5*0.5=1G;
并且0.3+0.5<=0.8,没有超过hbase设置的不能超过0.8这个值
预分区补充说明
上图说明的问题:
1)创建表指定和不指定预分区是有本质区别的;
2)创建表不指定预分区,hbase默认只创建一个区,默认区大小为4GB,最开始读写数据都在这一个区,而这个区只是在集群一台机器上有,造成集群中单台机器负载过大,而其他机器都一直空闲;当文件大于10GB时,hbase暂停几分钟用来做split和compact,分裂为两个区,但新的数据写全部又集中到新的第二区,问题依旧是其他机器空闲;
3)创建表指定预分区,数据会根据提供的rowkey与建表时预分区做对比,将数据分布到不同预分区读写,达到负载均衡
结论:
建表必须指定预分区才能提高hbase并发读写性能,否则,就别玩hbase了。
rowkey设计补充说明
hbase默认是一级索引,一级索引指的是hbase对于rowkey方面的精确查询和范围查询都是很快的,所以,你用hbase尽量要将你的关注点设计到rowkey里面去。
也补充下哈,hbase目前外面也有开源的二级索引,比如华为的hindex —— 来自华为的 HBase 二级索引
上图是一个电话拨打记录存hbase的例子,说明问题如下:
1)不是有了预分区就行了的,rowkey的设计很关键,设计不合理,仍然会导致数据倾斜;
2)rowkey设计尽量达到数据的均匀分布
split和compact补充说明
3.4.1 hbase的split
1)了解hbase的split
hbase默认建表时如果不指定预分区,那么这个表就默认只有一个区,默认分区大小为10G,这个区里存储数据不断增大后,分区会进行split,split是根据不同算法来分裂的,算法通过hbase.regionserver.region.split.policy参数在hbase-site.xml指定。
算法一IncreasingToUpperBoundRegionSplitPolicy:策略的意思是,数据表如果预分区为2个,配置的memstore flush size=128M,那么下一次分裂大小是2的平方然后乘以128MB,即2*2*128M=512MB。也即就算默认每个区不是通过参数hbase.hregion.max.filesize设置了大小10G么,但是这个对于本算法来说不起作用啦!!!!!!!!!!!!!!是不是要崩溃!!!!!!
算法二ConstantSizeRegionSplitPolicy:策略的意思是按照上面指定的region大小超过10G才做分裂,不超过则坚决不分裂
2)hbase的split触发带来后果
阻塞该分区所在表所有读写,时间范围影响长,所以要尽量避免!!!!
3)我们能做到的优化措施:
- 正式线上环境,一定要预估算你的数据保留时间,这样可以在hbase table上设置TTL删除过期数据;
- 数据保留时间定下来,就是预估每天数据量,然后算出在保留时间内数据的最大值,比如1TB;
- 通过上面得到的最大值,设置每个预分区hbase.hregion.max.filesize文件最大值,比如50G;
- 最终得出你大致要建预分区20个(1TB/50GB=20),这样尽量保证最开始建的预分区就是最优,在后期也不会做分裂split动作
3.4.2 hbase的compact
1)了解hbase的compact
HBase的compact是针对HRegion的HStore进行操作的。
compact操作分为major和minor两种,major会把HStore所有的HFile都compact为一个HFile,并同时忽略标记为delete的KeyValue(被删除的KeyValue只有在compact过程中才真正被"删除"),可以想象major会产生大量的IO操作,对HBase的读写性能产生影响。minor则只会选择数个HFile文件compact为一个HFile,minor的过程一般较快,而且IO相对较低。在日常任务时间,都会禁止mjaor操作,只在空闲的时段定时执行。
2)生产环境中首先禁用major compact,在hbase-site.xml增加如下配置:
<property>
<name>hbase.hregion.majorcompaction</name>
<value>0</value>
</property>
3)空闲时候用linux shell脚本进行major compact
mkdir -p /home/hadoop/crontab
#vi hbase_major_compact_small.sh
cd /opt/hbase-1.2.1/bin
./hbase shell
major_compact 'small_table1'
major_compact 'small_table2'
quit
#vi hbase_major_compact_big.sh
cd /opt/hbase-1.2.1/bin
./hbase shell
major_compact 'big_table1'
major_compact 'big_table2'
quit
#编辑crontab服务文件
crontab -e
#然后贴入如下内容:
#晚上23:30执行脚本/home/hadoop/crontab/hbase_major_compact_small.sh
30 23 * * * /home/hadoop/crontab/hbase_major_compact_small.sh
#林晨00:30执行脚本/home/hadoop/crontab/hbase_major_compact_big.sh
30 0 * * * /home/hadoop/crontab/hbase_major_compact_big.sh
这样就可以在比较空闲的时候发起major_compact动作。
网上一篇比较好的文章:http://itindex.net/detail/49632-hbase-%E6%80%A7%E8%83%BD%E8%B0%83%E4%BC%98
HBASE GC补充说明
上面hbase经过一番优化之后,读写性能都提升上去了,又会面临新的问题,在高并发写时候,频繁的创建了大量对象,这时候java GC就会在某一时刻进行垃圾回收GC。
垃圾回收GC没有错,我们需要关注的点时,如何避免GC造成的所有读写阻塞,当读写阻塞达到一定时间时候,会触发如下动作:
- java的老生代被占满,触发FULL GC,导致hbase读写阻塞很长一段时间;
- zookeeper会认为这台regionserver已经处于不可用状态,将当前regionserver从zookeeper中踢出;
- 踢出的regionserver发现自己被zookeeper踢出,此时就主动shutdown HOOK
为了避免上面那段情况,我们能优化的是尽早GC,解决方法参见
我的优化是,首先调整hbase-env.sh中参数HBASE_REGIONSERVER_OPTS
export HBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS -Xmx4g -Xms4g -Xmn512M -XX:SurvivorRatio=1 -XX:PermSize=128M -XX:MaxPermSize=128M -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv6Addresses=false -XX:MaxTenuringThreshold=15 -XX:+CMSParallelRemarkEnabled -XX:+UseFastAccessorMethods -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=60 -XX:+UseCMSInitiatingOccupancyOnly -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+HeapDumpOnOutOfMemoryError -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+PrintTenuringDistribution -Xloggc:/opt/hbase-1.2.1/logs/gc-hbase-regionserver.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=1 -XX:GCLogFileSize=512M"
然后是在hbase-site.xml中增加如下配置:
<property>
<name>hbase.hregion.memstore.mslab.enabled</name>
<value>true</value>
<description>
Enables the MemStore-Local Allocation Buffer,
a feature which works to prevent heap fragmentation under
heavy write loads. This can reduce the frequency of stop-the-world
GC pauses on large heaps.</description>
</property>
<property>
<name>hbase.hregion.memstore.mslab.chunksize</name>
<value>2097152</value>
<description>
The default value of hbase.hregion.memstore.mslab.chunksize is defined in file
org.apache.hadoop.hbase.regionserver.HeapMemStoreLAB,the size is 2048 * 1024 bytes.
</description>
</property>
<property>
<name>hbase.hregion.memstore.mslab.max.allocation</name>
<value>262144</value>
<description>
The default value of hbase.hregion.memstore.mslab.max.allocation is defined in file
org.apache.hadoop.hbase.regionserver.HeapMemStoreLAB,the size is 256 * 1024.
</description>
</property>
上面做法的目的有点类似于memcached中分配不同大小的内存块从而减少内存碎片的出现,尽量使得内存充分被使用。
- 大小: 29 KB
- 大小: 32.6 KB
- 大小: 60.6 KB
- 大小: 43 KB
- 大小: 129.1 KB
- 大小: 23.7 KB
- 大小: 31 KB
- 大小: 36.2 KB
分享到:
相关推荐
本压缩包提供了这些组件的安装部署资源,便于快速搭建一个完整的Hadoop2.7.1、ZK3.5、HBase2.1和Phoenix5.1.0的基础环境。 首先,Hadoop是Apache开源项目,它提供了分布式文件系统(HDFS)和MapReduce计算框架,...
标题 "hadoop2.7.1+hbase2.1.4+zookeeper3.6.2.rar" 提供的信息表明这是一个包含Hadoop 2.7.1、HBase 2.1.4和ZooKeeper 3.6.2的软件集合。这个压缩包可能包含了这些分布式系统的安装文件、配置文件、文档以及其他...
### Hadoop2.7.1 + HBase1.3.5 在 CentOS6.5 虚拟机环境下的安装配置指南 #### 准备工作 为了确保 Hadoop 和 HBase 的顺利安装,需要提前做好一系列准备工作,包括安装 VMware、设置虚拟机、配置 CentOS 操作系统等...
与Hadoop 2.7.1一同提及的还有hive-1.2.1,Hive是基于Hadoop的数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供SQL查询功能。在Hive 1.2.1中,可能包含的改进有: 1. 性能优化,包括更快的查询执行...
在探讨Hadoop2.7.1、HBase1.0、Hive1.2以及ZooKeeper3.4.6的安装和配置时,我们首先需要了解这些组件的基本功能以及它们在整个大数据处理框架中所扮演的角色。以下对这些知识点进行详细说明: ### Hadoop2.7.1 ...
6. **Hadoop的配置**:在安装和运行Hadoop 2.7.1时,需要配置一系列的配置文件,如`core-site.xml`、`hdfs-site.xml`、`mapred-site.xml`和`yarn-site.xml`,以设定集群参数和优化性能。 7. **安全特性**:此版本...
本文将深入探讨Hadoop Common 2.7.1与HBase 2.0.0之间的关系,以及在Windows环境下如何正确安装和配置这两个组件。 Hadoop是Apache软件基金会开发的一个开源框架,主要用于处理和存储大规模数据集。Hadoop Common是...
Hadoop2.7.1是Hadoop发展中的一个重要版本,它在前一个版本的基础上进行了一系列的优化和改进,增强了系统的稳定性和性能。这个压缩包文件包含的是Hadoop2.7.1的中文文档,对于学习和理解Hadoop的运作机制、配置以及...
Hadoop2.7.1是Hadoop的一个稳定版本,提供了许多改进和新特性,旨在提高性能、可靠性和可管理性。 在Hadoop2.7.1安装包中,`hadoop-2.7.1.tar.gz`是主要的发布文件,包含了Hadoop的所有组件和依赖库。这个tarball...
Hadoop 2.7.1 是一个重要的版本,在大数据处理领域具有广泛的影响力。这个版本包含了Hadoop的核心组件,包括HDFS(Hadoop Distributed File System)和MapReduce,这两个组件是Hadoop生态系统的基础。HDFS提供了...
Hadoop2.7.1是Hadoop的一个重要版本,它带来了许多改进和优化,而Spark则是一个快速、通用且可扩展的数据处理框架,尤其在处理大规模数据时表现出色。Spark与Hadoop的兼容性是确保大数据工作流流畅运行的关键。 ...
Hadoop 2.7.1 是 Apache 基金会发布的一个开源分布式计算框架,它在大数据处理领域扮演着至关重要的角色。...通过持续的改进和优化,Hadoop 2.7.1 为用户提供了更加灵活、高效和可靠的分布式计算环境。
这个名为“hadoop-2.7.1.tar.gz.zip”的文件包含了Hadoop的2.7.1版本,这是一个非常重要的里程碑,因为它包含了对Hadoop生态系统的许多改进和修复。 首先,我们要明白文件的结构。这是一个压缩文件,最外层是.zip...
6. **Hadoop 性能优化**:包括对磁盘 I/O、网络传输、内存管理等方面的改进,使得 Hadoop 2.7.1 在处理大数据时能更高效地利用硬件资源。 7. **Hadoop 容错性**:通过心跳检测和数据副本机制,Hadoop 可以自动检测...
9. **性能优化**:文档还会涵盖如何优化Hadoop集群的性能,例如调整配置参数,优化数据本地性,以及处理数据倾斜问题。 10. **安全性**:在Hadoop 2.7.1中,安全性也是一个重要的主题,包括Kerberos认证、访问控制...
7. **优化与调优**:为了提高Hadoop集群的性能,需要考虑节点配置、网络拓扑、数据分布策略等因素,并可能需要调整MapReduce和HDFS的配置参数。 8. **安全性**:Hadoop 2.7.1支持Kerberos认证,增强了集群的安全性...