HBase是实时的、分布式、高维的数据库。

实用于对大数据实时的查询,但前提是要利用Hbase的数据结构来存数据,才可以查询。

HBase是一个真正的数据库,是nosql数据库,主要用来存储非结构化和半结构化的松散数据。与Hive很不同,Hive不是数据库,数据存在HDFS上,只是建立一种表结构,最终使用mapreduce去操作。HBase是后台服务,数据存在HDFS上,但是数据结构是特有的数据结构,查询速度很快,也是利用mapreduce去计算


HBase数据模型:

---HBase表中的每个列都属于对应的列族,列族下的列是可以动态定义的,如列族为course,则该列族

    下的列为course:math, course:chinese;

---每个表都有ROW KEY,表示为一行,一行中对应很多的TimeStamp,也就是版本号;

---权限控制、存储以及调优都是在列族里进行的;

---HBase把同一列族里面的数据存储在同一目录下,由几个文件保存。

---单元格是由表的行和列的交叉来决定,每个单元格会表示同一块数据的不同的版本号,由时间戳指 

    定。

---单元格的内容是为解析的字节数组,其存储的内容是{ROW KEY,列族以及列内容,版本号},单元格 

   是HBase基本的存储类型。cell存储的是没有类型的,都是字节数组。

---HLog,记录了对数据的操作日志,和关系型数据库的log相似。


wKiom1YQ8nyC1sfsAAJydxZqB7M490.jpg

这个是HBase的基本的架构:

    HMaster是HBase的主节点,外界访问的时候,通过zookeeper去找主节点。干活的是主节点下面的从节点,主节点是管理的作用,从节点是干活的,也就是HRegionServer,其中有HLog来记录日志。

    HRegionServer(是一台服务器)下有很多的HRegion,这个是每行的数据,这是对HBase的表进行横向切分的结果,每若干行数据代表了一个HRegion。

    HRegion下有很多的store,一个store存储的就是一个列族的数据,这里就表示的列族,表里有很多的列族也就有很多的store。

    store分为MenStore和StoreFile,刚写入时数据会先放在MemStore,溢写时生产文件StoreFIle和HFile,HFile就是数据文件,存在HDFS中,StoreFile是元数据文件。其文件的元数据就存在StoreFile里。storeFile里存放的是文件在HDFS的位置信息等,storeFile存在region里,存在磁盘上,相当一个索引。查询时先在MemStore里查询(这里查的是数据),然后在Storefile里查询(这里通过索引找HDFS上的数据)。因此会产生很多的小文件,需要合并。


---client:HBase的接口并维护cache来加快对HBase的访问;

---Zookeeper:保证任何时候,集群只有一个Master;存储所有Region的寻找地址入口;实时监控        Region Server的上线与下线,并实时通知Master,如果某一个RegionServer出现问题,则拷贝其所

   有元数据到另一个regionServer中。存储Hbase的schema和table元数据。

---Master:负责负载均衡,使regionServer的数据数量不会变差太多,管理用户对table的增删改查。

   只负责新插入数据的负载均衡。

---RegionServer:存储region,并负责把过大的region进行切分。

---Region:HBase自动把表进行切分,每个region包含连续的几行数据,一开始,一张表对应的一个

   region,当数据不停插入时,region表示的数据不停增大,到达阈值时会裂变为两个。表会根据Row 

   Key进行数据排序。

---Memstore与storefile:

   一个region对应多个store,一个store对应一个列族;

   storefile数量增多到一定数量时,系统会合并这些(minor,major compaction),合并过程会进行版本合并或者删除(major),形成更大的storefile;

   当一个region的所有storefile太大了,会裂变,由hmaster操作;

注意:

    列族多了,store多,因此产生的storefile多,由于storefile的合并很耗内存,因此应是其数量少,所以Hbase表的列族不要超过三个。

---以region作为最小的管理单元,

wKiom1YRF4zB6q64AAFUwzeT6Ho102.jpg

外界可以根据row-Key去快速的定位到region,再快速的定位到store。

storefile合并,那么Hfile也进行合并。

列族里面存储的都是键值对的形式。


基于hadoop的HBase:

存储的文件是通过HBase建立的表导进去的,文件目录形式为:

/hbase/data/default/t_person/8281jsuququ8182u3192/cf1/

配置文件中会配置hbase存的文件在hdfs中的根目录为/hbase;

数据放在data下;

dafault为命名空间;

t_person为建立的表,也就是数据通过这个表结构导入进来的;

接下来后面的是regionServer里的region的名字;

cf1是列族;

接下来就是数据了。


具体操作:HA环境搭建Hbase

在hbase-site.xml里添加

<configuration>
  //是hbase文件在hdfs集群上存储的位置
  <property>
    <name>hbase.rootdir</name>
    <value>hdfs://mycluster/hbase</value>
  </property>
  //指明是分布式的
  <property>
    <name>hbase.cluster.distributed</name>
    <value>true</value>
  </property>
</configuration>
[root@hadoop1 conf]# vi regionservers 
hadoop2
hadoop3
//在regionservers中指明哪些节点作为regionserver。

在hbase-env.sh

首先要配置java_Home的路径;

还需更改ZK的启动方式为false,也就是指明启动hbase时,不启动自带的zookeeper。你需要修改conf/hbase-env.sh里面的HBASE_MANAGES_ZK 来切换。这个值默认是true的,作用是让HBase启动的时候同时也启动zookeeper. 

还需要在hbase-site.xml中加入:

   //这个是告诉hbase哪些节点上有zookeeper 
    <property>
      <name>hbase.zookeeper.quorum</name>
      <value>hadoop1,hadoop2,hadoop3</value>
    </property>
    //告诉hbase有zookeeper的节点,zookeeper的工作目录是哪个
    <property>
      <name>hbase.zookeeper.property.dataDir</name>
      <value>/root/zookeeper</value>
    </property>

因为hbase是要在zookeeper下运行的,zookeeper是hbase的入口。

这样hbase就能看到上面的入口了,其操作的存储文件放在hdfs上,因此也需要知道hdfs的配置信息,

  • ${HBASE_HOME}/conf下面加一个 hdfs-site.xml (或者 hadoop-site.xml) ,最好是软连接

把hadoop的hdfs-site.xml文件放在hbase的conf目录下。


在所有节点上都安装配置hbase后就可以在bin目录下运行./start-hbase.sh了。

在哪个节点上启动的hbase,则Hmaster就在哪个节点上有,而且还可以在其他节点上单独启动,只是运行一个,其它standby。(hbase-daemon.sh start master)


注意:

这里需要注意的一点就是:要保证所有节点的Hbase系统的时间同步,解决方法:

1.方案1
在hbase-site.xml添加配置
<property>
        <name>hbase.master.maxclockskew</name>
        <value>180000</value>
        <description>Time difference of regionserver from master</description>
 </property>

这个方案是把各个机器的时间差指明。

2.方案2

使用date -s指令修改时间。


这样hbase就正常启动了!./start-hbase.sh是启动后台服务。

下面操作hbase,使用./hbase shell进入hbase,在里面进行增删操作。在hbase里的操作不要加分号!!!使用指令去操作hbase。

./hbase hfile 这个是查看hbase里的文件。任何一个节点都可以查看。

hbase(main):004:0> create 't_person','cf1'
0 row(s) in 1.5030 seconds
hbase(main):006:0> put 't_person','007','cf1:name','zs'
0 row(s) in 0.1850 seconds
hbase(main):008:0> flush 't_person'
0 row(s) in 0.3800 seconds

建立表,建立表示,在regionserver里就生成region了,对应表的数据可以根据表结构进行插入或者导入了。对应的查询端口是60010。

可以插入数据,要根据表结构来插入,007是row key,表示为同一条数据。在最小存储单元里,注意数据是列族里的值,时间戳和rowKey是标志。

可以使用flush将内存的数据写入disk,是对应表。

也可以使用major_compact + 表名字,把对应表的数据进行合并,因为小文件太多所以要合并。

[root@hadoop2 bin]# ./hbase hfile -p -f /hbase/data/default/t_person/7e08e465df252e1711e4a6c7bcfb0d1c/cf1/9a4ae2788b314ed58d7a433634836e00
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/root/hbase-0.98.14-hadoop2/lib/slf4j-log4j12-1.6.4.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/root/hbase-0.98.14-hadoop2/lib/slf4j-log4j12-1.7.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/root/hadoop-2.4.1/share/hadoop/common/lib/slf4j-log4j12-1.7.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
2015-09-30 07:58:41,646 INFO  [main] Configuration.deprecation: fs.default.name is deprecated. Instead, use fs.defaultFS
2015-09-30 07:58:41,801 INFO  [main] Configuration.deprecation: hadoop.native.lib is deprecated. Instead, use io.native.lib.available
2015-09-30 07:58:41,909 INFO  [main] util.ChecksumType: Checksum using org.apache.hadoop.util.PureJavaCrc32
2015-09-30 07:58:41,911 INFO  [main] util.ChecksumType: Checksum can use org.apache.hadoop.util.PureJavaCrc32C
K: 007/cf1:age/1443612426300/Put/vlen=2/mvcc=0 V: 20
K: 007/cf1:name/1443612329875/Put/vlen=2/mvcc=0 V: zs
Scanned kv count -> 2

这里是使用hbase去查看文件:

./hbase hfile -p -f + 文件名


K: 007/cf1:age/1443612426300/Put/vlen=2/mvcc=0 V: 20
K: 007/cf1:name/1443612329875/Put/vlen=2/mvcc=0 V: zs

这个是插入的数据,一个列族为一个store,使用方式和时间戳都有,最后的是value值。

单元格由rowKey、列族+列来决定,里面存放的这些值和value

每一个rowKey可能会对应很多的单元格。


理解:

数据通过hbase进入hdfs时,会根据表结构来存,对应表的数据进入对应的region,对应表的列族分别存在列族中,数据多时,region会分裂操作,store多了时,也就是列族的数据文件多时就会合并。


Eclipse通过jar来操作hbase:

package bbdt.steiss.org;

import java.util.List;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.junit.Test;

public class PhontTest {

        //新建表操作
	@Test
	public void test1() throws Exception
	{
	        //配置类,添加zookeeper所在的节点,因为进入hbase入口由zookeeper来管理,找到zookeeper就可以找到hbase
		Configuration config = HBaseConfiguration.create();  
		config.set("hbase.zookeeper.quorum", "hadoop1,hadoop2,hadoop3");
		//通过conf获取管理者,可创建表
		HBaseAdmin admin = new HBaseAdmin(config);    
		String table = "t_zhenzhen";

		//判断hbase里是否有这个表结构
		if(admin.isTableAvailable(table))
		{
			admin.disableTable(table);   
			admin.deleteTable(table);
		}else{
		        //这个是建立表的类
			HTableDescriptor t = new HTableDescriptor(table.getBytes());                             //在表里添加列族
			HColumnDescriptor cf1 = new HColumnDescriptor("cf1".getBytes());
			t.addFamily(cf1);
			//建立表
			admin.createTable(t);
		}
		admin.close();
	}
	
	//插入操作
	@Test
	public void test2() throws Exception
	{
		Configuration config = HBaseConfiguration.create();  
		config.set("hbase.zookeeper.quorum", "hadoop1,hadoop2,hadoop3");
		//直接通过conf,得到对应表的类
		HTable hTable = new HTable(config, "t_zhenzhen");
		String rowKey = "29129101029_"+System.currentTimeMillis();
		//put对象是数据类,初始化时添加rowKey,标示是哪一行的
		Put put = new Put(rowKey.getBytes());
		//数据都是字节数组
		//列族,列,数据
		put.add("cf1".getBytes(), "age".getBytes(), "20".getBytes());
		put.add("cf1".getBytes(), "favor".getBytes(), "coding".getBytes());
		put.add("cf1".getBytes(), "aspiration".getBytes(), "google".getBytes());
		//将行插入,列族里可以有很多列,里面对应的键值对
		hTable.put(put);
		hTable.close();
	}
	
	//查找操作
	@Test
	public void Test3() throws Exception
	{
		Configuration config = HBaseConfiguration.create();  
		config.set("hbase.zookeeper.quorum", "hadoop1,hadoop2,hadoop3");
		//得到表
		HTable hTable = new HTable(config, "t_zhenzhen");
		//得到表中对应rowKey的对象
		Get get = new Get("29129101029_1444038378600".getBytes());
		Result result = hTable.get(get);
		List<Cell> list = result.listCells();
		System.out.println(new String(result.getRow()));
		for(Cell c : list)
		{
		        //列族
			String string = new String(c.getFamily())+"   ";
			//列
			string = string + new String(c.getQualifier())+"   ";
			//时间戳
			string = string + c.getTimestamp()+ "   ";
			//row-key
			string = string + new String(c.getRow())+ "   ";
			//值
			string = string + new String(c.getValue());
			System.out.println(string);
		}
		//这个是打印出的数据
//		29129101029_1444038378600
//		cf1   age   1443619829172   29129101029_1444038378600   20
//		cf1   aspiration   1443619829172   29129101029_1444038378600   google
//		cf1   favor   1443619829172   29129101029_1444038378600   coding

                //这个是实现范围查询,从哪个row_key查到哪个row_key,对于一个表结构来说,下面有很多行数据,hbase会针对row_key来排序
                
		Scan scan = new Scan();
		scan.setStartRow("29129101029_1444038378000".getBytes());
		scan.setStopRow("29129101029_1444038551070".getBytes());
		//可以针对某个表,进行遍历数据
		ResultScanner resultScanner = hTable.getScanner(scan);
		for(Result r: resultScanner)
		{
			System.out.println(r.size());
			System.out.println("ss");
		}
	}
	
}

hbase中的最大版本数是指:同样的key,根据不同的时间戳可以保存多少个,设定的版本数,用时间戳予以区分。

hbase使用row_key、列族以及列名来所得最小单元数据。都是按row_key来排序的。顶层通过标号来找下层。