分布式数据库 HBase思维导图下载
HBase是一个稀疏、多维度、排序的映射表,这张表的索引是行键、列族、列限定符和时间戳
面向列的存储
HBase中需要根据行键、列族、列限定符和时间戳来确定一个单元格,因此,可以视为一个“四维坐标”,即[行键, 列族, 列限定符, 时间戳]
主要的功能组件:
客户端并不是直接从Master主服务器上读取数据,而是在获得Region的存储位置信息后,直接从Region服务器上读取数据。
客户端并不依赖Master,而是通过Zookeeper来获得Region位置信息,大多数客户端甚至从来不和Master通信,这种设计方式使得Master负载很小。
客户端包含访问HBase的接口,同时在缓存中维护着已经访问过的Region位置信息,用来加快后续数据访问过程。
主服务器Master主要负责表和Region的管理工作:
Region服务器是HBase中最核心的模块,负责维护分配给自己的Region,并响应用户的读写请求。
•Store是Region服务器的核心
•多个StoreFile合并成一个
•单个StoreFile过大时,又触发分裂操作,1个父Region被分裂成两个子Region
Master-status(自带)、Ganglia、OpenTSDB、Ambari。
好处:
三种访问行的方式:
使用其他产品问HBase行键提供索引功能:
Hindex二级索引、HBase+Redis、HBase+solr
HBase编程实践:
本节介绍HBase的安装方法,包括下载安装文件、配置环境变量、添加用户权限等。
HBase是Hadoop生态系统中的一个组件,但是,Hadoop安装以后,本身并不包含HBase,因此,需要单独安装HBase。假设已经下载了HBase安装文件hbase-1.1.5-bin.tar.gz,被放到了Linux系统的“/home/hadoop/下载/”目录下。
$ cd ~/下载 # 进入下载目录
$ wget http://10.132.239.12:8000/file/hbase-1.1.5-bin.tar.gz # 下载HBase软件
下载完安装文件以后,需要对文件进行解压。按照Linux系统使用的默认规范,用户安装的软件一般都是存放在“/usr/local/”目录下。请使用hadoop用户登录Linux系统,打开一个终端,执行如下命令:
$ sudo tar -zxf ~/下载/hbase-1.1.5-bin.tar.gz -C /usr/local
将解压的文件名hbase-1.1.5改为hbase,以方便使用,命令如下:
$ sudo mv /usr/local/hbase-1.1.5 /usr/local/hbase
将HBase安装目录下的bin目录(即/usr/local/hbase/bin)添加到系统的PATH环境变量中,这样,每次启动HBase时就不需要到“/usr/local/hbase”目录下执行启动命令,方便HBase的使用。请使用vim编辑器打开“~/.bashrc”文件,命令如下:
$ vim ~/.bashrc
打开.bashrc文件以后,可以看到,已经存在如下所示的PATH环境变量的配置信息,因为,之前在安装配置Hadoop时,我们已经为Hadoop添加了PATH环境变量的配置信息:
export PATH=$PATH:/usr/local/hadoop/sbin:/usr/local/hadoop/bin
这里,需要把HBase的bin目录“/usr/local/hbase/bin”追加到PATH中。当要在PATH中继续加入新的路径时,只要用英文冒号“:”隔开,把新的路径加到后面即可,追加后的结果如下:
export PATH=$PATH:/usr/local/hadoop/sbin:/usr/local/hadoop/bin:/usr/local/hbase/bin
添加后,执行如下命令使设置生效:
$ source ~/.bashrc
需要为当前登录Linux系统的hadoop用户添加访问HBase目录的权限,将HBase安装目录下的所有文件的所有者改为hadoop,命令如下:
$ cd /usr/local
$ sudo chown -R hadoop ./hbase
可以通过如下命令查看HBase版本信息,以确认HBase已经安装成功:
$ /usr/local/hbase/bin/hbase version
HBase有三种运行模式,即单机模式、伪分布式模式和分布式模式:
单机模式:采用本地文件系统存储数据;
伪分布式模式:采用伪分布式模式的HDFS存储数据;
分布式模式:采用分布式模式的HDFS存储数据。
本教程仅介绍单机模式和伪分布式模式。
在进行HBase配置之前,需要确认已经安装了三个组件:JDK、Hadoop、SSH。HBase单机模式不需要安装Hadoop,伪分布式模式和分布式模式需要安装Hadoop。
使用vim编辑器打开“/usr/local/hbase/conf/hbase-env.sh”,命令如下:
$ vim /usr/local/hbase/conf/hbase-env.sh
打开hbase-env.sh文件以后,需要在hbase-env.sh文件中配置JAVA环境变量,此前在安装Hadoop的环节,已经配置了JAVA环境变量,比如,“JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64”,这里可以直接复制该配置信息到hbase-env.sh文件中。此外,还需要添加Zookeeper配置信息,配置HBASE_MANAGES_ZK为true,表示由HBase自己管理Zookeeper,不需要单独的Zookeeper,由于hbase-env.sh文件中本来就存在这些变量的配置,因此,只需要删除前面的注释“#”符号并修改配置内容即可,修改后的hbase-env.sh文件应该包含如下两行信息:
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
export HBASE_MANAGES_ZK=true
修改完成以后,保存hbase-env.sh文件并退出vim编辑器。
使用vim编辑器打开并编辑“/usr/local/hbase/conf/hbase-site.xml”文件,命令如下:
$ vim /usr/local/hbase/conf/hbase-site.xml
在hbase-site.xml文件中,需要设置属性hbase.rootdir,用于指定HBase数据的存储位置,如果没有设置,则hbase.rootdir默认为/tmp/hbase-${user.name},这意味着每次重启系统都会丢失数据。这里把hbase.rootdir设置为HBase安装目录下的hbase-tmp文件夹,即“/usr/local/hbase/hbase-tmp”,修改后的hbase-site.xml文件中的配置信息如下:
hbase.rootdir
file:///usr/local/hbase/hbase-tmp
保存hbase-site.xml文件,并退出vim编辑器。
启动运行HBase
现在就可以测试运行HBase,命令如下:
$ cd /usr/local/hbase
$ bin/start-hbase.sh #启动HBase
$ bin/hbase shell #进入HBase shell命令行模式
进入HBase Shell命令行模式以后,用户可以通过输入Shell命令操作HBase数据库。
最后,可以使用如下命令停止HBase运行:
$ bin/stop-hbase.sh
配置hbase-env.sh文件
使用vim编辑器打开“/usr/local/hbase/conf/hbase-env.sh”,命令如下:
$ vim /usr/local/hbase/conf/hbase-env.sh
打开hbase-env.sh文件以后,需要在hbase-env.sh文件中配置JAVA_HOME、HBASE_CLASSPATH和HBASE_MANAGES_ZK。其中,HBASE_CLASSPATH设置为本机Hadoop安装目录下的conf目录(即/usr/local/hadoop/conf)。JAVA_HOME和HBASE_MANAGES_ZK的配置方法和上面单机模式的配置方法相同。修改后的hbase-env.sh文件应该包含如下三行信息:
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
export HBASE_CLASSPATH=/usr/local/hadoop/conf
export HBASE_MANAGES_ZK=true
修改完成以后,保存hbase-env.sh文件并退出vim编辑器。
使用vim编辑器打开并编辑“/usr/local/hbase/conf/hbase-site.xml”文件,命令如下:
$ vim /usr/local/hbase/conf/hbase-site.xml
在hbase-site.xml文件中,需要设置属性hbase.rootdir,用于指定HBase数据的存储位置。在HBase伪分布式模式中,是使用伪分布式模式的HDFS存储数据,因此,需要把hbase.rootdir设置为HBase在HDFS上的存储路径,根据Hadoop伪分布式模式的配置可以知道,HDFS的访问路径为“hdfs://localhost:9000/”,因为,这里设置hbase.rootdir为“hdfs://localhost:9000/hbase”。此外,由于采用了伪分布式模式,因此,还需要将属性hbase.cluter.distributed设置为true。修改后的hbase-site.xml文件中的配置信息如下:
hbase.rootdir
hdfs://localhost:9000/hbase
hbase.cluster.distributed
true
保存hbase-site.xml文件,并退出vim编辑器。
启动运行HBase
首先登陆SSH,由于之前在“Hadoop的安装和使用”中已经设置了无密码登录,因此这里不需要密码。然后,切换至“/usr/local/hadoop”,启动Hadoop,让HDFS进入运行状态,从而可以为HBase存储数据,具体命令如下:
$ ssh localhost
$ cd /usr/local/hadoop
$ ./sbin/start-dfs.sh
输入命令jps,如果能够看到NameNode、DataNode和SecondaryNameNode这三个进程,则表示已经成功启动Hadoop。
然后,启动HBase,命令如下:
$ cd /usr/local/hbase
$ bin/start-hbase.sh
输入命令jps,如果出现以下进程,则说明HBase启动成功:
Jps
HMaster
HQuorumPeer
NameNode
HRegionServer
SecondaryNameNode
DataNode
现在就可以进入HBase Shell模式,命令如下:
$ bin/hbase shell #进入HBase shell命令行模式
进入HBase shell命令行模式以后,用户可以通过输入shell命令操作HBase数据库。
停止 运行HBase
最后,可以使用如下命令停止HBase运行:
$ bin/stop-hbase.sh
如果在操作HBase的过程中发生错误,可以查看{HBASE_HOME}目录(即/usr/local/hbase)下的logs子目录中的日志文件,来寻找可能的错误原因。
关闭HBase以后,如果不再使用Hadoop,就可以运行如下命令关闭Hadoop:
$ cd /usr/local/hadoop
$ ./sbin/stop-dfs.sh
最后需要注意的是,启动关闭Hadoop和HBase的顺序一定是:启动Hadoop—>启动HBase—>关闭HBase—>关闭Hadoop。
在使用具体的Shell命令操作HBase数据之前,需要首先启动Hadoop,然后再启动HBase,并且启动HBase Shell,进入Shell命令提示符状态,具体命令如下:
$ cd /usr/local/hadoop
$ ./sbin/start-dfs.sh
$ cd /usr/local/hbase
$ ./bin/start-hbase.sh
$ ./bin/hbase shell
假设这里要创建一个表student,该表包含Sname、Ssex、Sage、Sdept、course等字段。需要注意的是,在关系型数据库(比如MySQL)中,需要首先创建数据库,然后再创建表,但是,在HBase数据库中,不需要创建数据库,只要直接创建表就可以。在HBase中创建student表的Shell命令如下:
hbase> create 'student','Sname','Ssex','Sage','Sdept','course'
对于HBase而言,在创建HBasae表时,不需要自行创建行健,系统会默认一个属性作为行键,通常是把put命令操作中跟在表名后的第一个数据作为行健。
创建完“student”表后,可通过describe命令查看“student”表的基本信息,命令如下:
hbase>describe ‘student’
可以使用list命令查看当前HBase数据库中已经创建了哪些表,命令如下:
hbase> list
HBase使用put命令添加数据,一次只能为一个表的一行数据的一个列(也就是一个单元格,单元格是HBase中的概念)添加一个数据,所以,直接用Shell命令插入数据效率很低,在实际应用中,一般都是利用编程操作数据。因为这里只要插入1条学生记录,所以,我们可以用Shell命令手工插入数据,命令如下:
hbase> put 'student','95001','Sname','LiYing'
上面的put命令会为student表添加学号为’95001’、名字为’LiYing’的一个单元格数据,其行键为95001,也就是说,系统默认把跟在表名student后面的第一个数据作为行健。
下面继续添加4个单元格的数据,用来记录LiYing同学的相关信息,命令如下:
hbase> put 'student','95001','Ssex','male'
hbase> put 'student','95001','Sage','22'
hbase> put 'student','95001','Sdept','CS'
hbase> put 'student','95001','course:math','80'
HBase中有两个用于查看数据的命令:
get命令:用于查看表的某一个单元格数据;
scan命令:用于查看某个表的全部数据。
比如,可以使用如下命令返回student表中95001行的数据:
hbase> get 'student','95001'
下面使用scan命令查询student表的全部数据:
hbase> scan 'student'
在HBase中用delete以及deleteall命令进行删除数据操作,二者的区别是:delete命令用于删除一个单元格数据,是put的反向操作,而 deleteall命令用于删除一行数据。
首先,使用delete命令删除student表中95001这行中的Ssex列的所有数据,命令如下:
hbase > delete 'student','95001','Ssex'
然后,使用deleteall命令删除student表中的95001行的全部数据,命令如下:
hbase> deleteall 'student','95001'
删除表需要分两步操作,第一步先让该表不可用,第二步删除表。比如,要删除student表,可以使用如下命令:
hbase> disable 'student'
hbase> drop 'student'
在添加数据时,HBase会自动为添加的数据添加一个时间戳。在修改数据时,HBase会为修改后的数据生成一个新的版本(时间戳),从而完成“改”操作,旧的版本依旧保留,系统会定时回收垃圾数据,只留下最新的几个版本,保存的版本数可以在创建表的时候指定。
为了查询历史数据,这里创建一个teacher表,首先,在创建表的时候,需要指定保存的版本数(假设指定为5),命令如下:
hbase> create 'teacher',{NAME=>'username',VERSIONS=>5}
然后,插入数据,并更新数据,使其产生历史版本数据,需要注意的是,这里插入数据和更新数据都是使用put命令,具体如下:
hbase> put 'teacher','91001','username','Mary'
hbase> put 'teacher','91001','username','Mary1'
hbase> put 'teacher','91001','username','Mary2'
hbase> put 'teacher','91001','username','Mary3'
hbase> put 'teacher','91001','username','Mary4'
hbase> put 'teacher','91001','username','Mary5'
查询时,默认情况下回显示当前最新版本的数据,如果要查询历史数据,需要指定查询的历史版本数,由于上面设置了保存版本数为5,所以,在查询时制定的历史版本数的有效取值为1到5,具体命令如下:
hbase> get ‘teacher’,‘91001’,{COLUMN=>‘username’,VERSIONS=>5}
hbase> get ‘teacher’,‘91001’,{COLUMN=>‘username’,VERSIONS=>3}
最后退出数据库操作,输入exit命令即可退出,命令如下:
hbase> exit
注意,这里退出HBase数据库是退出HBase Shell,而不是停止HBase数据库后台运行,执行exit后,HBase仍然在后台运行,如果要停止HBase运行,需要使用如下命令:
$ bin/stop-hbase.sh
HBase提供了Java API对HBase数据库进行操作。这里采用Eclipse进行程序开发。在进行HBase编程之前,如果还没有启动Hadoop和HBase,需要首先启动Hadoop和HBase,但是,不需要启动HBase Shell,具体命令如下:
$ cd /usr/local/hadoop
$ ./sbin/start-dfs.sh
$ cd /usr/local/hbase
$ ./bin/start-hbase.sh
Eclipse启动以后,会弹出如下图所示界面,提示设置工作空间(workspace)。
可以直接采用默认的设置“/home/hadoop/workspace”,点击“OK”按钮。可以看出,由于当前是采用hadoop用户登录了Linux系统,因此,默认的工作空间目录位于hadoop用户目录“/home/hadoop”下。
Eclipse启动以后,在界面中,选择“FileàNewàJava Project”菜单,开始创建一个Java工程,弹出如下图所示界面。
在“Project name”后面输入工程名称“HBaseExample”,选中“Use default location”,让这个Java工程的所有文件都保存到“/home/hadoop/workspace/HBaseExample”目录下。在“JRE”这个选项卡中,可以选择当前的Linux系统中已经安装好的JDK,比如java-8-openjdk-amd64。然后,点击界面底部的“Next>”按钮,进入下一步的设置。
进入下一步的设置以后,会弹出如下图所示界面。
为了编写一个能够与HBase交互的Java应用程序,需要在这个界面中加载该Java工程所需要用到的JAR包,这些JAR包中包含了可以访问HBase的Java API。这些JAR包都位于Linux系统的HBase安装目录的lib目录下,也就是位于“/usr/local/hbase/lib”目录下。点击界面中的“Libraries”选项卡,然后,点击界面右侧的“Add External JARs…”按钮,弹出如下图所示界面。
选中“/usr/local/hbase/lib”目录下的所有JAR包(但是,不要选中“ruby”文件夹),然后,点击界面右下角的“确定”按钮。
添加完毕以后,就可以点击界面右下角的“Finish”按钮,完成Java工程HBaseExample的创建。
下面编写一个Java应用程序,对HBase数据库进行操作。
请在Eclipse工作界面左侧的“Package Explorer”面板中(如下图所示),找到刚才创建好的工程名称“HBaseExample”,然后在该工程名称上点击鼠标右键,在弹出的菜单中选择“NewàClass”菜单。
选择“NewàClass”菜单以后会出现如下图所示界面。
在该界面中,只需要在“Name”后面输入新建的Java类文件的名称,这里采用名称“HBaseOperation”,其他都可以采用默认设置,然后,点击界面右下角“Finish”按钮,出现如下图所示界面。
可以看出,Eclipse自动创建了一个名为“HBaseOperation.java”的源代码文件,请在该文件中输入以下代码:
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import java.io.IOException;
public class HBaseOperation{
public static Configuration configuration;
public static Connection connection;
public static Admin admin;
public static void main(String[] args)throws IOException{
createTable("t2",new String[]{"cf1","cf2"});
insertRow("t2", "rw1", "cf1", "q1", "val1");
getData("t2", "rw1", "cf1", "q1");
//deleteTable("t2"); //如果不想执行本行代码,可以注释掉本行代码
}
//建立连接
public static void init(){
configuration = HBaseConfiguration.create();
configuration.set("hbase.rootdir","hdfs://localhost:9000/hbase");
try{
connection = ConnectionFactory.createConnection(configuration);
admin = connection.getAdmin();
}catch (IOException e){
e.printStackTrace();
}
}
//关闭连接
public static void close(){
try{
if(admin != null){
admin.close();
}
if(null != connection){
connection.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
//建表
public static void createTable(String myTableName,String[] colFamily) throws IOException {
init();
TableName tableName = TableName.valueOf(myTableName);
if(admin.tableExists(tableName)){
System.out.println("talbe is exists!");
}else {
HTableDescriptor hTableDescriptor = new HTableDescriptor(tableName);
for(String str:colFamily){
HColumnDescriptor hColumnDescriptor = new HColumnDescriptor(str);
hTableDescriptor.addFamily(hColumnDescriptor);
}
admin.createTable(hTableDescriptor);
}
close();
}
//删表
public static void deleteTable(String tableName) throws IOException {
init();
TableName tn = TableName.valueOf(tableName);
if (admin.tableExists(tn)) {
admin.disableTable(tn);
admin.deleteTable(tn);
}
close();
}
//查看已有表
public static void listTables() throws IOException {
init();
HTableDescriptor hTableDescriptors[] = admin.listTables();
for(HTableDescriptor hTableDescriptor :hTableDescriptors){
System.out.println(hTableDescriptor.getNameAsString());
}
close();
}
public static void insertRow(String tableName,String rowKey,String colFamily,String col,String val) throws IOException {
init();
Table table = connection.getTable(TableName.valueOf(tableName));
Put put = new Put(rowKey.getBytes());
put.addColumn(colFamily.getBytes(), col.getBytes(), val.getBytes());
table.put(put);
table.close();
close();
}
//删除数据
public static void deleteRow(String tableName,String rowKey,String colFamily,String col) throws IOException {
init();
Table table = connection.getTable(TableName.valueOf(tableName));
Delete delete = new Delete(rowKey.getBytes());
//删除指定列族
//delete.addFamily(Bytes.toBytes(colFamily));
//删除指定列
//delete.addColumn(Bytes.toBytes(colFamily),Bytes.toBytes(col));
table.delete(delete);
table.close();
close();
}
//根据rowkey查找数据
public static void getData(String tableName,String rowKey,String colFamily,String col)throws IOException{
init();
Table table = connection.getTable(TableName.valueOf(tableName));
Get get = new Get(rowKey.getBytes());
get.addColumn(colFamily.getBytes(),col.getBytes());
Result result = table.get(get);
showCell(result);
table.close();
close();
}
//格式化输出
public static void showCell(Result result){
Cell[] cells = result.rawCells();
for(Cell cell:cells){
System.out.println("RowName:"+new String(CellUtil.cloneRow(cell))+" ");
System.out.println("Timetamp:"+cell.getTimestamp()+" ");
System.out.println("column Family:"+new String(CellUtil.cloneFamily(cell))+" ");
System.out.println("row Name:"+new String(CellUtil.cloneQualifier(cell))+" ");
System.out.println("value:"+new String(CellUtil.cloneValue(cell))+" ");
}
}
}
上述代码输入到HBaseOperation.java中以后,Eclipse可能会显示一些错误信息,如下图所示。
Syntax error, ‘for each’ statements are only available if source level is 1.5 or greater
之所以出现这个错误,是因为HBaseOperation.java代码中的一些语句的语法(比如for(String str:colFamily)),是在JDK1.5以上版本才支持的,JDK1.4以及之前的版本不支持这些语法。
现在可以查看一下Linux系统中安装的Eclipse的相关设置,如下图所示,请在顶部菜单中选择“Project”,在弹出的子菜单中选择“Properties”。
在该界面中,点击左侧栏目的“Java Compiler”,在右边出现的信息中可以看出,“Compiler compliance level”当前默认的设置为1.4,不支持HBaseOperation.java代码中的一些语句的语法(比如for(String str:colFamily))。
为了解决这个错误,如下图所示,用鼠标左键点击错误提示图标,在弹出的对话框中,在“Change project compliance and JRE to 1.5”这行文字上双击鼠标左键,就可以让错误信息消失。
再次强调,在开始编译运行程序之前,请一定确保Hadoop和HBase已经启动运行。现在就可以编译运行上面编写的代码。可以直接点击Eclipse工作界面上部的运行程序的快捷按钮,当把鼠标移动到该按钮上时,在弹出的菜单中选择“Run as”,继续在弹出来的菜单中选择“Java Application”,如下图所示。
程序运行结束后,结果如下图所示,“Console”面板中还会显示一些类似“log4j:WARN…”的警告信息,可以不用理会。
现在可以在Linux的终端中启动HBase Shell,来查看生成的t1表,启动HBase Shell的命令如下:
$ cd /usr/local/hbase
$ ./bin/hbase shell
进入HBase Shell以后,可以使用list命令查看HBase数据库中是否存在名称为t1的表。
hbase> list
然后,可以使用get命令查询刚才插入到t2中的一行数据:
hbase> get ‘t2’,’rw1’
最后,如果要重复调试HBaseOperation.java代码,则需要删除HBase中已经创建的表t2,可以使用Shell命令删除t2,命令如下:
hbase> disable ‘t2’
hbase> drop ‘t2’