一、HBASE概述
Hadoop Database
NoSQL
面向列
提供实时更新查询
。。。。
是一个高可靠性 高性能 面向列 可伸缩的分布式存储系统
利用hbase技术可以在廉价的PC上搭建起大规模结构化存储集群。
HBase利用HadoopHDFS作为其文件存储系统,利用Hadoop的MapReduce来处理HBase中的海量数据,利用Zookeeper作为协调工具
行键 - RowKey - 即hbase的主键,访问hbse中的数据有三种方式
通过单一行键访问
通过一组行键访问
全表扫描
列族(簇) - Column Family
在建表时声明,不能后期增加,如果需要增加只能alter表,一个列族可以包含一个或多个列
列 - Column
可以动态增加列,不需要提前声明
单元格与时间戳 - cell timestamp
通过row和columns确定的一个存储单元为一个cell。每个cell都保存着一个数据的多个版本,版本通过时间戳来区别。
数据都以二进制形式存储,没有数据类型的区别。
所有空数据都不占用空间。
二、安装配置--hbase资源
HBase-0.92.x HBase-0.94.x HBase-0.96
Hadoop-0.20.205 S X X
Hadoop-0.22.x S X X
Hadoop-1.0.x S S S
Hadoop-1.1.x NT S S
Hadoop-0.23.x X S NT
Hadoop-2.x X S S
s 支持 nt 未经过完整测试 x 不支持
前提条件,安装jdk,并配置了环境变量
1.单机模式,将数据存储到本地
直接解压安装包
tar -zxvf xxxxx.tar.gz
修改conf/hbase-site.xml,配置hbase使用的数据文件的位置,默认在/tmp/hbase-[username],此目录是linux的临时目录,可能会被系统清空,所以最好修改一下
<property>
<name>hbase.rootdir</name>
<value>file:///<path>/hbase</value>
</property>
测试
bin/start-hbase.sh
bin/hbase shell
hbase>status
hbase>help
hbase>create 'testtable',''colfam1','colfam2'
hbase>list
hbase>describe 'testtable'
hbase>put 'testtable','myrow-1','colfam1:q1','value-1'
hbase>put 'testtable','myrow-2','colfam1:q2','value-2'
hbase>put 'testtable','myrow-2','colfam1:q3','value-3'
hbase>scan 'testtable'
hbase>get 'testtable','myrow-1'
hbase>delete 'testtable','myrow-2','colfam1:q2'
hbase>scan 'testtable'
hbase>disable 'testtable'
hbase>drop 'testtable'
#建表时可以指定VERSIONS,配置的是当前列族在持久化到文件系统中时,要保留几个最新的版本数据,这并不影响内存中的历史数据版本
hbase>create 'testtable',{NAME=>'colfam1',VERSIONS=>3},{NAME=>'colfam2',VERSIONS=>1}
hbase>put 'testtable','myrow-1','colfam1:q1','value-1'
#直接使用scan而不加RAW=>true只能查询到最新版本的数据
hbase>scan 'testtable'
hbase>put 'testtable','myrow-1','colfam1:q1','value-2'
hbase>scan 'testtable'
hbase>put 'testtable','myrow-1','colfam1:q1','value-3'
hbase>scan 'testtable'
#可以在查询时加上RAW=>true来开启对历史版本数据的查询,VERSIONS=>3指定查询最新的几个版本的数据
hbase>scan 'testtable',{RAW=>true,VERSIONS=>3}
hbase>put 'testtable','myrow-1','colfam1:q1','value-4'
hbase>scan 'testtable'
hbase>scan 'testtable',{RAW=>true,VERSIONS=>3}
hbase>put 'testtable','myrow-1','colfam2:x1','value-1'
hbase>scan 'testtable'
hbase>put 'testtable','myrow-1','colfam2:x1','value-2'
hbase>scan 'testtable'
hbase>scan 'testtable',{RAW=>true,VERSIONS=>3}
#重启hbase
hbase>scan 'testtable',{RAW=>true,VERSIONS=>3}
#TODO:画图解释结构,没有数据的不占用空间,物理结构按列存储,方便压缩。
hbase>exit
bin/stop-hbase.sh
--------------------
hbase命令行下不能使用删除:
可以使用 ctrl+删除键 来进行删除
或
修改xshell配置:
文件->属性->终端->键盘
->delete键序列[VT220Del]
->backspace键序列[ASCII127]
--------------------
2.伪分布式模式
修改conf/hbase-env.sh修改JAVA_HOME
export JAVA_HOME=xxxx
修改hbase-site.xml,配置使用hdfs
<property>
<name>hbase.rootdir</name>
<value>hdfs://hadoop00:9000/hbase</value>
</property>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
启动hbase
3.完全分布式模式
修改conf/hbase-env.sh修改JAVA_HOME
export JAVA_HOME=xxxx
修改hbase-site.xml,配置开启完全分布式模式
配置hbase.cluster.distributed为true。
配置hbase.rootdir设置为HDFS访问地址
<property>
<name>hbase.rootdir</name>
<value>hdfs://hadoop00:9000/hbase</value>
</property>
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
配置region服务器,修改conf/regionservers文件,其中配置所有hbase主机,每个主机名独占一行,hbase启动或关闭时会按照该配置顺序启动或关闭主机中的hbase
使用已有的zookeeper集群。这种方式下zookeeper脱离了hbase,不会随着hbase的启动关闭而启动关闭。需要自己来启动关闭。
hbase默认使用自带的zookeeper,如果需要使用外部zookeeper,需要先关闭
修改conf/hbase-env.sh禁用内部zookeeper
export HBASE_MANAGES_ZK false
在hbase-site.xml中配置Zookeeper的连接地址与端口号
<property>
<name>hbase.zookeeper.quorum</name>
<value>hadoop01:2181,hadoop02:2181,hadoop03:2181</value>
</property>
--------------------------------------------------
~HBASE配置文件说明
hbase-env.sh配置HBase启动时需要的相关环境变量
hbase-site.xml配置HBase基本配置信息
HBASE启动时默认使用hbase-default.xml中的配置,如果需要可以修改hbase-site.xml文件,此文件中的配置将会覆盖hbase-default.xml中的配置
修改配置后要重启hbase才会起作用
--------------------------------------------------
启动集群
启动zookeeper
启动hdfs
启动hbase
访问http://xxxxx:60010来访问web界面,通过web见面管理hbase
也可以通过hbase shell脚本来访问bhase
关闭集群
stop-hbase.sh
三、HBASE原理---参考
HBase的工作方式:
hbase中的表在行的方向上分隔为多个HRegion,分散在不同的RegionServer中
这样做的目的是在查询时可以将工作量分布到多个RegionServer中以提高速度
region由[startkey,endkey)表示
HRegion是hbase分布式存储和负载均衡的最小单元
要注意HRegion不在hdfs中,而是在RegionServer的内存中,在内存(其实也有数据在本地文件系统中,因为内存空间毕竟是有限的)中存储数据以提高查询性能,对于修改会将数据最终同步到hdfs中,以持久化数据。
hbase中的数据按照rowkey的字典顺序(字典顺序!!!先比较第一位 如果相同再比较第二位。。。)按序存储,所以当以rowkey查询数据时,可以提高速度。
hregion的分裂,当hregion变的很大时会分裂成两个,存放到不同的RegionServer中,防止一个Region过大,导致查询其中数据时速度很慢
hbase的系统结构:主要有client hmaster regionServer zookeeper
为什么hbase可以很快:
从逻辑结构上来说:
表按照行键进行了排序,所以查询时可以很快定位
数据按照行键切分为多个HRegion,分布在多个RegionServer中,查询大量数据时,多个RegionServer可以一起工作,从而提高速度
从物理结构上来说:
HRegion是存活在RegionServer的内存中的,读写会非常的高效
还有HFile的支持保证大量的数据可以持久化的保存
数据最终落地到HDFS中,分布式的存储,保证数据段可靠性和可扩展性
为什么hbase可以存储很多数据:
基于hdfs,所以支持可扩展性,可以通过增加大量的廉价的硬件提高存储容量
按列存储,空的数据不占用空间,当存储稀疏数据时,不会浪费空间
按例存储,同一列的数据存放在一起,而同一列的数据一般都是同样的类型的内容相似的数据,可以实现非常高效的压缩,节省空间
为什么hbase的数据是可靠的:
基于hdfs,由hdfs的可靠性保证了hbase的可靠性--即数据可以有多个备份
利用zookeeper实现了HA,即使某一台机器挂掉另外的机器也可以很快的替换它
hbase和hive和传统的关系型数据库的比较:
比起传统的关系型数据库,可以存储半结构化非结构化的数据,可以存储和处理更大级别的数据,提供高效的查询,对于稀疏数据的处理更好,具有更好的横向扩展性,免费开源性价比很高。但是不能支持非常好的事务特性,只支持行级的事务。只能通过行键来查询,表设计时难度更高。而mysql用来存储结构化的数据提供更好的事务控制。
比起hive,hive只是在mapreduce上包了一层壳,本质上还是离线数据的处理的工具,实时查询性能有限,本质上是一个基于hadoop的数据仓库工具,不能支持行级别的新增修改和删除。hbase可以提供实时的数据的处理能力,适用于在线数据查询处理,本质上是一种数据库工具。
四、hbase的api操作--参考
其中的拦截器参考-参考
import java.io.IOException;
import java.util.List;
import org.apache.commons.io.filefilter.PrefixFileFilter;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.MasterNotRunningException;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.ZooKeeperConnectionException;
import org.apache.hadoop.hbase.client.Delete;
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.apache.hadoop.hbase.filter.BinaryComparator;
import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.KeyOnlyFilter;
import org.apache.hadoop.hbase.filter.PrefixFilter;
import org.apache.hadoop.hbase.filter.RegexStringComparator;
import org.apache.hadoop.hbase.filter.RowFilter;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Test;
public class TestHbase {
@Test
public void create() throws Exception{
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
HBaseAdmin admin = new HBaseAdmin(conf);
TableName table = TableName.valueOf("javaTable");
HTableDescriptor tb = new HTableDescriptor(table );
HColumnDescriptor cf1 = new HColumnDescriptor("cf1".getBytes());
tb.addFamily(cf1);
HColumnDescriptor cf2 = new HColumnDescriptor("cf2".getBytes());
tb.addFamily(cf2);
admin.createTable(tb);
admin.close();
}
@Test
public void put() throws Exception{
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
HTable table = new HTable(conf, Bytes.toBytes("javaTable"));
Put put = new Put(Bytes.toBytes("rk2"));
put.add("cf1".getBytes(), "c1".getBytes(), "rk1-cf1-c1-value1".getBytes());
put.add("cf2".getBytes(), "c1".getBytes(), "rk1-cf2-c1-value2".getBytes());
table.put(put );
table.close();
}
//修改将原先位置重新put一个值即可,这不是更新实际上是增加了一个新版本的数据
@Test
public void update() throws Exception{
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
HTable table = new HTable(conf, Bytes.toBytes("javaTable"));
Put put = new Put(Bytes.toBytes("rk2"));
put.add("cf1".getBytes(), "c1".getBytes(), "rk2-cf1-c1-value1".getBytes());
put.add("cf2".getBytes(), "c1".getBytes(), "rk2-cf2-c1-value2".getBytes());
table.put(put );
table.close();
}
@Test
public void get() throws Exception{
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
HTable table = new HTable(conf, Bytes.toBytes("javaTable"));
Get get = new Get("rk1".getBytes());
get.addColumn("cf1".getBytes(), "c1".getBytes());
Result result = table.get(get );
byte[] value = result.getValue("cf1".getBytes(), "c1".getBytes());
String aString = new String(value);
System.out.println(aString);
table.close();
}
//条件查询、批量查询01
@Test
public void scan01() throws Exception{
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
HTable table = new HTable(conf, Bytes.toBytes("javaTable"));
Scan scan = new Scan();
ResultScanner scanner = table.getScanner(scan );//scan不追加值表明是遍历全表
Result next = null;
while((next=scanner.next())!=null){
byte[] rowBytes = next.getRow();
String row = new String(rowBytes);
byte[] valueBytes = next.getValue("cf1".getBytes(), "c1".getBytes());
String value = new String(valueBytes);
System.out.println(row+":"+value);
}
table.close();
}
//条件查询、批量查询02
@Test
public void scan02() throws Exception{
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
HTable table = new HTable(conf, Bytes.toBytes("javaTable"));
Scan scan = new Scan();//scan中的一个功能:如下包含开始不包含结束
scan.setStartRow("rk1".getBytes());
scan.setStopRow("rk2".getBytes());
ResultScanner scanner = table.getScanner(scan );
Result next = null;
while((next=scanner.next())!=null){
byte[] rowBytes = next.getRow();
String row = new String(rowBytes);
byte[] valueBytes = next.getValue("cf1".getBytes(), "c1".getBytes());
String value = new String(valueBytes);
System.out.println(row+":"+value);
}
table.close();
}
//条件查询、批量查询03
@Test
public void scan03() throws Exception{
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
HTable table = new HTable(conf, Bytes.toBytes("javaTable"));
Scan scan = new Scan();//scan中的一个功能:增加一个拦截器
// Filter filter = new RowFilter(CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes("rk1")));//hbase提供的拦截器1
// Filter filter = new RowFilter(CompareOp.EQUAL, new RegexStringComparator("^.*2.*$"));//hbase提供的拦截器2-正则
//filterList多个过滤器一起起作用
Filter filter1 = new PrefixFilter("rk1".getBytes());
Filter filter2 = new KeyOnlyFilter();
FilterList filterList = new FilterList(filter1,filter2);
scan.setFilter(filterList);
ResultScanner scanner = table.getScanner(scan );
Result next = null;
while((next=scanner.next())!=null){
byte[] rowBytes = next.getRow();
String row = new String(rowBytes);
byte[] valueBytes = next.getValue("cf1".getBytes(), "c1".getBytes());
String value = new String(valueBytes);
System.out.println(row+":"+value);
}
table.close();
}
@Test
public void delete() throws Exception{
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
HTable table = new HTable(conf, Bytes.toBytes("javaTable"));
Delete delete = new Delete("rk1".getBytes());
table.delete(delete );
table.close();
}
@Test
public void drop() throws Exception{
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
HBaseAdmin admin = new HBaseAdmin(conf);
admin.disableTable("javaTable".getBytes());
admin.deleteTable("javaTable".getBytes());
admin.close();
}
}
五、hbase表设计
HBase是用来存放半结构化 非结构化数据的数据库。
1.列族的设计
在设计hbase表时候,列族不宜过多,尽量的要少使用列族。
经常要在一起查询的数据最好放在一个列族中,尽量的减少跨列族的数据访问。
2.行键的设计
hbase表中行键是唯一标识一个表的字段,首先要求唯一,另外最好是一些有意义的值,来帮助我们识别表中的数据。所以hbase中的行键是需要进行设计的。
hbase中的行键的设置至关重要,严重的影响hbase的执行效率和查询的便利性。
行键设计的基本原则:
行键必须唯一
必须唯一才能唯一标识数据
行键最好是字符串类型
因为数值类型在不同的系统中处理的方式可能不同
行键必须有意义
这样才能方便数据的查询
行键最好具有固定的长度
不同长度的数据可能会造成自然排序时排序的结果和预期不一致
行键的最佳实践:
长度原则:
行键最多可以达到64KB,但是最好是在10~100字节之间,最好不要超过16字节,越短越好,最好是8字节的整数倍。
散列原则:
行键的设计将会影响数据在hbase表中的排序方式,这会影响region切分后的结果,要注意,在设计行键时应该让经常要查询的数据分散在不同的region中,防止某一个或某几个regionserver称为热点。
有序原则:
行键的设计将会影响数据在hbase表中的排序方式,所以一种策略是将经常连续查询的条件作为行键最前面的数据,这样一来可以方便批量查询
六、案例
用户表
id name age gender email
001 zhang 19 男 zhang@qq.com
002 wang 20 男 wang@qq.com
用户访问的网页
host viewtime content userid
www.baidu.com 2016-12-20 xxxx 001
www.sina.com 2016-11-10 xxxx 001
www.souhu.com 2016-11-09 xxxx 001
www.baidu.com 2016-12-20 xxxx 002
www.163.com 2016-12-20 xxxx 002