1. HBase存储中的3个核心机制
1.flush机制:
当MemStore达到阈值之后,会flush成一个StoreFile (也就是内存中的数据写入了磁盘)。
2.compact机制:
当StoreFile达到阈值时,合并StoreFile。合并过程中cell版本合并和数据删除。
3.split机制:
当region不断增大,达到阈值,region会分成两个新的region。
2.HBase Java API使用
1. 为HBase Java编程准备开发环境
使用java进行HBase编程,pom文件需要添加2个依赖,分别是hbase-client 、 hbase-server(可以没有)、hadoop-client。
查看HBase的API文档,以下几个类是比较常用的,需要熟悉:
HBaseAdmin (管理操作、创建表等)
HTable (操作表、上传数据)
Configuration
HBase的java API开发与HBase shell的命令基本上是一致的。例如shell中的put操作,对应着API中的Put对象。
2. 添加IDE依赖的jar包:
HBase的开发,需要在pom.xml中引入 hbase-client、hbase-server和hadoop-client几个依赖包(选取的版本是:hadoop: 2.5.0 ; hbase:0.98.6-hadoop2),如下是pom.xml定义:
<properties>
<hadoop.version>2.5.0</hadoop.version>
<hbase.version>0.98.6-hadoop2</hbase.version>
</properties>
<dependencies>
<!-- hbase client -->
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>${hbase.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>${hbase.version}</version>
</dependency>
<!-- hadoop client -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
</dependencies>
3. 添加配置文件:
需要将HBase和Hadoop的配置文件,添加到项目的Classpath。新建“Source Folder”:src/main/resources,将hadoop和hbase安装目录下的配置文件挪到这个目录下(主要是core-site.xml、hdfs-site.xml和hbase-site.xml):
4. 获取HBase配置环境信息:
在使用Java API时,Client端需要知道HBase的环境配置,使用Configuration类来封装该信息。
Configuration conf = HBaseConfiguration.create();
在创建该对象时,首先在classpath中寻找hbase-site.xml文件来读取配置,如果不存在该文件,则寻找hbase-default.xml文件来读取配置(也就是默认配置)。
5. Get获取数据:
使用Get对象获取数据:
// 抽象的获取HTable对象的静态方法,返回HTable对象
public static HTable getTable (String tablename) throws IOException {
Configuration conf = HBaseConfiguration.create();
HTable table = new HTable(conf,tablename);
return table;
}
//根据row_key返回这条记录中的信息
public static void getByRowKey(String tablename,String rowkey)
throws IOException
{
HTable table = HBaseAPIOper.getTable(tablename);
//Get Object is bound to one row(identified by rowkey)
Get get = new Get(Bytes.toBytes(rowkey));
Result rs = table.get(get);
Cell[] cells = rs.rawCells();
for (Cell cell : cells){
System.out.print( Bytes.toString(CellUtil.cloneFamily(cell)) + ":" );
System.out.print( Bytes.toString(CellUtil.cloneQualifier(cell)) + "=>" );
System.out.print( Bytes.toString(CellUtil.cloneValue(cell)) + "\n" );
}
table.close();
}
//返回某个row_key、某个列族、某个列的数据(只返回指定列的信息)
public static void getByColumns (String tablename,String rowkey,
String family,String qualifier) throws IOException{
HTable table = HBaseAPIOper.getTable(tablename);
//Get Object is bound to one row(identified by rowkey)
Get get = new Get(Bytes.toBytes(rowkey));
get.addColumn(Bytes.toBytes(family), Bytes.toBytes(qualifier));
Result rs = table.get(get);
Cell[] cells = rs.rawCells();
for (Cell cell : cells){
System.out.print( Bytes.toString(CellUtil.cloneFamily(cell)) + ":" );
System.out.print( Bytes.toString(CellUtil.cloneQualifier(cell)) + "=>" );
System.out.print( Bytes.toString(CellUtil.cloneValue(cell)) + "\n" );
}
table.close();
}
Get get = new Get(rowkey的字节数组)。 Get对象和rowkey进行绑定。Result result = HTable对象.get(Get对象)。下面遍历结果集,使用rawCells()方法,获取所有查到的单元格信息。CellUtil类,是用于操作单元格的工具类,CellUtil.cloneFamily()获取列族信息,CellUtil.cloneQualifier()获取列信息,CellUtil.cloneValue()获取对应的值。为了防止资源使用崩溃,一定要记得关闭table对象。
同时,也可以通过操作Get对象,来限定只显示部分列。get.addColumn("列族","列名")
6. Put插入数据:
实体信息的插入,通过put操作完成。
// Put rows by rowkey (Auto Generated!!)
public static void putRows(String tablename) throws IOException{
HTable table = HBaseAPIOper.getTable(tablename);
long rowkey = System.currentTimeMillis();
// The Integer rowkey must be cast to String.
Put put0 = new Put(Bytes.toBytes(Long.toString(rowkey)));
put0.add(
Bytes.toBytes("info"),
Bytes.toBytes("name"),
Bytes.toBytes("natty")
) ;
put0.add(
Bytes.toBytes("info"),
Bytes.toBytes("age"),
Bytes.toBytes("31")
) ;
// The Integer rowkey must be cast to String.
Put put1 = new Put(Bytes.toBytes(Long.toString(rowkey + 1)));
put1.add(
Bytes.toBytes("info"),
Bytes.toBytes("name"),
Bytes.toBytes("natty")
) ;
put1.add(
Bytes.toBytes("info"),
Bytes.toBytes("age"),
Bytes.toBytes("31")
) ;
ArrayList<Put> list = new ArrayList<Put>();
list.add(put0);
list.add(put1);
table.put(list);
table.close();
}
put操作对应于Put对象。创建Put对象, Put put = new Put(rowkey对应的字节数组);之后添加列信息,put.add("列族","列名","列值"),通过多次put.add()可以一次添加多个列值信息。将列信息添加到表格,表格对象.put(Put对象),或者添加多个put对象,表格对象.put(含有多个Put对象的list集合)。
7. Delete删除数据和Update数据:
删除数据需要使用Delete对象, Delete delete = new Delete(行键对应的字节数组)。指定要删除的列(列族),delete.deleteColumn("列族","列名")
public static void deleteByRowkey(String tablename,String rowkey)
throws IOException{
HTable table = HBaseAPIOper.getTable(tablename);
Delete del = new Delete(Bytes.toBytes(rowkey));
//delete all the family
del.deleteFamily(Bytes.toBytes("info"));
//delete just one column
del.deleteColumn(Bytes.toBytes("info"), Bytes.toBytes("name"));
table.delete(del);
table.close();
}
HBase中Update操作也使用Put对象完成,操作方法与插入操作完全一致。
8. Scan操作:
ResultScanner是由查询结果Result对象组成的,可以通过2次遍历获取数据。getRow()获得行键。另外,也可以使用IOUtils.closeStream()来关闭table对象等资源。通过setStartRow()和setStopRow()方法,限定scan的查询范围。通过scan.addFamily()或者scan.addColumn()增加通过列族/列筛选数据。
public static void scanByTable(String tablename) throws IOException
{
HTable table = HBaseAPIOper.getTable(tablename);
Scan scan = new Scan();
//make the scope of scan.
// >= setStartRow() AND < setStopRow()
scan.setStartRow(Bytes.toBytes("10001"));
scan.setStopRow(Bytes.toBytes("10004"));
//filter the data through Family or Column
scan.addFamily(Bytes.toBytes("info"));
scan.addColumn(Bytes.toBytesBinary("info"), Bytes.toBytes("name"));
ResultScanner resultscanner = table.getScanner(scan);
for (Result result:resultscanner)
{
// getRow() method can get the rowkey.
System.out.println(Bytes.toString(result.getRow()));
Cell[] cells = result.rawCells();
for (Cell cell : cells){
System.out.print( Bytes.toString(CellUtil.cloneFamily(cell)) + ":" );
System.out.print( Bytes.toString(CellUtil.cloneQualifier(cell)) + "=>" );
System.out.print( Bytes.toString(CellUtil.cloneValue(cell)) + "\n" );
}
}
IOUtils.closeStream(resultscanner);
IOUtils.closeStream(table);
}
3.HBase架构深入总结
1.客户端作用:
1.整个HBase集群的访问入口;
2.使用HBase RPC机制与HMaster 和 HRegionServer通信。
3.与HMaser通信进行管理类操作。
4.与HRegionServer通信进行数据读写类操作。
2.Zookeeper作用:
1.实现HMaster的HA,保证集群中只有一个HMaster。
2.保存root region的位置(meta表),Region的寻址入口。
客户端在访问表时,需要找到表的每个region对应的RegionServer(管理Region的角色)。Root(新版本是meta)表存储了Region的分布情况以及每个Region的详细信息。
3.实时监控RegionServer的上下线信息,并及时通知Master。
4.存储HBase的schema和table元数据(有哪些表、每个表有哪些列族)。
3.HMaster作用和特点:
1.HMaster作用:
(1). HMaster负责Table和Region的管理工作,为RegionServer分配Region。
(2). 管理Region Server的负载均衡(为每一个Region Server分配多少Region),调整Region分布。
(3). 当某一个Region Server停机下线后,负责失效Region Server上的Region的迁移。
(4). 监听zookeeper,基于zookeeper来得知Region Server上下线。
(5). 监听zookeeper,基于zookeeper来保证Master的HA
2.HMaster的特点:
(1)HMaster没有单点故障问题(SPOF:Single Point Of Failure)。HBase可以启动多个HMaster,Zookeeper保证只有一个HMaster运行。
启动backup master节点方法:在conf下修改配置文件backup-masters,通过hbase-daemons.sh脚本启动backup master。
$bin/hbase-daemons.sh start master-backup
(2)不参与客户端数据读写
(3)HMaster的负载很低(在同一个主机上可以和NN、SNN等服务共同运行)。
4.Region Server的作用:
- 维护Region(HMaster分配的),响应客户端的IO访问请求(读写),向HDFS读写数据。
- 负责处理region的 flush、compact、split 几个过程。
- 维护region的cache
4.HBase数据检索三种方式
HBase的数据检索,主要提供了3种方式,分别是:Get方式检索、Scan范围检索、Scan全表扫描。
- Get方式:
Get需要使用rowkey作为参数检索。
在hbase shell中使用get命令
在java API中使用Get对象。 - Scan 范围搜索:
依据rowkey匹配查找,默认根据rowkey匹配
在hbase shell中使用scan命令
在java API中使用Scan对象。 - Scan 全部扫描:
全表扫描
和MapReduce/Spark一起使用。
5.HBase与MapReduce集成
1.HBase和MapReduce的整合场景:
第一种应用场景: 文本文档txt或者csv文件导入到HBase中。
第二种应用场景: HBase的表导成另外一个表。
Data Source(数据源): 。本地、HBASE表、HDFS
Data Sink: 数据写进哪个地方。
Data Source & Data Sink:
2.HBase和MapReduce的原因和方法:
- 为什么使用MapReduce导入:
MapReduce是并行处理框架,比较快。 - HBase和MapReduce整合的方法:
使用如下命令可以返回hbase的classpath的jar包:
$ bin/hbase mapredcp
查看命令的具体含义:
$ bin/hbase
3.HBase的默认MapReduce程序:
HBase默认集成了一些mapreduce程序。这些程序存放在这个jar包(HBase的安装目录下)中 :
/opt/modules/hbase-0.98.6-cdh5.3.6/lib/hbase-server-0.98.6-cdh5.3.6.jar
下面,我们执行这个jar包:
(1) 导入HBASE_CLASS path环境变量:
export HADOOP_HOME=/opt/modules/hadoop-2.5.0-cdh5.3.6
export HBASE_HOME=/opt/modules/hbase-0.98.6-cdh5.3.6
export HADOOP_CLASSPATH=`$HBASE_HOME/bin/hbase mapredcp`
上边步骤设置HBase的CLASSPATH。变量引入,一般写进shell脚本,跟随job任务执行的命令一起。如果要写入/etc/profile 会永久生效,并且需要加export。
(2)通过yarn执行mapreduce的jar包:
查看整个jar包所提供的所有功能:
$ /opt/modules/hadoop-2.5.0-cdh5.3.6/bin/yarn jar /opt/modules/hbase-0.98.6-cdh5.3.6/lib/hbase-server-0.98.6-cdh5.3.6.jar
(3)通过mapreduce查看某个表的行数(rowkey的个数):
$ /opt/modules/hadoop-2.5.0-cdh5.3.6/bin/yarn jar /opt/modules/hbase-0.98.6-cdh5.3.6/lib/hbase-server-0.98.6-cdh5.3.6.jar rowcounter 'user'
4.自定义HBase的MapReduce程序:
5.HBase数据迁移工具importTsv:
HBase存在较多数据迁移的场景:例如,需要将一个数据文件直接导入到HBase表中,还有将生产集群的数据迁移到测试集群等等。
ImportTsv是HBase官方提供的基于MapReduce的批量数据导入工具。ImportTsv是一个命令行工具,可以将存储在HDFS上的自定义分隔符(默认\t)的数据文件,通过一条命令导入到HBase表中,非常适合大数据量导入。
下面运行一个实例来演示导入过程:
(1)创建一个将要导入的测试文件:
(2)上传该文件到HDFS上的指定目录:
(3)HBase建表:
(4)开始运行MapReduce执行导入。
(5)验证结果。
6. 使用BulkLoad加载数据:
6.创建HBase表
创建HBase有常见的三种方式:
1.创建表时,指定region划分方式(根据rowkey)
create 'tb1','fa1',SPLITS => ['10','20','30','40']
tb1表有五个Region。SPLITS根据rowkey来将记录分配到各自的region中:
2.创建表时,通过文件来将表划分为多分区(最常用方式):
将分区范围放在一个物理文件中,然后在创建表时指定这个文件。例如定义下面一个regions.txt文件:
20170605
20170606
20170607
20170608
20170609
20170610
下面创建表,根据这个文件指定划分region。
create table 'tb2','fa2',SPLITS_FILE => '/home/natty/hbase/regions.txt',OWNER => 'natty'
需要注意,这里建议使用绝对路径来指定文件。
3.指定分区的数量,创建表:
create table 'tb3','fa3',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
如果指定region的起止范围,系统可以自动定义。
7. HBase的RowKey设计
1.表结构设计:
最核心的内容是设计rowkey。设计rowkey的要点:
1.根据需求设计,查询速度快。
2.尽量覆盖更多业务场景,增加复用的可能性。
3.离线设计,避免热点
2.案例设计:
下面介绍一个实例,订单历史表。这个表有以下的查询场景。