HBase是一个分布式的、面向列的开源数据库,该技术来源于 Fay Chang 所撰写的Google论文“Bigtable:一个结构化数据的分布式存储系统”。就像Bigtable利用了Google文件系统(File System)所提供的分布式数据存储一样,HBase在Hadoop之上提供了类似于Bigtable的能力。HBase是Apache的Hadoop项目的子项目。HBase不同于一般的关系数据库,它是一个适合于非结构化数据存储的数据库,即Nosql。另一个不同的是HBase基于列的而不是基于行的模式。
这是关于hbase体系结构官方的一张图,可以看出,hbase是基于hdfs之上的分布式数据库,hbase上的数据最终还是存到hdfs上。hbase和hdfs对应的存储结构如下:
HBase | HDFS |
---|---|
表 | 目录 |
数据 | 文件(HFile) |
也就是说,hbase上的表对应在hdfs上是目录的形式,表中的数据在hdfs上则是以文件的形式存在。
下面对上图做一点简化。
由上面两张图可以看出,和HDFS、YARN类似,HBase也是一个主从结构,主节点为HMaster,从节点为RegionServer。
此外,与HDFS、YARN不同的是,客户端不能直接与主节点HMaster进行通信,要通过“中间人”zookeeper进行通信或读写请求。在HBase体系中,自带了一个zookeeper,可以直接使用,也可自行安装zookeeper,将其部署到HBase体系中。
ZooKeeper作为一个分布式应用程序协调服务,可以将其看为一个“数据库”。它用于选举一个集群内的主节点HMaster,存储着集群的元信息,包括HMaster的信息、在实现HA(高可用性)时存储着HMaster的active或standby状态(上图中的紫色HMaster就是standby状态)。客户端通过访问zookeeper就可以知道HMaster的位置信息,以及应该访问哪个HMaster,建立客户端到该HMaster的连接。
如果在region的分配过程中某个服务器崩溃了,就可以通过zookeeper来进行分配的协调,在zookeeper上管理分配事务的状态有助于在恢复时能够从崩溃的服务器遗留的状态开始分配;在HA高可用中,如果主节点HMaster崩溃了,zookeeper可以将另一个standby状态的HMaster切换为active的主节点,继续执行集群的管理和任务。
先看HBase表和关系型数据库Oracle的对比
和一般的表类似,HBase的表由行和列组成。表格的“单元格”由行和列的坐标交叉决定,是有版本的,默认情况下,版本号是自动分配的,为HBase插入单元格时的时间戳。
表中行的键rowkey也是也是字节数组,所以理论上任何东西都能通过表示成字符串或将二进制形式转化为长整型或直接对数据结构进行序列化,来作为键值。表中的行根据行的键值(也就是表的主键进行排序,默认是升序),所有对表的访问都要通过表的主键。行键rowkey不能为null,可以重复,但是相同的rowkey是同一条数据。
行中的列被分为Column Family(“列族”或“列簇”),Table在水平方向有一个或者多个Column Family组成,一个Column Family中可以由任意多个Column(列)组成,即Column Family支持动态扩展,无需预先定义Column的数量以及类型,所有Column均以二进制格式存储,用户需要自行进行类型转换。同一个列族的所有成员具有相同的前缀,一般为列族的名称。列族和修饰符(即列名)始终以冒号分格。如上图中的列info:name和info:age都是列族info的成员。
用上面的图作例子来说,在创建student表的时候只创建了info和grade两个列族,但是在插入数据的时候,对于Oracle中的一行数据,可以通过指定相同的行键s001来保证插入hbase中的是同一条数据,并且在插入时通过 列族:列名 的格式指定数据插入的位置,如果该列族中没有该列,则会自动创建,并将数据存入,如指定’info:name’在列族info中建立name列,然后再把TOM这个值插入这一列。
所以,一个hbase表的列族必须作为表模式定义的一部分预先给出,不能更改,但是新的列族成员(即新的列)可以随时按需要加入。物理上,所有的列族成员都一起存放在文件系统中。所以,虽然说HBase是面向列的存储器,但是实际上更准确的说法是面向列族的存储器。调优和存储都是在列族这个层次上进行的,所有列族成员都应具有相同的访问模式和大小特征。
到此,对比上图再看HBase和RDBMS中的区别,简单来说两者类似,只是HBase表的单元格有版本,行是排序的,行键(主键)是可以重复的,而只要列族预先存在,就可以随时把新的列添加到列族中去。此外,HBase是一个分布式、面向列的数据存储系统,通过在HDFS上提供随机读/写来解决大规模数据的处理问题。它的表可以很“高”(数十亿个数据行),也可以很宽(数百万个列);水平分区并在上千个普通商用机节点上自动复制。表的模式是物理存储的直接反映,使系统有可能提供高效的数据结构的序列化、存储和检索。
-ROOT- && .META. Table
HBase中有两张特殊的Table,-ROOT-和.META.
.META.:记录了用户表的Region信息,.META.可以有多个regoin
-ROOT-:记录了.META.表的Region信息,-ROOT-只有一个region
Zookeeper中记录了-ROOT-表的location
Client访问用户数据之前需要首先访问zookeeper,然后访问-ROOT-表,接着访问.META.表,最后才能找到用户数据的位置去访问,
HBase提供了一个shell的终端给用户交互,启动hdfs和hbase后,通过在命令行输入"hbase shell"就可以进入hbase的shell终端,对hbase进行操作,常见的一些操作命令如下:
名称 | 命令表达式 |
---|---|
创建表 | create ‘表名称’,‘列族名称1’,‘列族名称2’,‘列族名称n’ |
添加记录 | put ‘表名称’,‘行名称’,‘列族名称:列名称’,‘值’ |
查看记录 | get ‘表名称’,‘行名称’ |
查看表中的记录数 | count ‘表名称’ |
删除记录 | delete ‘表名’,‘行名称’,‘列名称’ |
删除一张表 | 先要屏蔽该表,才能对该表进行删除,第一步disable ‘表名称’ 第二部 drop ‘表名称’ |
查看所有记录 | scan ‘表名称’ |
查看某个表某个列中所有的数据 | scan ‘表名称’,{COLUMNS=>‘列族名称:列名称’} |
更新记录 | 就是重写一遍进行覆盖 |
HBase上的过滤器类似程序中的if条件判断,即将需要的数据提取出来,不需要的数据过滤掉,主要有以下几种:
HDFS上的MapReduce在运行过程中Mapper的输入
先在HBase上创建一张word表作为输入源
create 'word','content'
put 'word','1','content:info','I love Beijing'
put 'word','2','content:info','I love China'
put 'word','3','content:info','Beijing is the capital of China'
再创建一张stat表作为输出源
create 'stat','content'
//这时候处理的就是HBase表中的一条数据
// 代表输入,现在输入是:表中的一条记录 k2 v2
public class WordCountMapper extends TableMapper {
@Override
protected void map(ImmutableBytesWritable key, Result value,Context context)
throws IOException, InterruptedException {
// 获取数据: I love Beijing
String data = Bytes.toString(value.getValue(Bytes.toBytes("content"), Bytes.toBytes("info")));
//分词
String[] words = data.split(" ");
for(String w:words){
context.write(new Text(w), new IntWritable(1));
}
}
}
// k3 v3 keyout代表输出的一条记录:指定行键
public class WordCountReducer extends TableReducer {
@Override
protected void reduce(Text k3, Iterable v3,Context context)
throws IOException, InterruptedException {
//对v3求和
int total = 0;
for(IntWritable v:v3){
total = total + v.get();
}
//输出:也是表中的一条记录
//构造一个Put对象,把单词作为rowkey
Put put = new Put(Bytes.toBytes(k3.toString()));
put.add(Bytes.toBytes("content"), //列族
Bytes.toBytes("result"), // 列
Bytes.toBytes(String.valueOf(total))); //值
//输出
context.write(new ImmutableBytesWritable(Bytes.toBytes(k3.toString())),
put);//得到结果
}
}
由于zookeeper的存在和MapReducer的不同,HBase的主程序也有所不同,具体如下:
public static void main(String[] args) throws Exception {
//指定Zookeeper地址
//指定的配置信息: ZooKeeper
Configuration conf = new Configuration();
conf.set("hbase.zookeeper.quorum", "zookeeper所在ip地址");
//创建一个任务
Job job = Job.getInstance(conf);
job.setJarByClass(WordCountMain.class);
//定义一个扫描器只读取 content:info这个列的数据
Scan scan = new Scan();
scan.addColumn(Bytes.toBytes("content"), Bytes.toBytes("info"));
//指定mapper
TableMapReduceUtil.initTableMapperJob(Bytes.toBytes("word"), //输入的表
scan, //定义一个扫描器,过滤我们想要处理的数据
WordCountMapper.class, //运行哪个mapper
Text.class, //k2的类型
IntWritable.class, //v2的类型
job);
//指定reducer
// 表名,哪个reducer,任务
TableMapReduceUtil.initTableReducerJob("stat", WordCountReducer.class, job);
//执行任务
job.waitForCompletion(true);
}