可能有的图片显示不出来,如有需要,请留邮箱我发给你!
Hbase是一种分布式、可扩展、支持海量数据存储的NoSQL数据库
从物理结构上,Hbase包含了三种类型的server:zookeeper,HMaster,HRegion server,采用一种主从模式的结构。
而底层的存储,还是依赖于HDFS的。
HBase的tables根据rowkey的范围进行水平切分,切分成region后分配到各个region server。一个region包含一个表在start key和end key所有行。而用户都是跟region server进行读写交互。一个region建议大小在5~10G。可通过参数 hbase.hregion.max.filesize
去设置
一般也叫做HMaster ,HMaster主要职责包括三个方面:
Hbase使用zookeeper作为分布式协调服务,来维护集群内的server状态。(通过heartbeat维护那些server是存活并可用的)。负责HMaster的选举工作,通过创建临时节点,并监控状态,来实现HA。
1)HMaster的高可用
多个HMaster会去竞争zookeeper上的临时节点,而zookeeper会将第一个创建成功的HMaster作为当前active的HMaster,其他HMaster进入stand by的状态。这个active的HMaster会不断发送heartbeat给zk,其他stand by状态的HMaster节点会监听这个active HMaster的故障信息,一旦发现active HMaster宕机了,就会重新竞争新的active HMaster
2) HMaster监控HRegion Server
每个regeion server会创建一个临时节点。HMaster会监控这些节点来确认那些region server是可用的,如果region server没有发送heartbeat给zk,zk会删除这个临时节点,其他监听这会受到这个故障节点被删除的信息。比如HMaster会监控RS的消息,如果发现某个RS下线了,那么就会重新分配RS来恢复相应的region数据。
有一张特殊的HBase目录表,叫做META table,保存了集群中各个region的位置。zookeeper中保存了这样meta data的位置信息。(在0.96之前,zookeeper保存的是root table 里面存的是meta table的位置)
基本流程如下:
.meta. table的存储结构
一个region server运行在一个HDFS的data node上,并且拥有以下组件:
基本流程:
Memstore刷写时机:
当某个memstore的大小达到了hbase.hregion.memsotre.flush.size(默认128M)
时
当region server中 memstore的总大小达到
java_headpsize*hbase.regionserver.global.memstore.size(默认值0.4) *hbase.regionserver.global.memsotre.size.lower.limit(默认值0.5)
region会按照其所有memstore的大小顺序(由大到下)一次进行刷写。直到region server中所有memstore的总大小减小到上述值一下。
到达自动刷写时间,也会触发memstore。hbase.regionserver.optionalcacheflushinterval(1h)
当region server中的memsotre的总大小达到以下值时时,会阻止往所有的memstore写数据。
java_heapsize*hbase.regionserver.global.memstore.size(默认值0.4)
在HBase中,数据以有序kv的形式,存储在HFile中。当MemStore存储足够的数据,全部kv对被写入HFile存入HDFS。这里写文件时顺序写,避免了磁盘大量移动磁头的过程,比随机写高效很多。
HFile的逻辑结构主要包括6个部分
由于memstore每次刷写都会生成一个新的HFile,且同一个字段的不同版本(timestamp)和不同类型(put/delete)有可能会分布在不同的HFile中,因此查询时需要遍历所有的HFile。为了减少HFile的文件个数,清理过期数据,所以HBase提供了Compaction机制,将小文件合并成大文件,目的是提高查询性能。compaction分为两种 :Minor Compaction和major Compaction
hbase.server.thread.wakefrequency*hbase.server.compactchecker.interval.multiplier
默认情况下,每个Table起初只有一个Region,随着数据的不断写入,Region会自动进行拆分。刚拆分时,两个子Region 都位于当前的 RS,但处于负载均衡考虑,HMaster有可能会将某个region转移给其他的Region server
Region split时机:
hbase.hregion.max.filesize
(默认是10G),该Region就会进行拆分(0.94版本之前)实现方法
通过Merge类冷合并Region
Hase org.apache.hadoop.hbase.util.Merge region_name
通过online_mege 热合并Region
在线进行合并
online_merge传参是Region的hash值,而Region的hash值就是region名称最后那段在两个.之间的字符串部分
需要进入Hbase shell
merge_region 'c2212a3956b814a6f0d57a90983a8515','553dd4db667814cf2f050561167ca030'
1、显示HBase的表列表 list
2、创建表 create '表名','列族','列族' ..
3、查看表的信息: desc '表名'
4、往表里插入数据: put '表名','rowkey','cell...'
5、查询数据 : get '表名','rowkey'
6、查询数据 : scan '表名'
7、删除数据: delte '表名','rowkey'
8、修改表结构: alter
9、修改数据: hbase没有修改数据的显示操作,重复插入就相当于是修改数据
10、清空表 truncate '表名' ,首先要disable 表 启动表:enable table_name
11、删除表 drop table , 先要disable 表
12、过滤器操作:
get 'user', 'rk0001', {FILTER => "ValueFilter(=, 'binary:中国')"}
get 'user', 'rk0001', {FILTER => "(QualifierFilter(=,'substring:a'))"}
public class HbaseUtil {
public static Configuration conf = null ;
public static Connection connection = null ;
public static Admin admin = null ;
/*
* @desc 取得连接
* */
public static void setConf(String quorum,String port){
try {
conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum",quorum); //zookeeper地址 qyl01,qyl02,qyl03
conf.set("hbase.zookeeper.property.clientPort",port); //2181
connection = ConnectionFactory.createConnection(conf);
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* @desc 关闭连接
* */
public static void closeHBaseConnection(){
try {
if (connection != null) {
connection.close();
}
if(admin != null){
admin.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
/*
* @desc 创建表
* */
public static void createTable(String tableName,String columnFamily){
try {
TableName tbName = TableName.valueOf(tableName);
if(!admin.tableExists(tbName)){
HTableDescriptor hTableDescriptor = new HTableDescriptor(tbName);
HColumnDescriptor hColumnDescriptor = new HColumnDescriptor(columnFamily);
hTableDescriptor.addFamily(hColumnDescriptor);
admin.createTable(hTableDescriptor);
}else{
System.out.println(tableName+" exists !");
}
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* @desc 添加多条记录
* */
public static void addMoreCord(String tableName, String columnFamily, String qualifier, List<String> rowList, String value){
Table table = null ;
try{
table = connection.getTable(TableName.valueOf(tableName));
List<Put> puts = new ArrayList<Put>();
Put put = null;
for(int i = 0; i < rowList.size(); i++){
put = new Put(Bytes.toBytes(rowList.get(i)));
put.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(qualifier),Bytes.toBytes(value));
puts.add(put);
}
table.put(puts);
}catch (IOException e){
e.printStackTrace();
}finally {
if(table != null){
try {
table.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*
* @desc 查询rowkey下多列值,一起返回
* */
public static String[] getValue(String tableName,String rowKey,String family,String[] qualifier){
Table table = null;
try {
table = connection.getTable(TableName.valueOf(tableName));
Get get = new Get(rowKey.getBytes());
//返回指定列族、列名,避免rowKey下所有数据
get.addColumn(family.getBytes(), qualifier[0].getBytes());
get.addColumn(family.getBytes(), qualifier[1].getBytes());
Result rs = table.get(get);
// 返回最新版本的Cell对象
Cell cell = rs.getColumnLatestCell(family.getBytes(), qualifier[0].getBytes());
Cell cell1 = rs.getColumnLatestCell(family.getBytes(), qualifier[1].getBytes());
String[] value = new String[qualifier.length];
if (cell!=null) {
value[0] = Bytes.toString(CellUtil.cloneValue(cell));
value[1] = Bytes.toString(CellUtil.cloneValue(cell1));
}
return value;
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
if (table!=null){
try {
table.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*
* @desc 查询rowkey下某一列值
*/
public static String getValue(String tableName, String rowKey, String family, String qualifier) {
Table table = null;
try {
table = connection.getTable(TableName.valueOf(tableName));
Get get = new Get(rowKey.getBytes());
//返回指定列族、列名,避免rowKey下所有数据
get.addColumn(family.getBytes(), qualifier.getBytes());
Result rs = table.get(get);
Cell cell = rs.getColumnLatestCell(family.getBytes(), qualifier.getBytes());
String value = null;
if (cell!=null) {
value = Bytes.toString(CellUtil.cloneValue(cell));
}
return value;
} catch (IOException e) {
e.printStackTrace();
} finally {
if (table!=null){
try {
table.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
/*
* 全表扫描
* @param tableName
* @return
* @desc qualifier 指的是某列
*/
public static ResultScanner scan(String tableName,String family,String qualifier) {
Table table = null;
try {
table = connection.getTable(TableName.valueOf(tableName));
Scan scan = new Scan();
ResultScanner rs = table.getScanner(scan);
// 一般返回ResultScanner,遍历即可
// if (rs!=null){
// String row = null;
// String quali = null;
// String value = null;
// for (Result result : rs) {
// row = Bytes.toString(CellUtil.cloneRow(result.getColumnLatestCell(family.getBytes(), qualifier.getBytes())));
// quali =Bytes.toString(CellUtil.cloneQualifier(result.getColumnLatestCell(family.getBytes(), qualifier.getBytes())));
// value =Bytes.toString(CellUtil.cloneValue(result.getColumnLatestCell(family.getBytes(), qualifier.getBytes())));
// System.out.println(row+"-"+quali+"-"+value);
// }
// }
return rs;
} catch (IOException e) {
e.printStackTrace();
} finally {
if (table!=null){
try {
table.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
要完成一个过滤操作,至少需要两个参数,一个是抽象操作符,另一个就是具体的比较器了(Comparator)
抽象操作符
LESS <
LESS_OR_EQUAL <=
EQUAL =
NOT_EQUAL <>
GREATER_OR_EQUAL >=
GREATER >
NO_OP 排除所有
比较器(指定比较机制)
BinaryComparator 按字节索引顺序比较指定字节数组,采用 Bytes.compareTo(byte[])
SubstringComparator 判断提供的子串是否出现在 value 中
Filter rowFilter = new RowFilter(CompareOp.GREATER, new BinaryComparator("95007".getBytes()));
scan.setFilter(rowFilter);
1、pre-Creating-Regions(预分区)
默认情况下,在创建HBase表的时候会自动创建一个region分区,当导入数据的时候,所有的HBase客户端都向这一个region写数据,直到很大的时候才进行切分,提前创建分区,可以加快批量数据写入。(列如以手机号)
2、Row Key的设计
越小越好,rowkey的设计是要根据实际业务来 ,散列性(取反,用hash值对某个数取余)
3、column family
不要在一张表里定义太多的column family,建议2~3个,因为某个column family在flush的时候,它临近的column family 也会因关联效应被触发flush,最终导致系统产生更多个I/0。
4、compaction & Split
关闭 hbase.hregion.majorcompaction(默认7天),手动编写脚本避开业务高峰期进行major compaction
5、BlockCache
一个rs上的block和memstore大小不能超过heapsize * 0.8 ,对于注重读响应时间的系统,可以将blockcache设置大写 如 blockcache=0.4 memstore = 0.39 ,以加大缓存命中率(默认blockcache=0.2 memstore=0.4)
1、多个HTable并发写: 创建多个HTable客户端用于写操作,提高写数据的吞吐量
2、使用bulkload写入
3、Auto Flash: 调用HTable.setAutoFlushTo(false)
方法可以将HTable写客户端自动flush关闭,默认是开启的
4、Compression压缩:推荐使用snappy hcd.setCompressionType(Algorithm.SNAPPY)
1、批量读 : 通过调用HTable.get(List)方法可以根据一个指定的rowkey列表,批量获取多行记录,减少IO
2、缓存查询的结果:创建表的时候,可以通过HColumnDEscriptor.setInMemory(true)建表放在rs的缓存中。
简单来说 Bulkload 就是利用 HBase 的数据信息按照特定格式存储在 HDFS 内这一原理,直接在 HDFS 中生成持久化的 HFile 数据格式文件,然后上传至合适位置,即完成巨量数据快速入库的办法。
实现步骤:
代码
使用mapReduce生成HFile文件
public class IteblogBulkLoadMapper extends Mapper<LongWritable, Text, ImmutableBytesWritable, Put>{
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String line = value.toString();
String[] items = line.split("\t");
ImmutableBytesWritable rowKey = new ImmutableBytesWritable(items[0].getBytes());
Put put = new Put(Bytes.toBytes(items[0])); //ROWKEY
put.addColumn("f1".getBytes(), "url".getBytes(), items[1].getBytes());
put.addColumn("f1".getBytes(), "name".getBytes(), items[2].getBytes());
context.write(rowkey, put);
}
}
通过BlukLoad方式加载HFile文件
public class LoadIncrementalHFileToHBase {
public static void main(String[] args) throws Exception {
Configuration configuration = HBaseConfiguration.create();
HBaseConfiguration.addHbaseResources(configuration);
LoadIncrementalHFiles loder = new LoadIncrementalHFiles(configuration);
HTable hTable = new HTable(configuration, "blog_info");
loder.doBulkLoad(new Path("hdfs://iteblog:9000/user/iteblog/output"), hTable);
}
}
驱动程序
public class IteblogBulkLoadDriver {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
final String SRC_PATH= "hdfs://iteblog:9000/user/iteblog/input";
final String DESC_PATH= "hdfs://iteblog:9000/user/iteblog/output";
Configuration conf = HBaseConfiguration.create();
Job job=Job.getInstance(conf);
job.setJarByClass(IteblogBulkLoadDriver.class);
job.setMapperClass(IteblogBulkLoadMapper.class);
job.setMapOutputKeyClass(ImmutableBytesWritable.class);
job.setMapOutputValueClass(Put.class);
job.setOutputFormatClass(HFileOutputFormat2.class);
HTable table = new HTable(conf,"blog_info");
HFileOutputFormat2.configureIncrementalLoad(job,table,table.getRegionLocator());
FileInputFormat.addInputPath(job,new Path(SRC_PATH));
FileOutputFormat.setOutputPath(job,new Path(DESC_PATH));
System.exit(job.waitForCompletion(true)?0:1);
}
}
Hbase读写流程
rowkey设计
简述Hbase filter的实现原理是什么?
简述Hbase性能优化的思路。
Hbase和传统数据库的区别?
hbaes 的bulkload