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相似。
这个是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作为最小的管理单元,
外界可以根据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来排序的。顶层通过标号来找下层。
转载于:https://blog.51cto.com/wukong0716/1700257