一:Hbase读写流程
- 读流程
⑴client访问Zookeeper中,找到ROOT表的Region所在的RegionServer信息;
⑵client连接RegionServer访问ROOT表查询.meta表的region位置信息
⑶再去连接.meta表的region所在的regionserver然后访问meta表,找到目标数据在哪个region上
及region所在的regionserver位置信息
⑷然后去访问目标数据所在的regionserver中的region,先在memstore中查询数据,memstore中不存在则在
BlockCache中读数据,
BlockCache中还是不存在的话就最后在storefile中读数据,并且将读取到的数据先写入到BlockCache中,然后再返回给客户端
- 写流程
⑴Client访问Zookeeper集群,查询ROOT表region所在的regionserver地址信息,比如rs1
⑵client连接rs1,访问ROOT表,根据写入信息查询.meta表的region位于哪些regionserver上,将得到的结果返回给client;
⑶client去连接相应的rs,访问.meta表,根据写入的namespace、表名和rowkey找到对应的region信息
⑷client连接最终的rs,为了持久化和恢复,将数据先写到Hlog(write ahead log)中;
⑸再将数据写入到memstore中,当memstore达到预设阈值后,就会创建一个新的memstore,而老的memstore就会加入flush队
列,由单独的线程flush到磁盘上,形成一个storefile;
⑹与此同时,系统会在Zookeeper记录一个checkpoint,表示这个时刻之前的数据变更已经持久化了,当系统出现意外可能导致
memstore中的数据丢失,就可以通过hlog来恢复checkpoint之后的数据;
⑺每次flush就会形成一个storefile文件,而storefile文件是只读的,一旦创建之后就不可修改,因此hbase的更新就是不断追加的 操作;
⑻随着storefile的数量不断增多,当达到设定阈值后就会触发compact合并操作,将多个storefile合并成一个大的storefile,同时进 行版本合并和数据删除;
⑼当store中的单个storefile文件的大小超过阈值的时候,触发split操作,regionserver把当前的region split成2个新的region
⑽父region就会下线,新split出的2个region就会被hmaster分配到两个regionserver上,实现负载均衡,使得原先一个region的压
力分流到两个region上。
二:Hbase中.meta简介
- 简介
当我们在对HBase的读写操作时,都需要提前知道我们需要操作的region的所在位置,即是存在于哪个HRegionServer上,
因此在HBase中存在一张表元数据表.meta表(属于Hbase的内置表)专门存储了表的元数据信息,以及region位于哪个
regionserver上。.meta表的结构类似于下图:
.meta表的RowKey由三部分组成:TableName(表名)、StartKey(起始键)、TimeStamp(时间戳),rowkey存储的内容又称为
region的Name(扩展:用来存放Region的文件夹的名字为RegionName的hash值,因为某些RegionName包含某些非法字符,而
RegionName为什么会包含非法字符是因为startkey是允许包含任何值的)。将组成rowkey的三个部分用逗号隔开组成了整个完整的
rowkey。TimeStamp使用十进制的数字字符串来表示。
.meta表的info为表中最主要的列簇,含有三个column:regioninfo、server、serverstartcode。regioninfo存储的是region的详细信
息,包括startkey、endkey、以及每个family信息。server存储的是管理这个region的regionserver地址。
由于.meta存储的是region的信息,如果当hbase中表的数据非常大会被分成很多个region,那么此时在.meta中所占的空间也会变大,而.meta本身也是一张表,在存储数据非常大的情况下,也会被分割成多个region存储于不同的regionserver上,此时要是想把.meta表的region位置信息存储在zookeeper集群中就不太现实,.meta表region的位置信息是会发生变化的。因此,此时我们可以通过另外一张表来存储.meta表的元数据信息,即-ROOT-(根数据表),hbase认为这张表不会太大,因此-ROOT-只会有一个region,这个region的信息存在于hbase中,而管理-ROOT-表的位置信息(regionserver地址)存储在Zookeeper中。
所以综上所述要想对HBase进行读写首先去访问Zookeeper集群,获得ROOT表的所在regionserver地址信息。
三:JAVA api操作Hbase
- 在maven项目中添加依赖
配置文件:
- HbaseDemo.java类
⑴判断表是否存在
⑵创建表
⑶删除表
⑷添加一行数据
⑸删除一行数据
⑹删除多行数据
⑺扫描数据
⑻获取一个列簇的数据
四:MapReduce
- 简介
通过 HBase 的相关 JavaAPI,我们可以实现伴随 HBase 操作的 MapReduce 过程,比如使用
MapReduce 将数据从本地文件系统导入到 HBase 的表中,比如我们从 HBase 中读取一些原始数
据后使用 MapReduce 做数据分析。
- 查看 HBase 的 MapReduce 任务所需的依赖
- 执行环境变量的导入
- 运行官方的 MapReduce 任务 ,统计 Student 表中有多少行数据
- 让环境变量永久生效,修改/etc/profile配置文件
五: 案例一:使用 MapReduce 将本地数据导入到 HBase
- 在本地创建一个 tsv 格式的文件:fruit.tsv
- 在 HDFS 中创建 input_fruit 文件夹并上传 fruit.tsv 文件
- 创建 HBase 的fruit表
- 执行 MapReduce 到 HBase 的 fruit 表中
- 进入hbase,查看fruit表
六:自定义 HBase-MapReduce1
- 需求
目标:将 fruit 表中的一部分数据,通过 MR 迁入到 fruit_mr 表中。 - 创建MAVEN项目
- 创建ReadFruitMapper.java类
package com.kgf.mr1; import java.io.IOException; import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.CellUtil; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.mapreduce.TableMapper; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.mapreduce.Mapper; /*** * 创建mapper类,读数据 * ImmutableBytesWritable:可以看做是rowkey,代表一行数据 * Put:这里面封装了一条一条数据 * @author KGF * */ public class ReadFruitMapper extends TableMapper<ImmutableBytesWritable,Put> { /*** * map方法处理数据 */ @Override protected void map(ImmutableBytesWritable key, Result value, Mapper<ImmutableBytesWritable, Result, ImmutableBytesWritable, Put>.Context context) throws IOException, InterruptedException { //读取数据,使用Put封装数据 Put put = new Put(key.get()); //遍历column for (Cell cell : value.rawCells()) { //筛选,我们只需要info列簇的数据 if("info".equals(Bytes.toString(CellUtil.cloneFamily(cell)))) { //我们只要列名为name的数据 if("name".equals(Bytes.toString(CellUtil.cloneQualifier(cell)))) { //将数据添加到put中 put.add(cell); } } } //将数据写出 context.write(key, put); } }
- 创建WriteFruitMRReducer.java类
package com.kgf.mr1; import java.io.IOException; import org.apache.hadoop.hbase.client.Mutation; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.mapreduce.TableReducer; import org.apache.hadoop.io.NullWritable; import org.apache.hadoop.mapreduce.Reducer; /*** * 创建reducer类 * @author KGF * */ public class WriteFruitMRReducer extends TableReducer<ImmutableBytesWritable, Put,NullWritable>{ @Override protected void reduce(ImmutableBytesWritable key, Iterable<Put> values, Reducer<ImmutableBytesWritable, Put, NullWritable, Mutation>.Context context) throws IOException, InterruptedException { for (Put put : values) { context.write(NullWritable.get(),put); } } }
- 创建Fruit2FruitMRRunner.java类
package com.kgf.mr1; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.util.Tool; import org.apache.hadoop.util.ToolRunner; /*** * 创建Runner * @author KGF * */ public class Fruit2FruitMRRunner implements Tool { private Configuration conf; public void setConf(Configuration conf) { //创建hbase的conf this.conf = HBaseConfiguration.create(); } public Configuration getConf() { return this.conf; } public int run(String[] args) throws Exception { //创建Job Job job = Job.getInstance(); //设置入口jar job.setJarByClass(Fruit2FruitMRRunner.class); //配置job Scan scan = new Scan(); //设置mapper TableMapReduceUtil.initTableMapperJob("fruit", scan, ReadFruitMapper.class,ImmutableBytesWritable.class, Put.class, job); //设置reducer TableMapReduceUtil.initTableReducerJob("fruit_mr",WriteFruitMRReducer.class, job); job.setNumReduceTasks(1); boolean result = job.waitForCompletion(true); return result?0:1; } public static void main(String[] args) { try { int status = ToolRunner.run(new Fruit2FruitMRRunner(), args); System.out.println(status); } catch (Exception e) { e.printStackTrace(); } } }
- 打成jar包,上传Linux
- 在Hbase上创建fruit_mr表
- 执行jar包
- 效果,数据成功导入
七:自定义 HBase-MapReduce2
- 需求:实现将 HDFS 中的数据(fruit.tsv)写入到 HBase 表中(先将fruit表数据清空)。
- 创建ReadFruitFromHDFSMapper.java类
package com.kgf.mr2; import java.io.IOException; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper; /** * 创建mapper类,因为我们是从HDFS上读取数据,所以我们继承的是普通的mapper, * 我们读取数据向HBASE中写所以使用ImmutableBytesWritable,Put写出, * 读取HDFS上的fruit.tsv中的数据 * @author KGF * */ public class ReadFruitFromHDFSMapper extends Mapper<LongWritable, Text, ImmutableBytesWritable,Put> { @Override protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, ImmutableBytesWritable, Put>.Context context) throws IOException, InterruptedException { //读取一行数据 String[] split = value.toString().split("\t"); byte[] rowkey = Bytes.toBytes(split[0]); byte[] name = Bytes.toBytes(split[1]); byte[] color = Bytes.toBytes(split[2]); //创建Put对象 Put put = new Put(rowkey); //为指定的列族,列添加数据 put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("name"), name); put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("color"),color); //写出 context.write(new ImmutableBytesWritable(rowkey),put); } }
- 创建Writer2HbaseReducer.java类
package com.kgf.mr2; import java.io.IOException; import org.apache.hadoop.hbase.client.Mutation; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.mapreduce.TableReducer; import org.apache.hadoop.io.NullWritable; import org.apache.hadoop.mapreduce.Reducer; public class Writer2HbaseReducer extends TableReducer<ImmutableBytesWritable, Put,NullWritable>{ @Override protected void reduce(ImmutableBytesWritable key, Iterable<Put> values, Reducer<ImmutableBytesWritable, Put, NullWritable, Mutation>.Context context) throws IOException, InterruptedException { for (Put put : values) { context.write(NullWritable.get(),put); } } }
- 创建HDFS2HbaseRunner.java
package com.kgf.mr2; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.util.Tool; import org.apache.hadoop.util.ToolRunner; import com.kgf.mr1.Fruit2FruitMRRunner; public class HDFS2HbaseRunner implements Tool { private Configuration conf; public void setConf(Configuration conf) { //创建hbase的conf this.conf = HBaseConfiguration.create(); } public Configuration getConf() { return this.conf; } public int run(String[] args) throws Exception { //创建Job Job job = Job.getInstance(); //设置入口jar job.setJarByClass(HDFS2HbaseRunner.class); //设置mapper job.setMapperClass(ReadFruitFromHDFSMapper.class); job.setMapOutputKeyClass(ImmutableBytesWritable.class); job.setMapOutputValueClass(Put.class); //设置reducer,OutPutFormat TableMapReduceUtil.initTableReducerJob("fruit",Writer2HbaseReducer.class, job); //设置FileInputFormat FileInputFormat.addInputPath(job, new Path("/input_fruit/")); job.setNumReduceTasks(1); boolean result = job.waitForCompletion(true); return result?0:1; } public static void main(String[] args) { try { int status = ToolRunner.run(new Fruit2FruitMRRunner(), args); System.out.println(status); } catch (Exception e) { e.printStackTrace(); } } }
- 打成jar包,上传Linux
- 进入/usr/local/module/hadoop-2.7.2目录下执行jar包
- 查看fruit表信息