Hbase(四)MR on Hbase & Hbase的 架构
五、MR On HBase
5.1 依赖
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-jobclient</artifactId>
<version>2.6.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-common -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.6.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-hdfs -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.6.0</version>
</dependency>
@Test
public void putData02() throws Exception {
/*
*ali baidu sina
*
* rowkey :公司名称+编号
* */
BufferedMutator mutator = connection.getBufferedMutator(TableName.valueOf("csdn:t_user1"));
String[] company = {"baidu", "ali", "sina"};
ArrayList<Put> puts = new ArrayList<Put>();
for (int i = 0; i < 1000; i++) {
String companyName = company[new Random().nextInt(3)];
String empid = "";
if (i < 10) {
empid = "00" + i;
} else if (i < 100) {
empid = "0" + i;
} else {
empid = "" + i;
}
String rowKey = companyName + ":" + empid;
Put put = new Put(rowKey.getBytes());
put.addColumn("cf1".getBytes(), "name".getBytes(), ("zs" + i).getBytes());
put.addColumn("cf1".getBytes(), "salary".getBytes(), Bytes.toBytes(100.0 * i));
put.addColumn("cf2".getBytes(), "age".getBytes(), "18".getBytes());
put.addColumn("cf2".getBytes(), "sex".getBytes(), "true".getBytes());
puts.add(put);
}
mutator.mutate(puts);
mutator.close();
}
package com.csdn.mrdemo;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.mapreduce.TableInputFormat;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.hbase.mapreduce.TableOutputFormat;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.MRJobConfig;
public class AVGJob {
public static void main(String[] args) throws Exception {
System.setProperty("HADOOP_USER_NAME", "root");
Configuration conf = new Configuration();
/*
* 设置跨平台提交
* */
conf.set("mapreduce.app-submission.cross-platform", "true");
conf.addResource("conf2/core-site.xml");
conf.addResource("conf2/hdfs-site.xml");
conf.addResource("conf2/mapred-site.xml");
conf.addResource("conf2/yarn-site.xml");
conf.set("hbase.zookeeper.quorum", "HadoopNode00");
conf.set("hbase.zookeeper.property.clientPort", "2181");
conf.set(MRJobConfig.JAR, "D:\\大数据\\Code\\BigData\\HBase_Test\\target\\HBase_Test-1.0-SNAPSHOT.jar");
Job job = Job.getInstance(conf);
job.setInputFormatClass(TableInputFormat.class);
job.setOutputFormatClass(TableOutputFormat.class);
TableMapReduceUtil.initTableMapperJob(
"baizhi:t_user1",
new Scan(),
AVGMapper.class,
Text.class,
DoubleWritable.class,
job
);
TableMapReduceUtil.initTableReducerJob(
"baizhi:t_result",
AVGReducer.class,
job
);
job.waitForCompletion(true);
}
}
package com.csdn.mrdemo;
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.io.DoubleWritable;
import org.apache.hadoop.io.Text;
import java.io.IOException;
public class AVGMapper extends TableMapper<Text, DoubleWritable> {
@Override
protected void map(ImmutableBytesWritable key, Result value, Context context) throws IOException, InterruptedException {
/*
* key 为rowky
* value 为rowky 下所代表的值
* */
/*
* 获取到rowkey的值
* */
byte[] bytes = key.get();
/*
* 转成字符串进行分割 获取下标为0的公司名字
* */
String companyName = Bytes.toString(bytes).split(":")[0];
byte[] salaryBytes = value.getValue("cf1".getBytes(), "salary".getBytes());
/*
* 工资数据
* */
double salary = Bytes.toDouble(salaryBytes);
context.write(new Text(companyName), new DoubleWritable(salary));
}
}
package com.csdn.mrdemo;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.mapreduce.TableReducer;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import java.io.IOException;
public class AVGReducer extends TableReducer<Text, DoubleWritable, NullWritable> {
@Override
protected void reduce(Text key, Iterable<DoubleWritable> values, Context context) throws IOException, InterruptedException {
double totalSalary = 0;
int countPeople = 0;
for (DoubleWritable value : values) {
totalSalary += value.get();
countPeople++;
}
Put put = new Put(key.getBytes());
put.addColumn("cf1".getBytes(), "avgSalary".getBytes(), (totalSalary / countPeople + "").getBytes());
context.write(NullWritable.get(), put);
}
}
六 、HBase架构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rUf2DRGA-1594544562381)(assets/1573022781166.png)]
6.1 Zookeeper
HBase通过zk来做Master的高可用,RegionServer的监控、元数据的入口以及集群配置的维护工作;具体如下:
通过ZK保证集群中只有一个Master可用,在主的Master的出现异常后,会通过竞争机制产生新的Master
通过ZK监控RegionServer的状态,当RegionServer有异常时,同时Master RegionServer上下线信息。
通过ZK存储元数据
ZK作为统一的入口地址
6.2 HMaster
为RegionServer分配Region,维护集群的负载均衡,维护集群的元数据信息,发现失效的Region,将失效的Region分配到正常的RegionServer上,当RegionServer失效时,协调对应的HLog的拆分。
6.3 HRegionServer
HRegionServer直接对接用户的读写请求,是真正的干活的额节点,功能如下:
管理Master为其分配的Region
处理来自客户端的读写请求,负责与HDFS进行交互,负责Region变大后的拆分。
6.3.1 Region
- HBase 表根据Rowkey 划分成Region,理论上一个Region包含该表格从起始行到结束之间的所有行。
- 但是往往有可能一个Region没有办法存储这个表中所有的行,会进行切分
数据大小>=N^2*128MB
(N为Region数量),当N=9时,切分大小会超过10GB,此时就按照10GB进行切分。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-emZs10EL-1594544562393)(assets/1573010149953.png)]
- Region由Store 组成
- Region会被分配到称之为“HRegionServer”的节点上
Store
每一个Region由一个或者多个Store组成,至少是一个Store 。HBase会把经常访问的数据放在一个Store里面,即一个列簇组成一个Store ,有多少个列簇就多少个Store 。
在Store 中,由一个memStore 和 0个或者多个StoreFile 组成
MemStore
- 写缓存。数据先写入到MemStore,触发flush机制后写到磁盘中
- 以key-value的形式存储到内存中
- MemStore的6种Flush机制(当MemStore Flush触发后,同一Region下的所有的MemStore都会刷新)
- MemStore级别:当Region中人一个MemStore的大小达到上限(默认为128MB hbase.hregion.memstore.flush.size),会触发MemStore刷新
- Region级别:当Region中所有的MemStore大小总和达到的上限(256MB hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size,默认 2* 128M = 256M)
- RegionServer级别:当RegionServer中所有的MemStore大小总和达到上限(hbase.regionserver.global.memstore.upperLimit * hbase_heapsize,默认 40%的JVM内存使用量),会触发部分Memstore刷新。Flush顺序是按照Memstore由大到小执行,先Flush Memstore最大的Region,再执行次大的,直至总体Memstore内存使用量低于阈值(hbase.regionserver.global.memstore.lowerLimit * hbase_heapsize,默认 38%的JVM内存使用量)。
- 当一个RegionServer中HLog数量达到了上限(可通过参数hbase.regionserver.maxlogs配置)时,系统会选取最早的一个 HLog对应的一个或多个Region进行flush
- HBase定期刷新Memstore:默认周期为1小时,确保Memstore不会长时间没有持久化。为避免所有的MemStore在同一时间都进行flush导致的问题,定期的flush操作有20000左右的随机延时。
- 手动执行flush:用户可以通过shell命令 flush ‘tablename’或者flush ‘region name’分别对一个表或者一个Region进行flush。
StoreFile|HFile
HFile(StoreFile) 用于存储HBase的数据(Cell/KeyValue)。在HFile中的数据是按RowKey、Column Family、 Column排序,对相同的Cell(即这三个值都一样),则按timestamp倒序排列。
由于MemStore中存储的Cell遵循相同的排列顺序,因而Flush过程是顺序写,由于不需要不停的移动磁盘指针,因此磁盘的顺序写性能很高。
HFile的组成分为6部分,分别是数据块、元数据块、FileInfo块、数据索引块、元数据索引块、HFile文件尾,它们的详细描述如下:
名称 | 描述 |
---|---|
数据块 | 由多个Block(块)组成,每个块的格式为: [块头] + [Key长] + [Value长] + [Key] + [Value]。 |
元数据块 | 元数据是Key-Value类型的值,但元数据块只保存元数据的Value值,元数据的Key值保存在第五项(元数据索引块)中。 该块由多个元数据值组成。 |
FileInfo块 | 该块保存与HFile相关的一些信息。 FileInfo是以Key值排序Key-Value类型的值,基本格式为: KeyValue元素的个数 + (Key + Value类型id + Value) + (Key + Value类型id + Value) + …… |
数据索引块 | 该块的组成为: 索引块头 + (数据块在文件中的偏移 + 数据块长 + 数据块的第一个Key) + (数据块在文件中的偏移 + 数据块长 + 数据块的第一个Key) + …… |
元数据索引块 | 该块组成格式同数据块索引,只是部分的意义不一样,组成格式: 索引块头 + (元数据在文件中的偏移 + 元数据Value长 + 元数据Key) + (元数据在文件中的偏移 + 元数据Value长 + 元数据Key) + …… |
HFile文件尾 | 该块记录了其他各块在HFile文件中的偏移信息和其他一些元信息。组成格式如下: 文件尾 + Fileinfo偏移 + 数据块索引偏移 + 数据块索引个数 + 元数据索引偏移 + 元数据索引个数 + 数据块中未压缩数据字节数 + 数据块中全部数据的Key-Value个数 + 压缩代码标识 + 版本标识 |
6.3.2 WAL
Write Ahead Log | HLog
-
一个文件
-
0.94之前叫做HLog,存储在/hbase/.logs/目录中
-
0.94之后存储在HDFS上的**/hbase/WALs/{HRegionServer_name}**中
-
记录RegionServer 上所有的编辑信息(Puts/Deletes,属于哪个Region),在写入MemStore之前。
-
理论上一个RegionServer上只有一个WAL实例,数据操作为串行,造成性能瓶颈。在1.0之后 ,可以通过使用底层HDFS的多管道实现了多WAL并行写入。提高了吞吐量(但是并行化是通过对多个Region进行分区实现的,如果只有一个Region那么该方案无效)下面是配置多WAL示例:hbase-site.xml 配置
<property>
<name>hbase.wal.provider</name>
<value>multiwal</value>
</property>
6.4 BlockCache|HBase Block(了解)
- 读缓存,数据被读取之后仍然缓存在内存中
- 有LruBlockCache(效率较高,GC压力大)和BucketCache(效率较低,没有GC压力)两种BlockCache,默认为LruBlockCache
- 每个RegionServer中只有一个BlockCache实例
6.5 HDFS
HDFS为HBase提供最终的底层数据存储服务,同时为HBase提供高可用(HLog存储在HDFS)的支持,具体功能概括如下:
提供元数据和表数据的磁层分布式存储服务
数据多副本,保证高可靠性和高可用性
献给每一个正在努力的我们,就算在忙,也要注意休息和饮食哦!我就是我,一个在互联网跌跌撞撞,摸爬滚打的热忱,给个三连吧~