Hbase
来源:
1、hdfs的数据读写延迟高
2、不能近实时更新删除局部数据
3、hive的数据必须要指定的列或者字段,必须要格式化的数据。
4、hbase来源于google的bigtable。
定义:
Hbase是一个基于Hadoop的开源, 分布式的,多版本的,可扩展的,非关系型数据库,能够处理海量数据(数十亿行和百万列)。
特性:
Hbase的表模型和关系型数据库的表模型不同
Hbase的表没有固定的字段定义
Hbase的表每行存储的都是一行key-value对
Hbase的表中有列簇的划分,用户可以指定将哪些kv插入到哪个列簇
Hbase的表在物理存储上是按照列簇来分割的,不同的列簇的数据一定存放在不同的文件中
Hbase的表中每一行都固定有一个行键,而且每一行的行键在表中不能重复
Hbase中数据,包含行键,包含key,包含value,都是byte[]类型,Hbase不负责为用户维护数据
Hbase对事物的支持性很差
HBASE与RDBMS和NoSQL的区别:
Hbase的表数据存储在HDFS文件系统中,因此,hbase具有以下特性:
1.存储容量可以线性扩展
2.数据存储的安全性可靠性极高
3.hbase的表模型跟mysql之类的关系型数据库差别巨大
4.hbase的表模型中没有字段的概念,但有行的概念
5.行中存的都是key-value对,每行中key-value对中的key可以各种各样,每行中的key-value对的数量也可以各种各样
Hbase表模型特点:
1.一个表名,有表名
2.一个表可以分为多个列簇(不同的列簇的数据会存储在不同的文件中),列簇一般1-2个即可,最好一个
3.表中的每一行有一个“行键“rowkey”,而且行键在表中不能重复
4.表中的每一对key数据称为一个cell
5.hbase可以对数据存储多个历史版本
6.整张表由于数据量过大,会被横向切分成若干格region(用rowkey范围标识),不同的region的数据也存储在不同文件中
7.hbase会对插入的数据按顺序存储
7.1要点1:首先会按行键排序
7.2要点2:同一行里面的kv会按列簇排序,再按k排序
8.hbase类似于:Redis,mongoDB,canssdra,clickhouse等
Hbase里存储的数据:
hbase中只支持byte[]
此处的byte[]包括了:rowkey,key,value,列簇名,表名
hbase基于Hadoop:hbase的存储依赖于hdfs
hbase表结构:
1.数据在rowkey上按照字典排序的,如果rowkey相同,就按照列簇进行字典顺序排序,如果列簇也相同,那就按照列名的字典顺序排序
2.我们一般都是通过列簇采取数据,因为这种效率最高,通过字段也可以查,不过效率就低下很多
3.hbase会自动维护rowkey,相当于主键,base_info和extra_info叫做列簇,每一个列簇里面保存的数据可以不同,每一个键值对称为ceil
Hbase的组件:
Client:hbase的客户端,1.包含访问hbase的接口,比如linux shell,java api。2.除此之外,它会维护缓存来加速访问hbase的速度,比如region的位置信息。
Zookeeper:1.监控Hmaster的状态,保证有且仅有一个活跃的Hmaster达到Hmater达到高可用。2.它可以存储所有region的寻址入口。如:root表在哪一台服务器上。3.实时监控HregionServer的状态,感知HRegionServer的上下限信息,并通知给Hmaster。4存储hbase的元数据
HMater:1.为HRegionServer分配Region(新建表等)。2.负责HRegionServer的负载均衡。3,负责Region的重新分配(HRegionServer宕机之后的Region分配,HRegion裂变:当Region过大之后的差分。4,HDFS上的垃圾回收。5.处理schema的更新请求)
HRegionServer:1.维护HMaster分配给Region(管理本机的Region)。2,处理client对这些region的读写请求,并与HDFS进行交互,3,负责切分在运行过程中组件变大的Region
HLog:1.对HBase的操作进行记录,使用WAL写数据,优先写入log(put操作:先写日志再写memstore,这样可以防止数据丢失,即使丢失数据也可以回滚)
HRegion:1.Hbase中分布式存储和负载均衡的最小单元,他是表或者表的一部分
Store:1.相当于一个列簇
MemStore:1.内存缓冲区,用于将数据批量刷新到hdfs中,默认大小是128M
HStoreFile:1.不过是一个逻辑概念,HBase中数据是以HFile存储在hdfs上
总结:
rowkey:行键,和mysql的行键同理,不允许重复
columnfamily:列簇,列的集合之意
column:列
timestamp:时间戳,默认显示最新的时间戳,可用于控制k对应的多个版本值,默认查看最新的数据
version:版本号,表示记录数据的版本
cell:单元格,kv就是cell
模式:无
数据类型:只存储byte[]
多版本:每个值都可以有多个
列式存储:一个列簇存储到一个目录
稀疏存储:如果一个kv为null,不占用存储空间
HBase Shell操作:
1.NameSpace操作
list_namespace ---->查询所有的命名空间
list_namespace_tables 'hbase' ---->查询指定命名空间的表
create_namespace 'ns1' ----->创建指定空间的命名文件
describe_namespace 'ns1' ----->查询指定空间的结构
alter_namespace 'ns1',{METHOD=>'set','name'=>'lixi'} ---->修改命名空间的结构
drop_namespace 'ns1' ----->删除命名空间
create 'ns1:t1','f1','f2' ---->利用新添加的命名空间建表
2.DDL:
1.建表:
create 'user_info','bash_info','extra_info' ---->此表会建在default命名空间中
create 'first:user_info',{NAME=>'bash_info',BLOOMFILTER=>'ROWCOL',VERSIONS=>'3'} --->此表会建在指定的命名空间中,其中bloomfilter是布隆过滤器
2.查询表结构:
describe 'first:user_info'
3.创建表分片:
create 'first:user_info','f1',SPLITS=>['10','20','30','40']
4.添加列簇信息
alter 'first:user_info',{NAME=>'lixi_info'}
5.删除列簇
alter 'first:user_info','delete'=>'lixi_info'
describe 'first:user_info'
6.删除表(先要disable表,再删除表)
disable 'first:user_info'
drop 'first:user_info'
补充:
/*
NAME:列簇名(在Java API中,创建列簇时就指定了列簇名,所以在设置属性时无需再设置)
BLOOMFILTER:布隆过滤器的类型(NONE,ROW,ROWCOL)
VERSIONS:数量
IN_MEMORY:激进缓存(默认是false)
KEEP_DELETE_CELLS:保留删除的单元格(默认是false)
COMPRESSION:压缩
DATA_BLOCK_ENCODING:数据块编码
TTL:存活时间
MIN_VERSIONS:最小版本数量
BLOCKCACHE:块缓存,负责读
BLOCKSIZE:数据块大小
REPLICATION_SCOPE:复制范围
*/
3.DML:
1.插入数据(put命令,不能一次插入多条)
put 'user_info','001','base_inof:username','lixi'
2.scan扫描
scan 'user_info'
3.通过指定的版本号进行查询
scan 'user_info',{RAW=>true,VERSIONS=>1}
scan 'user_info',{RAW=>true,VERSIONS=>2}
4.查询指定列的数据
scan 'user_info' ,{COLUMNS=>'base_info:name'}
5.分页查询
scan 'user_info',{COLUMNS=>['base_info:name','base_info:age'],LIMIT =>10,STARTROW=>'001'}
6.get查询
get 'user_info','001','base_info:username'
get 'user_info','001'
7.根据时间戳查询
get 'user_info','001',{TIMERANGE=>[15755874669924,56884496863]}
8.hbase一个重要特性:排序特性(rowkey)
1.插入到hbase中的数据,hbase会自动排序存储,排序规则:首先看rowkey,然后看列簇名,然后看key名,均是按照字典排序
9.更新数据
put 'user_info','001','base_info:name','rock'
10.incr(新增数据)
incr 'user_info','002','base_info:age3'
11.删除一个key-value数据
delete 'user_info','001','bas2_info:love'
12.删除指定的版本
delete 'user_info','001','extra_info:feature',TIMESTAMP=>981228442214556
13.判断表是否存在
exists 'user_info'
14.表生效和失效
enable 'user_info'
disable 'user_info'
15统计表行数
count 'user_info'
16清空表数据
truncate 'user_info'
Hbase中的Filter:
过滤器:
单值过滤器:SingleColumnValueFilter
过滤器链:
行过滤器:RowFilter
列过滤器:QualifierFilter
比较器:
二进制比较器:BinaryComparator/BinaryPrefixComparator
字符串比较器:SubStringComparator
正则比较器:RexgexStringComparator
备注:能不用过滤器就尽量不要用过滤器,因为性能肯定没有直接使用get快
BloomFilter:
由来:
BloomFilter是由Howard Bloom在1970年提出来的二进制向量数据结构,它具有良好的空间和事件效率,被用来检测一个元素是不是集合中的一个成员,如果检测结果是,那么该元素不一定在集合中,但是如果检测结果为否,该元素一定不在集合中,因此BloomFilter具有100%的召回率,这样每个检测结果请求返回有“在集合中(可能错误)”和“不在集合(绝对正确)”这两种情况,可见BloomFilter是牺牲了正确率来节省空间
原理:
将集合中的元素通过hash函数运算后,将映射到BloomFilter的超大位数组中的某些点,并将这些点的值由0改成1,再将欲比较的元素也通过hash函数运算后映射成数组上的点,如果这几个的点位置的元素全是1,那这个点就有可能在集合中,但凡出现0,那么就一定不在集合中
BloomFilter在Hbase中的应用:
布隆过滤器是hbase中的高级功能,它能够减少特定访问模式(get/scan)下的查询时间,不过由于这种模式增加了内存和存储的负担,所以被默认为关闭的状态,Hbase支持以下类型的BloomFilter:
None:不使用布隆过滤器
Row:行键使用布隆过滤器
RowCol:列键使用布隆过滤器
其中RowCol是粒度更细的模式
Hbase使用BloomFilter作用:
当我们随机读get数据的时候,如果采用的是hbase的块索引机制,hbase会加载很多块文件,如果采用布隆过滤器后,它能够准确的判断该Hfile的所有数据块中,是否含有我们查询的数据,从而大大减少不必要的块加载,从而增加hbase集群的吞吐率,这里有几点细节:
1.布隆过滤器的存储在哪里?
对于hbase而言,我们选择采用布隆过滤器之后,Hbase会在生成storefile(hfile)时包含一份布隆过滤器结构的数据,称其为MetaBlock:MetaBlock与DataBlock(真实的keyvalue数据),一起由LRUBlockCache维护,所以开启bloomfilter会有一定的存储以及内存cache开销,但是在大多数情况下,这些负担相较于布隆过滤器带来的好处是可以接受的
2.采用BloomFilter后,hbase如何get数据?
在读取数据时,hbase会首先在布隆过滤器中查询,根据布隆过滤器的结果,再在MemStore中查询,最后再在相应的Hfile中查询
3.采用Row还是Rowcol布隆过滤器?
这取决于用户使用模式,如果用户只是做行扫描,使用更细粒度的行加列布隆过滤器不会有任何的帮助,这种场景就适合用Row,当用户不能批量更新特定的一行,并且最后的使用存储文件都含有改行的一部分时,行加列级的可以使用rowcol更好
Hbase整合MR:
package com.qf.summary.MR;
import com.qf.summary.Utils.HBaseUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.TableDescriptors;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import javax.management.ImmutableDescriptor;
import java.io.IOException;
/*
Hbase整合MR
*/
public class HbaseWithMR {
//驱动程序
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
String tableName = "second:user_info";
//1.获取admin(用于建表)
Admin admin = HBaseUtils.getAdmin();
//2.获取Table(用于插入数据)
Table table = HBaseUtils.getTable(tableName);
//3.判断输出的表是否存在,如果不存在,创建
if (!admin.tableExists(TableName.valueOf(tableName))){
//表不存在,就创建表
//表描述器
HTableDescriptor descriptors = new HTableDescriptor(TableName.valueOf(tableName));
//列簇描述器
HColumnDescriptor columnDescriptor = new HColumnDescriptor("base_info");
descriptors.addFamily(columnDescriptor);
//提交创建
admin.createTable(descriptors);
}
//4.获取job实例
Configuration configuration = new Configuration();
configuration.set("fs.defaultFS","hfds://qianfeng01:8020");
configuration.set("hbase.zookeeper.quorum","qianfeng01:2181,qianfeng02:2181,qianfeng03:2181");
Job job = Job.getInstance(configuration);
job.setJarByClass(HbaseWithMR.class);
//5.初始化Map
job.setMapperClass(Map.class);
job.setMapOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
FileInputFormat.addInputPath(job,new Path(""));
//6.初始化Reduce
TableMapReduceUtil.initTableReducerJob(tableName,Reduce.class,job);
//7.提交job
boolean b = job.waitForCompletion(true);
}
static class Map extends Mapper<LongWritable, Text,Text,Text>{
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
/*
数据处理逻辑
*/
}
}
static class Reduce extends TableReducer<Text,Text, ImmutableBytesWritable>{
@Override
protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
/*
数据处理逻辑
*/
//构建Put,准备将处理好的数据传入Hbase
Put put = new Put(Bytes.toBytes(""));
//为put添加数据
put.addImmutable(Bytes.toBytes(""),Bytes.toBytes(""),Bytes.toBytes(" "));
//将数据输出
context.write(new ImmutableBytesWritable(),put);
}
}
}
Hbase整合Hive:
1.在hive中创建hbase能够看到的表:
2.在hive中创建映射到hbase的表:
HBase的协处理器:
简介:协处理器允许用户在region服务器上运行自己的代码,允许用户执行region级别的操作,并且可以与RDBMS中触发器(trigger)类似的功能,在客户端,用户不用关心操作具体在哪里执行,Hbase的分布式框架会帮助用户把这些工作变得透明。
Hbase协处理器组件分为:observer,endpoint
observer:
这一类协处理器与触发器(tigger)类似:回调函数(也称为钩子函数,hook)在一些特定事件发生时被执行,这些事件包括一些用户产生的事件,也包括服务器端内部自动产生的事件
协处理器框架提供的接口如下:
RegionObserver:用户可以用这样的处理器处理数据修改事件,他们与表的region联系紧密
MasterObserver:可以被用作管理或DDL类型的操作,这些是集群级事件
WALObserver:提供控制WAL的钩子函数
Observer:提供了一些设计好的回调函数,每个操作在集群服务器段都可以被调用
endpoint:
除了事件处理之外还需要将用户自定义操作添加到服务器端,用户代码可以被部署到管理数据的服务器端,例如,做一些服务器端设计的工作
Endpoint通过添加一下远程过程调用来动态扩展RPC协议 ,可以将它们理解为与RDBMS中类似的处理过程,Endpoint可以与observer的实现组合起来直接作用于服务器端的状态
HBase的rowkey设计原则:
1.长度原则:
rowkey是一个二进制码流,rowkey的长度被很多开发者建议设计成10-100个字节,不过建议是越短越好,不要超过16个字节,原因如下:
1.1 数据的持久化文件HFile中是按照key-value存储的,如果rowkey过长比如100个字节,1000万列数据光rowkey及要占用100*1000万=10亿个字节,将近1G的数据,这会极大影响HFile的存储效率
1.2 Memstore将缓存部分数据到内存,如果rowkey字段过长,内存的利用效率会降低,系统将无法缓存更多的数据,这会降低检索效率,因此rowkey的字节长度越短越好
1.3 目前的操作系统是64位的,内存8字节对齐,控制在16个字节,8字节的整数倍将会最好的利用操作系统的性能
2.散列原则;
如果rowkey是按照时间戳的方式递增,不要将时间放在二进制码的前面,建议将rowkey的高位作为散列字段,由程序循环生成,低位放时间段,这样提高数据均衡分布在每个RegionServer实现负载均衡的几率,如果没有散列字段,那么首字段直接是时间信息将产生所有新数据都在一个RegionServer上堆积的热点现象,这样在做数据检索的时候负载将会集中在个别RegionServer,降低查询效率
3.唯一原则:
必须要在设计上保证其唯一性
rowkey设计技巧:
一条数据的唯一标识就是rowkey,那么这条数据存储在哪个分区,取决于rowkey处于哪一个预分区的区间内,设计rowkey的主要目的,就是让数据均匀地分布在所有的region上,在一定程度上可以防止数据倾斜,常用的设计方案:
1.字符串反转:20191124000001转成10000042119102
2.字符串拼接:20191124000001_lixi
3.生成随机数,hash,散列值;原本rowKey为1001的,SHA1后变成:dd01903921ea24941c26a48f2cec24e0bb0e8cc7
Hbase的寻址:
说明:
既然读写都在RegionServer上发生,我们前面有讲到, 每个RegionSever为一定数量的Region服务,那么Client要对某一行数据做读写的时候如何能知道具体要去访问哪个RegionServer呢?,这就是我们接下来要讨论的问题
老的寻址方式:
第一步:Clinet请求Zookeeper获得-ROOT-所在的RegionServer地址
第二步:Clinet请求-ROOT-所在的RS地址,获取.META.表的地址,Client会将-root-表的相关信息 cache下来,以便下一次快速访问
第三步:Client请求.META.表的RegionServer地址,获取访问数据所在的RegionServer的地址,Client会将.META.表的信息cache下来,以便下一次快速访问
第四步:Client请求访问数据所在的RegionServer的地址,获取相应的数据
从上述的路径我们不难看出:用户需要3次请求才能直到用户Table,这在一定程度上带来了性能的降低,所以在0.96版本之后,hbase就嫁给-root-表去掉了
新的寻址方式:
访问路径变成了3步:
第一步:Client请求Zookeeper获取.META.表所在的RegionServer的地址
第二步:Client请求.META.所在的RegionServer获取访问数据所在的RegionServer地址,Client会将.META.的相关信息cache存储下来,以便下一次快速访问
第三步:Client请求数据所在的RegionServer,获取所需要的数据
总结:去掉-ROOT-的原因主要有以下两点原因:
其一:提高性能
其二:2层架构已经足以满足集群的需求
这里还有一个问题需要说明,那就是Client会缓存.META.的数据,用来加速访问,既然有缓存,那么它什么时候更新呢?如果.META.更新了,比如Region1不在RegionServer2上,被转移到了RegionServer3上,Client的缓存没有更新会出现什么情况?
其实Client的元数据缓存不更新,当.META.的数据发生更新,如上面的例子,由于Region1的位置发生了变化,Client再次根据缓存去访问的时候,会出现错误,当出现异常达到重试次数后就会去.META.所在的RegionServer获取最新的数据,如果.META所在的RegionServer也变了,Client就会去Zookeeper上获取.META.所在RegionServer的最新地址
优化属性:
服务端:
1.hbase.regionserver.handler.count:rpc请求的线程数量,默认值是10,生产环境建议使用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主合并的间隔时间,默认为1天,建议设置为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:默认值2,如果一个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的region进行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.25,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"
由于我们服务器内存较大(96G),我们给一部分regionserver的jvm内存开到64G,到现在为止,还没有发生过一次full gc,hbase在内存使用控制方面确实下了不少功夫,比如各种blockcache的实现
客户端:
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
7.限定扫描范围:指定列簇或者指定要查询的列,指定startRow和endRow
8.使用Filter可大量减少网络消耗
9.通过Java多线程入库和查询,并控制超时时间。后面会共享下我的hbase单机多线程入库的代码
10.建表注意事项:
开启压缩
合理的设计rowkey
进行预分区
开启bloomfilter
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
其余设置:
columnFamily,column,rowkey均会存储到Hfile中,因此这几项在设计表结构的时候都尽量设计短一些
regionserver的region数量不要过1000,过多的region会导致产生很多的memstore,可能会导致内存溢出,也会增加major compact的耗时
内存优化:
Hbase操作过程中需要大量的内存开销,毕竟Table是可以缓存到内存中的,一般会分配整个可用内存的70%给Hbase的java堆,但是不建议分配非常大的堆内存,因为GC过程持续太久会导致RegionServer处于长期不可用状态,一般16-48G内存就可以,如果因为框架占用内存过高导致系统内存不足,框架一样会被系统服务拖死
1.设置在HDFS中追加内容
- hdfs-site.xml、hbase-site.xml
<property>
<name>dfs.support.append</name>
<value>true</value>
<description>开启HDFS追加同步,可以优秀的配合HBase的数据同步和持久化。默认值为true</description>
</property>
2 dataNode允许的最大文件打开数
- hdfs-site.xml
<property>
<name>dfs.datanode.max.transfer.threads</name>
<value>4096</value>
<description>HBase一般都会同一时间操作大量的文件,根据集群的数量和规模以及数据动作,设置为4096或者更高。默认值:4096</description>
</property>
3 设置延迟高的数据操作的等待时间
- hdfs-site.xml
<property>
<name>dfs.image.transfer.timeout</name>
<value>60000</value>
<description>如果对于某一次数据操作来讲,延迟非常高,socket需要等待更长的时间,建议把该值设置为更大的值(默认60000毫秒),以确保socket不会被timeout掉。</description>
</property>
4 优化数据的写入效率
- mapred-site.xml
<property>
<name>mapreduce.map.output.compress</name>
<value>true</value>
<description>开启这两个数据可以大大提高文件的写入效率,减少写入时间。</description>
</property>
<property>
<name>mapreduce.map.output.compress.codec</name>
<value>org.apache.hadoop.io.compress.GzipCodec</value>
<description>开启这两个数据可以大大提高文件的写入效率,减少写入时间。org.apache.hadoop.io.compress.GzipCodec或者其他压缩方式</description>
</property>
5 设置RPC监听数量
- hbase-site.xml
<property>
<name>hbase.regionserver.handler.count</name>
<value>30</value>
<description>默认值为30,用于指定RPC监听的数量,可以根据客户端的请求数进行调整,读写请求较多时,增加此值。</description>
</property>
6 设置HStore文件大小
- hbase-site.xml
<property>
<name>hbase.hregion.max.filesize</name>
<value>10737418240</value>
<description>默认值10737418240(10GB),如果需要运行HBase的MR任务,可以减小此值,因为一个region对应一个map任务,如果单个region过大,会导致map任务执行时间过长。该值的意思就是,如果HFile的大小达到这个数值,则这个region会被切分为两个Hfile。</description>
</property>
7 设置hbase客户端缓存
- hbase-site.xml
<property>
<name>hbase.client.write.buffer</name>
<value>4096</value>
<description>用于指定HBase客户端缓存,增大该值可以减少RPC调用次数,但是会消耗更多内存,反之则反之。一般我们需要设定一定的缓存大小,以达到减少RPC次数的目的。</description>
</property>
8 指定scan.next扫描HBase所获取的行数
- hbase-site.xml
<property>
<name>hbase.client.scanner.caching</name>
<value>4096</value>
<description>用于指定scan.next方法获取的默认行数,值越大,消耗内存越大。</description>
</property>
9 flush、compact、split机制
大的Storefile文件。split则是当Region达到阈值,会把过大的Region一分为二。
涉及属性:
1. 128M就是Memstore的默认阈值
hbase.hregion.memstore.flush.size:134217728
tip:
这个参数的作用是当单个HRegion内所有的Memstore大小总和超过指定值时,flush该HRegion的所有memstore。RegionServer的flush是通过将请求添加一个队列,模拟生产消费模型来异步处理的。那这里就有一个问题,当队列来不及消费,产生大量积压请求时,可能会导致内存陡增,最坏的情况是触发OOM。
2.
hbase.regionserver.global.memstore.upperLimit:0.4
hbase.regionserver.global.memstore.lowerLimit:0.38
tip:
当MemStore使用内存总量达到hbase.regionserver.global.memstore.upperLimit指定值时,将会有多个MemStores flush到文件中,MemStore flush 顺序是按照大小降序执行的,直到刷新到MemStore使用内存略小于lowerLimit