9、hbase

一、简介

(一)概述

(Ⅰ)概述

1、HBASE是Apache提供的基于Hadoop的、分布式的、可扩展的、能够存储大量数据的非关系型数据库
2、基于Google的论文《BigTable》设计实现
3、区别于传统数据库的行存储,HBASE是面向列进行存储,底层基于Key-Value结构存储
4、HBASE能够提供低延迟的数据查询能力,其原因是底层充分利用了缓存机制以及复杂的数据结构和算法来实现

(Ⅱ)行存储和列存储

1、行存储在磁盘上的存储是连续的;列存储在磁盘上的存储是不连续的
2、从写入性能上对比,写入次数越少性能越高。因为针对磁盘的每一次写入,都要发生磁头调度,产生寻道时间。因为行存储是只写一次而列存储要写多次,所以行存储在写入性能上更有优势
3、从读取性能上对比:
a. 如果读取的是整表,则行存储性能较高
b. 如果是读取指定的列,则行存储会产生冗余列,而冗余列的消除是在内存中发生。而列存储则不会存在冗余列
4、在存储数据的时候,如果基于行存储,由于一行数据的字段类型可能不同,所以会产生频繁的数据类型转换;如果是基于列存储,由于同一列数据的类型一般一致,则可以避免频繁的数据类型转换,同时可以考虑一些更好的压缩算法对一列数据进行压缩

(二)基本概念

(Ⅰ)概述

1、HBase以表的形式存储数据。表有行和列族组成。列族划分为若干个列。其结构如下:
在这里插入图片描述

(Ⅱ)Row Key:行键

1、hbase本质上也是一种Key-Value存储系统。Key相当于RowKey,Value相当于列族数据的集合
2、与nosql数据库们一样,row key是用来检索记录的主键
3、访问hbase table中的行,只有三种方式:
a. 通过单个row key访问
b. 通过row key的range
c. 全表扫描
4、Row key行键 (Row key)可以是任意字符串(最大长度是 64KB,实际应用中长度一般为 10-100bytes),在hbase内部,row key保存为字节数组
5、存储时, 数据按照Rowkey的字典序(byte order)排序存储。设计key时,要充分排序存储这个特性,将经常一起读取的行存储放到一起。(位置相关性)

(Ⅲ)列族(列簇)

1、hbase表中的每个列,都归属与某个列族
2、列族是表的schema的一部分(而列不是),列族必须在使用表之前定义
3、列名都以列族作为前缀。例如courses:history , courses:math 都属于 courses 这个列族
4、访问控制、磁盘和内存的使用统计都是在列族层面进行的。实际应用中,列族上的控制权限能帮助管理不同类型的应用:允许一些应用可以添加新的基本数据、一些应用可以读取基本数据并创建继承的列族、一些应用则只允许浏览数据(甚至可能因为隐私的原因不能浏览所有数据)

(Ⅳ)Cell与时间戳

1、由rowkey + (colomn family:colomn qualifier) + version 唯一确定的单元
2、cell中的数据是没有类型的,全部是字节码形式存贮
3、每个 cell都保存着同一份数据的多个版本,版本通过时间戳来索引
4、时间戳的类型是 64位整型。时间戳可以由hbase(在数据写入时自动 )赋值,此时时间戳是精确到毫秒的当前系统时间。时间戳也可以由客户显式赋值
5、如果应用程序要避免数据版本冲突,就必须自己生成具有唯一性的时间戳
6、每个 cell中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面
7、为了避免数据存在过多版本造成的的管理 (包括存贮和索引)负担,hbase提供了两种数据版本回收方式:
a. 保存数据的最后n个版本
b. 保存最近一段时间内的版本(比如最近七天)
用户可以针对每个列族进行设置

二、安装

(一)单机安装

(Ⅰ)特点

1、不依赖于Hadoop的HDFS,配置完既可使用,好处是便于测试。坏处是不具备分布式存储数据的能力

(Ⅱ)安装步骤

1、安装JDK
2、上传或者下载HBASE的安装包
3、解压安装包:tar -xvf hbase-0.98.17-hadoop2-bin.tar.gz
4、进入HBASE安装目录下的子目录conf:cd hbase-0.98.17-hadoop2/conf
5、修改配置文件hbase-site.xml:vim hbase-site.xml
6、添加如下配置:

<!--指定HBASE的数据存储目录-->
	<property>
	    <name>hbase.rootdir</name>
	    <value>file:///home/software/hbase/tmp</value>
	</property>

7、进入HBASE的安装目录的子目录bin下:cd …/bin
8、启动服务器端,执行sh start-hbase.sh
启动完成之后可以通过jps命令查看是否有HMaster进程
9、启动客户端 ./hbase shell

(二)伪分布式安装

1、安装JDK
2、安装Hadoop的伪分布式或者完全分布式集群
3、上传或者下载HBASE的安装包
4、解压安装包:tar -xvf hbase-0.98.17-hadoop2-bin.tar.gz
5、进入HBASE安装目录下的子目录conf:cd hbase-0.98.17-hadoop2/conf
6、修改conf/hbase-env.sh:vim hbase-env.sh
7、添加JAVA_HOME:export JAVA_HOME=JDK的实际安装路径
8、重新生效:source hbase-env.sh
9、修改配置文件hbase-site.xml:vim hbase-site.xml
10、配置使用hdfs:

	<property>
	    <name>hbase.rootdir</name>
	    <value>hdfs://hadoop01:9000/hbase</value>
	</property>
	<property>
	    <name>dfs.replication</name>
	    <value>1</value>
	</property>

11、启动Hadoop。如果是使用的Hadoop完全分布式集群,则还需要启动Zookeeper
12、进入HBASE的安装目录的子目录bin下:cd …/bin
13、启动服务器端,执行sh start-hbase.sh
启动完成之后可以通过jps命令查看是否有HMaster进程
14、启动客户端 ./hbase shell

(三)完全分布式安装

1、安装和配置:Hadoop+JDK+Zookeeper
2、安装Hbase
(1)上传文件
在这里插入图片描述
(2)解压文件并重命名

[root@hadoop01 ~]# cd /home/software/
[root@hadoop01 software]# tar -zxvf hbase-0.98.17-hadoop2-bin.tar.gz 
[root@hadoop01 software]# mv hbase-0.98.17-hadoop2 hbase

3、修改conf/hbase-env.sh

#修改JAVA_HOME:export JAVA_HOME=/home/software/jdk1.8.0_131
	#修改ZookeeperHbase的协调模式,hbase默认使用自带的zookeeper,如果需要使用外部zookeeper,需要先关闭:export HBASE_MANAGES_ZK=false

4、修改hbase-site.xml,配置开启完全分布式模式

<configuration>
<property> 
         <name>hbase.rootdir</name>
         <value>hdfs://hadoop01:9000/hbase</value>
</property> <property>
        <name>hbase.cluster.distributed</name>
        <value>true</value></property>
<!--#配置Zookeeper的连接地址与端口号-->
<property>
        <name>hbase.zookeeper.quorum</name>        <value>hadoop01:2181,hadoop02:2181,hadoop03:2181</value>
</property>
</configuration>

或者在这里插入图片描述

5、配置region服务器,修改conf/regionservers文件,每个主机名独占一行,hbase启动或关闭时会按照该配置顺序启动或关闭主机中的hbase:

hadoop01
hadoop02
hadoop03

6、将01节点配置好的hbase通过远程复制拷贝到其他节点上

scp -r /home/software/hbase 192.168.232.130:/home/software/
scp -r /home/software/hbase 192.168.232.131:/home/software/

7、启动Zookeeper服务

sh /home/software/zookeeper-3.4.7/bin/zkServer.sh start

8、启动Hadoop

start-all.sh

9、启动Hbase

sh start-hbase.sh

到这基本结束,如果有问题,执行下列操作:
10、查看各节点的java进程是否正确,或者通过浏览器访问http://xxxxx:60010来访问web界面,通过web见面管理hbase
11、关闭Hmaster,进入到hbase安装目录下的bin目录,执行:stop-hbase.sh
12、关闭regionserver,进入到hbase安装目录下的bin目录,执行:sh hbase-daemon.sh stop regionserver
13、如果启动集群后regionserver自动挂掉可能由于服务器时间不同步。
每台服务器执行:ntpdate -u ntp.sjtu.edu.cn
为了保证HBase集群的高可靠性,HBase支持多Backup Master 设置。当Active Master挂掉后,Backup Master可以自动接管整个HBase的集群。
该配置极其简单:
在$HBASE_HOME/conf/ 目录下新增文件配置backup-masters,在其内添加要用做Backup Master的节点hostname。如下:
在这里插入图片描述

三、hbase表

一张hbase表是由行键+列族组成
一个列族可以有多个列组成
cell 行键+列族+列+值确定一个cell
可以重复赋值,可以有多个版本(version),以时间戳区分
hbase是稀疏存储,即可以在列中跳跃着存储。会优化磁盘存储空间,不会产生磁盘碎片,造成浪费

四、基本指令

(一)指令表

在这里插入图片描述

(二)具体示例

进入hbase的客户端

[root@hadoop01 bin]# sh hbase shell
2021-08-26 08:15:54,821 INFO  [main] Configuration.deprecation: hadoop.native.lib is deprecated. Instead, use io.native.lib.available
HBase Shell; enter 'help<RETURN>' for list of supported commands.
Type "exit<RETURN>" to leave the HBase Shell
Version 0.98.17-hadoop2, rd5f8300c082a75ce8edbbe08b66f077e7d663a4a, Fri Jan 15 22:46:43 PST 2016

hbase(main):001:0> 

想要删除 按住ctrl + 删除键

hbase(main):001:0> list
TABLE                                                                           
2021-08-26 08:42:57,337 WARN  [main] util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/home/software/hbase/lib/slf4j-log4j12-1.6.4.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/home/software/hadoop-2.7.1/share/hadoop/common/lib/slf4j-log4j12-1.7.10.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
0 row(s) in 1.4760 seconds

=> []
hbase(main):002:0> create 'student','basic','info'
0 row(s) in 0.8800 seconds

=> Hbase::Table - student
hbase(main):003:0> list
TABLE                                                                           
student                                                                         
1 row(s) in 0.0130 seconds

=> ["student"]
hbase(main):004:0> put 'student','r1','basic:name','zhangsan'
0 row(s) in 0.1720 seconds

hbase(main):005:0> put 'student','r1','basic:age','18'
0 row(s) in 0.0270 seconds

hbase(main):006:0> put 'student','r1','info:adress','beijing'
0 row(s) in 0.0220 seconds

hbase(main):007:0> put 'student','r1','info:gender','male'
0 row(s) in 0.0300 seconds

hbase(main):008:0> put 'student','r2','basic:name','lisi'
0 row(s) in 0.0140 seconds

hbase(main):009:0> put 'student','r2','basic:age','19'
0 row(s) in 0.0090 seconds

hbase(main):010:0> put 'student','r2','info:adress','shanghai'
0 row(s) in 0.0100 seconds

hbase(main):011:0> put 'student','r2','info:gender','male'
0 row(s) in 0.0100 seconds

hbase(main):012:0> get 'student','r1'
COLUMN                CELL                                                      
 basic:age            timestamp=1629938768076, value=18                         
 basic:name           timestamp=1629938729561, value=zhangsan                   
 info:adress          timestamp=1629938830720, value=beijing                    
 info:gender          timestamp=1629938871896, value=male                       
4 row(s) in 0.0550 seconds

hbase(main):013:0> get 'student','r2'
COLUMN                CELL                                                      
 basic:age            timestamp=1629938998920, value=19                         
 basic:name           timestamp=1629938939646, value=lisi                       
 info:adress          timestamp=1629939024229, value=shanghai                   
 info:gender          timestamp=1629939046173, value=male                       
4 row(s) in 0.0160 seconds

hbase(main):014:0> get 'student','r1','basic'
COLUMN                CELL                                                      
 basic:age            timestamp=1629938768076, value=18                         
 basic:name           timestamp=1629938729561, value=zhangsan                   
2 row(s) in 0.0140 seconds

hbase(main):015:0> get 'student','r1','basic:name','info'
COLUMN                CELL                                                      
 basic:name           timestamp=1629938729561, value=zhangsan                   
 info:adress          timestamp=1629938830720, value=beijing                    
 info:gender          timestamp=1629938871896, value=male                       
3 row(s) in 0.0180 seconds

hbase(main):016:0> scan 'student'
ROW                   COLUMN+CELL                                               
 r1                   column=basic:age, timestamp=1629938768076, value=18       
 r1                   column=basic:name, timestamp=1629938729561, value=zhangsan
 r1                   column=info:adress, timestamp=1629938830720, value=beijing
 r1                   column=info:gender, timestamp=1629938871896, value=male   
 r2                   column=basic:age, timestamp=1629938998920, value=19       
 r2                   column=basic:name, timestamp=1629938939646, value=lisi    
 r2                   column=info:adress, timestamp=1629939024229, value=shangha
                      i                                                         
 r2                   column=info:gender, timestamp=1629939046173, value=male   
2 row(s) in 0.0800 seconds

hbase(main):017:0> scan 'student',{COLUMNS=>['basic']}
ROW                   COLUMN+CELL                                               
 r1                   column=basic:age, timestamp=1629938768076, value=18       
 r1                   column=basic:name, timestamp=1629938729561, value=zhangsan
 r2                   column=basic:age, timestamp=1629938998920, value=19       
 r2                   column=basic:name, timestamp=1629938939646, value=lisi    
2 row(s) in 0.0210 seconds

hbase(main):018:0> scan 'student',{COLUMNS=>['basic:name','info:gender']}
ROW                   COLUMN+CELL                                               
 r1                   column=basic:name, timestamp=1629938729561, value=zhangsan
 r1                   column=info:gender, timestamp=1629938871896, value=male   
 r2                   column=basic:name, timestamp=1629938939646, value=lisi    
 r2                   column=info:gender, timestamp=1629939046173, value=male   
2 row(s) in 0.0220 seconds

hbase(main):019:0> put 'student','r1','basic:name','mayun'
0 row(s) in 0.0140 seconds

hbase(main):020:0> get 'student','r1'
COLUMN                CELL                                                      
 basic:age            timestamp=1629938768076, value=18                         
 basic:name           timestamp=1629939276476, value=mayun                      
 info:adress          timestamp=1629938830720, value=beijing                    
 info:gender          timestamp=1629938871896, value=male                       
4 row(s) in 0.0150 seconds

hbase(main):021:0> put 'student','r1','basic:name','xiyangyang'
0 row(s) in 0.0060 seconds

hbase(main):022:0> get 'student','r1'
COLUMN                CELL                                                      
 basic:age            timestamp=1629938768076, value=18                         
 basic:name           timestamp=1629939305222, value=xiyangyang                 
 info:adress          timestamp=1629938830720, value=beijing                    
 info:gender          timestamp=1629938871896, value=male                       
4 row(s) in 0.0190 seconds

hbase(main):023:0> scan 'student',{RAW=>true,VERSIONS=>3}
ROW                   COLUMN+CELL                                               
 r1                   column=basic:age, timestamp=1629938768076, value=18       
 r1                   column=basic:name, timestamp=1629939305222, value=xiyangya
                      ng                                                        
 r1                   column=basic:name, timestamp=1629939276476, value=mayun   
 r1                   column=basic:name, timestamp=1629938729561, value=zhangsan
 r1                   column=info:adress, timestamp=1629938830720, value=beijing
 r1                   column=info:gender, timestamp=1629938871896, value=male   
 r2                   column=basic:age, timestamp=1629938998920, value=19       
 r2                   column=basic:name, timestamp=1629938939646, value=lisi    
 r2                   column=info:adress, timestamp=1629939024229, value=shangha
                      i                                                         
 r2                   column=info:gender, timestamp=1629939046173, value=male   
2 row(s) in 0.0420 seconds

hbase(main):024:0> drop 'student'

ERROR: Table student is enabled. Disable it first.

Here is some help for this command:
Drop the named table. Table must first be disabled:
  hbase> drop 't1'
  hbase> drop 'ns1:t1'


hbase(main):025:0> disable 'student'
0 row(s) in 1.2880 seconds

hbase(main):026:0> drop 'student'
0 row(s) in 0.1870 seconds

hbase(main):027:0> 

五、API操作

(一)新建java工程,导入包

在这里插入图片描述
将下述lib文件夹中的所有jar包复制到上面的jars中,并build path
在这里插入图片描述

(二)create table

// 创建表
	@Test
	public void createTable() throws MasterNotRunningException, ZooKeeperConnectionException, IOException {
		// --获取配置对象
		Configuration conf = HBaseConfiguration.create();
		// --配置zookeeper的链接地址--编写hbase-site.xml
		conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
		// --获取hbase的管理对象并连接
		HBaseAdmin admin=new HBaseAdmin(conf);
		// --指定表名
		HTableDescriptor table = new HTableDescriptor(TableName.valueOf("student"));
		// --指定列族名
		HColumnDescriptor basic = new HColumnDescriptor("basic");
		HColumnDescriptor info = new HColumnDescriptor("info");
		// --设置列族存储的数据历史版本上限
		basic.setMaxVersions(5);
		info.setMaxVersions(3);
		// --将列族添加到表中
		table.addFamily(basic);
		table.addFamily(info);
		// --创建表
		admin.createTable(table);
		// --关闭连接
		admin.close();
	}

在这里插入图片描述

(三)添加数据与批量添加数据

//--添加数据
	@Test
	public void addDate() throws IOException{
		// --获取配置对象
		Configuration conf = HBaseConfiguration.create();
		// --配置zookeeper的链接地址--编写hbase-site.xml
		conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
		// --获取表-org.apache.hadoop.hbase.client.HTable;
		HTable table = new HTable(conf, "student");
		// --添加行键-org.apache.hadoop.hbase.client.Put;
		Put put = new Put("r1".getBytes());
		// --向指定的列族中添加列值
		put.add("basic".getBytes(),"name".getBytes(),"jerry".getBytes());
		put.add("info".getBytes(),"address".getBytes(),"beijing".getBytes());
		// --将数据添加到table中
		table.put(put);
		// --关闭流
		table.close();
	}
	// --添加一百万条数据
	@Test
	public void addDate2() throws IOException{
		// --获取配置对象
		Configuration conf = HBaseConfiguration.create();
		// --配置zookeeper的链接地址--编写hbase-site.xml
		conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
		// --获取表-org.apache.hadoop.hbase.client.HTable;
		HTable table = new HTable(conf, "student");
		long begin = System.currentTimeMillis();
		List<Put> list = new ArrayList<>();
		for (int i = 1;i <= 100000;i++) {
			Put put = new Put((""+i).getBytes());
			put.add("basic".getBytes(),"id".getBytes(),("id~"+i).getBytes());
			list.add(put);
			if(i%10000==0) {
				table.put(list);
				list = new ArrayList();
			}

		}
		long end =  System.currentTimeMillis();
		System.out.println(end-begin);
		// --关流	
		table.close();
	}

在Xshell中验证,执行:

hbase(main):004:0> get 'student','r1'
COLUMN                                     CELL                                                                                                                      
 basic:name                                timestamp=1629948212343, value=jerry                                                                                      
 info:address                              timestamp=1629948212343, value=beijing                                                                                    
2 row(s) in 0.2640 seconds

hbase(main):005:0>  scan 'student'

(四)获取数据

//获取数据 
	@Test
	public void get() throws IOException {
		// --获取配置对象
		Configuration conf = HBaseConfiguration.create();
		// --配置zookeeper的链接地址--编写hbase-site.xml
		conf.set("hbase.zookeeper.quorum", "192.168.232.129:2181,192.168.232.130:2181,192.168.232.131:2181");
		// --获取表-org.apache.hadoop.hbase.client.HTable;
		HTable table = new HTable(conf, "student");
		// --获取一条数据
		Get get = new Get("9995".getBytes());
		// 获取结果
		// --根据行键获取列族对应的列以及值
		Result rs = table.get(get);
		byte[] data = rs.getValue("basic".getBytes(), "id".getBytes());
		System.out.println(new String(data));
		// 关流
		table.close();
		
	}

在这里插入图片描述

(五)获取结果集

// 获取结果集
	@Test
	public void scan() throws IOException {
		// --获取配置对象
		Configuration conf = HBaseConfiguration.create();
		// --配置zookeeper的链接地址--编写hbase-site.xml
		conf.set("hbase.zookeeper.quorum", "192.168.232.129:2181,192.168.232.130:2181,192.168.232.131:2181");
		// --获取表-org.apache.hadoop.hbase.client.HTable;
		HTable table = new HTable(conf, "student");
		// --需要一个扫描器
		Scan s = new Scan("9999".getBytes());
		// --所有的结果集
		ResultScanner rs = table.getScanner(s);
		// -将结果集转化成迭代器进行遍历
		Iterator<Result> iterator = rs.iterator();
		// 迭代遍历
		while (iterator.hasNext()) {
			// --拿到具体的某一个数据
			Result result = iterator.next();
			byte[] datas = result.getValue("basic".getBytes(), "id".getBytes());
			System.out.println(new String(datas));
		}
		// --关流
		table.close();
	}

在这里插入图片描述

(六)过滤器

//过滤器
//查询含9597的
	@Test
	public void scanDataWithRegex() throws IOException {
		// --获取配置对象
		Configuration conf = HBaseConfiguration.create();
		// --配置zookeeper的链接地址--编写hbase-site.xml
		conf.set("hbase.zookeeper.quorum", "192.168.232.129:2181,192.168.232.130:2181,192.168.232.131:2181");
		// --获取表-org.apache.hadoop.hbase.client.HTable;
		HTable table = new HTable(conf, "student");
		//--获取扫描器
		Scan s = new Scan();
		//--正则过滤器,匹配行键为9的数据
		Filter filter = new RowFilter(CompareOp.EQUAL,new RegexStringComparator(".*9597.*"));
		// --给扫描器添加过滤器
		s.setFilter(filter);
		ResultScanner scanner = table.getScanner(s);
		Iterator<Result> iterator = scanner.iterator();
		// 迭代遍历
		while (iterator.hasNext()) {
			Result result = iterator.next();
			byte[] datas = result.getValue("basic".getBytes(), "id".getBytes());
			System.out.println(new String(datas));
		}
		// 关流
		table.close();
	}
	//查询小于等于1000的
	@Test
	public void lessData() throws IOException {
		// --获取配置对象
		Configuration conf = HBaseConfiguration.create();
		// --配置zookeeper的链接地址--编写hbase-site.xml
		conf.set("hbase.zookeeper.quorum", "192.168.232.129:2181,192.168.232.130:2181,192.168.232.131:2181");
		// --获取表-org.apache.hadoop.hbase.client.HTable;
		HTable table = new HTable(conf, "student");
		//--获取扫描器
		Scan s = new Scan();
		// --行键比较过滤器,匹配小于或等于1000行键的数据
		Filter filter = new RowFilter(CompareOp.LESS_OR_EQUAL, new BinaryComparator("1000".getBytes()));
		s.setFilter(filter);
		ResultScanner scanner = table.getScanner(s);
		Iterator<Result> iterator = scanner.iterator();
		while (iterator.hasNext()) {
			Result result = iterator.next();
			byte[] datas = result.getValue("basic".getBytes(), "id".getBytes());
			System.out.println(new String(datas));
		}
		table.close();
	}
	//查询以3754开头的
	@Test
	public void preData() throws IOException {
		// --获取配置对象
		Configuration conf = HBaseConfiguration.create();
		// --配置zookeeper的链接地址--编写hbase-site.xml
		conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
		// --获取表-org.apache.hadoop.hbase.client.HTable;
		HTable table = new HTable(conf, "student");
		// --获取扫描器
		Scan s = new Scan();
		// --行键前缀过滤器
		Filter filter = new PrefixFilter("3754".getBytes());
		s.setFilter(filter);
		ResultScanner scanner = table.getScanner(s);
		Iterator<Result> iterator = scanner.iterator();
		while (iterator.hasNext()) {
			Result result = iterator.next();
			byte[] datas = result.getValue("basic".getBytes(), "id".getBytes());
			System.out.println(new String(datas));
		}
		table.close();
	}

在这里插入图片描述在这里插入图片描述在这里插入图片描述

(七)删除数据

// 删除数据
	@Test
	public void delete() throws IOException {
		// --获取配置对象
		Configuration conf = HBaseConfiguration.create();
		// --配置zookeeper的链接地址--编写hbase-site.xml
		conf.set("hbase.zookeeper.quorum", "192.168.232.129:2181,192.168.232.130:2181,192.168.232.131:2181");
		// --获取表-org.apache.hadoop.hbase.client.HTable;
		HTable table = new HTable(conf, "student");
		// 进行删除
		Delete del = new Delete("1000".getBytes());
		table.delete(del);
		// 关流
		table.close();
	}

(八)删除表

//删除表
	@Test
	public void deleteTable() throws IOException{
		// --获取配置对象
		Configuration conf = HBaseConfiguration.create();
		// --配置zookeeper的链接地址--编写hbase-site.xml
		conf.set("hbase.zookeeper.quorum", "192.168.232.129:2181,192.168.232.130:2181,192.168.232.131:2181");
		// --获取管理权限,hbase表管理员
		HBaseAdmin admin = new HBaseAdmin(conf);
		// --禁用表
		admin.disableTable("student".getBytes());
		// --删除表
		admin.deleteTable("student".getBytes());
		// 关流
		admin.close();
	}

六、原理

(一)物理存储原理

(Ⅰ)概述

1、Hbase里的一个Table 在行的方向上分割为一个或者多个HRegion,即HBase中一个表的数据会被划分成一个或者很多的HRegion,每一个HRegion会被存储在一个节点上。这样做的目的是为了能做到数据的分布式存储
2、HRegion可以动态扩展(即增加或者删除节点,并且会进行分裂)并且HBase保证HRegion的负载均衡**(Hmaster)**
3、HRegion实际上是行键排序(默认是字典排序)后的按规则分割的连续的存储空间
在这里插入图片描述
4、一张Hbase表,可能有多个HRegion,每个HRegion达到一定大小(默认是10GB)时,进行分裂。
在这里插入图片描述

(Ⅱ)拆分流程

1、HRegion是按大小分割的,每个表一开始只有一个HRegion,随着数据不断插入表,HRegion不断增大,当增大到一个阀值的时候,HRegion就会等分两个新的等大的HRegion。因此随着table中的行不断增多,就会有越来越多的HRegion
2、按照现在主流硬件的配置,每个HRegion的大小可以是1~20GB。这个大小由hbase.hregion.max.filesize指定,默认为10GB
3、HRegion的拆分和转移是由HBase(HMaster)自动完成的,用户感知不到
4、HRegion是Hbase中分布式存储和负载均衡的最小单元
在这里插入图片描述
5、HRegion虽然是分布式存储的最小单元,但并不是存储(数据)的最小单元
6、HRegion由一个或者多个HStore组成,每个HStore保存一个columns family
7、每个HStore又由一个memStore(写缓存,默认是128M)以及0个或者多个StoreFile组成,StoreFile以HFile格式保存在HDFS上
8、总结:HRegion是分布式的存储最小单位,StoreFile(Hfile)是存储最小单位
在这里插入图片描述

(二)系统架构

(Ⅰ)Hbase与Hadoop架构图

在这里插入图片描述

(Ⅱ)HBase架构组成

1、HBase采用Master/Slave(主从)架构搭建集群,它隶属于Hadoop生态系统,由以下类型节点组成:
a. HMaster节点:
i. 管理HRegionServer,实现其负载均衡,保证每一个HRegionServer上的数据量差别不大(用户透明)
ii. 管理和分配HRegion,比如在HRegion分裂时分配新的HRegion;在HRegionServer退出时迁移其内的HRegion到其他HRegionServer上
iii. 实现DDL操作(Data Definition Language,namespace和table的增删改,column familiy的增删改等)
iv. 管理namespace和table的元数据(实际存储在HDFS上)
v. 权限控制(ACL)
b. HRegionServer节点:
i. 存放和管理本地HRegion
ii. 读写HDFS,管理Table中的数据
iii. Client直接通过HRegionServer读写数据(从HMaster中获取元数据,找到RowKey所在的HRegion/HRegionServer后)
c. ZooKeeper集群
i. 存放整个 HBase集群的元数据、集群的状态信息以及RS服务器的运行状态
ii. 实现HMaster主备节点的failover
d. HBase的数据存储于HDFS中,因而涉及到HDFS的NameNode、DataNode等。RegionServer和DataNode一般会放在相同的Server上实现数据的本地化(避免或减少数据在网络中的传输,节省带宽)。
2、下图更全面展示了Hbase于Hadoop的体系图
在这里插入图片描述
3、HBase Client通过RPC方式和HMaster、HRegionServer通信;一个HRegionServer可以存放1000个HRegion(1000个数字的由来是来自于Google的Bigtable论文);底层Table数据存储于HDFS中,而HRegion所处理的数据尽量和数据所在的DataNode在一起,实现数据的本地化;数据本地化并不是总能实现,比如在HRegion移动(如因Split)时,需要等下一次Compact才能继续回到本地化

(三)架构原理详解

(Ⅰ)HRegion

在这里插入图片描述
1、HBase使用RowKey将表水平切割成多个HRegion
2、从HMaster的角度,每个HRegion都纪录了它的StartKey和EndKey
3、由于RowKey是排序的,因而Client可以通过HMaster快速的定位每个RowKey在哪个HRegion中
4、HRegion由HMaster分配到相应的HRegionServer中,然后由HRegionServer负责HRegion的启动和管理、和Client的通信以及负责数据的读(使用HDFS)
5、每个HRegionServer可以同时管理1000个左右的HRegion

(Ⅱ)HMaster

在这里插入图片描述
1、 HMaster没有单点故障问题,因为在HBase集群中可以启动多个HMaster
2、通过ZooKeeper的Master Election机制保证同时只有一个HMaster处于Active状态,其他的HMaster则处于热备份状态
3、一般情况下会启动两个HMaster,Backup的HMaster会定期的和Active HMaster通信以获取其最新状态,从而保证它是实时更新的,因而如果启动了多个HMaster反而增加了Active HMaster的负担
4、HMaster的主要用于HRegion的分配和管理,DDL(Data Definition Language,既Table的新建、删除、修改等)的实现等,既它主要有两方面的职责:
a. 协调HRegionServer:
i. 启动时HRegion的分配,以及负载均衡和修复时HRegion的重新分配
ii. 监控集群中所有HRegionServer的状态(通过Heartbeat和监听ZooKeeper中的状态)
b. Admin职能
i. 管理创建、删除、修改Table的操作

(Ⅲ)ZooKeeper:协调者

在这里插入图片描述
1、ZooKeeper为HBase集群提供协调服务,它管理着HMaster和HRegionServer的状态(available/alive等)
2、ZooKeeper协调集群所有节点的共享信息,在HMaster和HRegionServer连接到ZooKeeper后创建Ephemeral( 临时)节点,并使用Heartbeat机制维持这个节点的存活状态,如果某个Ephemeral节点实效,则HMaster会收到通知,并做相应的处理
3、当HMaster宕机的时候实现HMaster之间的failover(失败恢复)
4、在HRegionServer宕机时通知给HMaster,从而对宕机的HRegionServer中的HRegion集合的修复(将它们分配给其他的HRegionServer)
在这里插入图片描述
5、另外,HMaster通过监听ZooKeeper中的Ephemeral节点(默认:/hbase/rs/*)来监控HRegionServer的加入和宕机。在第一个HMaster连接到ZooKeeper时会创建Ephemeral节点(默认:/hbasae/master)来表示Active的HMaster,其后加进来的HMaster则监听该Ephemeral节点,如果当前Active的HMaster宕机,则该节点消失,因而其他HMaster得到通知,而将自身转换成Active的HMaster,在变为Active的HMaster之前,它会创建在/hbase/back-masters/下创建自己的Ephemeral节点。

(Ⅳ)HBase的第一次读写

1、在HBase 0.96以前,HBase有两个特殊的Table:-ROOT-和.META.,
a. -ROOT- 表的存储位置存储在ZooKeeper,它存储了.META.表的RegionInfo信息,并且它只能存在一个HRegion
b. .META. 表则存储了用户定义的Table的RegionInfo信息,它可以被切分成多个HRegion
c. 第一次访问Table时,首先从ZooKeeper中读取-ROOT- 表所在HRegionServer;然后从该HRegionServer中根据请求的TableName,RowKey读取.META. 表所在HRegionServer;最后从该HRegionServer中读取.META. 表的内容而获取此次请求需要访问的HRegion所在的位置,然后访问该HRegionSever获取请求的数据,这需要三次请求才能找到用户Table所在的位置,然后第四次请求开始获取真正的数据。当然为了提升性能,客户端会缓存-ROOT- Table位置以及-ROOT-/.META. Table的内容
在这里插入图片描述

		a. 可是即使客户端有缓存,在初始阶段需要三次请求才能获取到用户自定义的Table真正所在的位置

2、在HBase 0.96以后去掉了-ROOT- Table,只剩下这个特殊的目录表叫做Meta Table(hbase:meta),它存储了集群中所有用户HRegion的位置信息,而ZooKeeper的节点中(/hbase/meta-region-server)存储的则直接是这个Meta Table的位置,并且这个Meta Table如以前的-ROOT- Table一样是不可split的。这样,客户端在第一次访问用户Table的流程是:
a. 从ZooKeeper(/hbase/meta-region-server)中获取hbase:meta的位置(HRegionServer的位置),缓存该位置信息
b. 从HRegionServer中查询用户Table对应请求的RowKey所在的HRegionServer,缓存该位置信息
c. 从查询到HRegionServer中读取Row
3、从这个过程中,客户端会缓存这些位置信息,然而第二步它只是缓存当前RowKey对应的HRegion的位置,因而如果下一个要查的RowKey不在同一个HRegion中,则需要继续查询hbase:meta所在的HRegion,然而随着时间的推移,客户端缓存的位置信息越来越多,以至于不需要再次查找hbase:meta Table的信息
4、当某个HRegion因为宕机或Split被移动,此时需要重新查询并且更新缓存。
在这里插入图片描述

(Ⅴ)HRegionServer详解

在这里插入图片描述
1、HRegionServer一般和DataNode在同一台机器上运行,实现数据的本地性
2、HRegionServer存储和管理多个HRegion,由WAL(HLog)、BlockCache、Region组成:
a. WAL即Write Ahead Log
i. 在早期版本中称为HLog,它是HDFS上的一个文件,所有写操作都会先保证将写操作写入这个Log文件后,才会真正更新MemStore,最后写入HFile中
ii. 采用这种模式,可以保证HRegionServer宕机后,依然可以从该Log文件中恢复数据,重新执行所有的操作,而不至于数据丢失
iii. HLog文件就是一个普通的Hadoop Sequence File,Sequence File 的Key是HLogKey对象,HLogKey中记录了写入数据的归属信息,除了table和region名字外,同时还包括 sequence number和timestamp,timestamp是”写入时间”,sequence number的起始值为0,或者是最近一次存入文件系统中sequence number。HLog Sequece File的Value是HBase的KeyValue对象,即对应HFile中的KeyValue
iv. 这个Log文件会定期Roll出新的文件而删除旧的文件(那些已持久化到HFile中的Log可以删除)。WAL文件存储在/hbase/WALs/${HRegionServer_Name}的目录中(在0.94之前,存储在/hbase/.logs/目录中),一般一个HRegionServer只有一个WAL实例,也就是说一个HRegionServer的所有WAL写都是串行的(就像log4j的日志写也是串行的)
v. 一个RS服务器只有一个HLOG文件,在0.94版本之前,写HLOG的操作是串行的,所以效率很低,所以1.0版本之后,Hbase引入多管道并行写技术,从而提高性能
b. BlockCache是一个读缓存
i. 采用“引用局部性”原理(也应用于CPU,分空间局部性和时间局部性,空间局部性是指CPU在某一时刻需要某个数据,那么有很大的概率在一下时刻它需要的数据在其附近;时间局部性是指某个数据在被访问过一次后,它有很大的概率在不久的将来会被再次的访问),将数据预读取到内存中,以提升读的性能
ii. 这样设计的目的是为了提高读缓存的命中率
iii. HBase中默认采用on-heap LruBlockCache策略(LRU -evicted,是一种数据的回收策略, LRU– 最近最少使用的:移除最长时间不被使用的对象)
c. HRegion是一个Table中的一个Region在一个HRegionServer中的表达
i. 一个Table可以有一个或多个HRegion,它们可以在一个相同的HRegionServer上,也可以分布在不同的HRegionServer上
ii. 一个HRegionServer可以有多个HRegion,他们分别属于不同的Table
iii. HRegion由一个或者多个Store(HStore)构成,每个HStore对应了一个Table在这个HRegion中的一个Column Family,即每个Column Family就是一个集中的存储单元,因而最好将具有相近I/O特性的Column存储在一个Column Family,以实现高效读取(数据局部性原理,可以提高缓存的命中率)。HStore是HBase中存储的核心,它实现了读写HDFS功能,一个HStore由一个MemStore 和0个或多个StoreFile组成
iv. MemStore是一个写缓存(In Memory Sorted Buffer),所有数据的写在完成WAL日志写后,会写入MemStore中,由MemStore根据一定的算法(LSM-TREE算法,这个算法的作用是将数据顺序写入磁盘,而不是随机写,减少磁头调度时间,从而提高写入性能)将数据Flush到底层的HDFS文件中(HFile),通常每个HRegion中的每个 Column Family有一个自己的MemStore
v. HFile(StoreFile) 用于存储HBase的数据(Cell/KeyValue)。在HFile中的数据是按RowKey、Column Family、Column排序,对相同的Cell(即这三个值都一样),则按timestamp倒序排列(即最新的数据在最前面)
3、因为Hbase的HFile是存到HDFS上,所以Hbase实际上是具备数据的副本冗余机制的

(四)HBASE写流程

1、当客户端发起一个Put请求时,首先它从hbase:meta表中查出该Put数据最终需要去的HRegionServer。然后客户端将Put请求发送给相应的HRegionServer,在HRegionServer中它首先会将该Put操作写入WAL日志文件中(Flush到磁盘中)
在这里插入图片描述
2、写完WAL日志文件后,然后会将数据写到Memstore,在Memstore按Rowkey排序,以及用LSM-TREE对数据做合并处理。HRegionServer根据Put中的TableName和RowKey找到对应的HRegion,并根据Column Family找到对应的HStore,并将Put写入到该HStore的MemStore中。此时写成功,并返回通知客户端
在这里插入图片描述
3、MemStore是一个In Memory Sorted Buffer,在每个HStore中都有一个MemStore,一个HRegion的一个Column Family对应一个HStore实例。在MemStore中,数据的排列顺序以RowKey、Column Family、Column的顺序以及Timestamp的倒序
在这里插入图片描述
4、每一次Put/Delete请求都是先写入到MemStore中,当MemStore满后会Flush成一个新的StoreFile(底层实现是HFile),即一个HStore(Column Family)可以有0个或多个StoreFile(HFile)。有以下三种情况可以触发MemStore的Flush动作:
a. 当一个HRegion中的MemStore的大小超过了:hbase.hregion.memstore.flush.size的大小,默认128MB,此时当前的MemStore会Flush到HFile中
b. 当RS服务器上所有的MemStore的大小超过了:hbase.regionserver.global.memstore.upperLimit的大小,默认35%的内存使用量,此时当前HRegionServer中所有HRegion中的MemStore可能都会Flush。一般从最大的Memostore开始flush
c. 当前HRegionServer中WAL的大小超过了 1GB。当前HRegionServer中所有HRegion中的MemStore都会Flush,这里指的是两个参数相乘的大小
5、此外,在MemStore Flush过程中,还会在尾部追加一些meta数据,其中就包括Flush时最大的WAL sequence值,以告诉HBase这个StoreFile写入的最新数据的序列,那么在Recover时就知道从哪里开始。在HRegion启动时,这个sequence会被读取,并取最大的作为下一次更新时的起始sequence
在这里插入图片描述

6、HBase的数据以KeyValue(Cell)的形式顺序的存储在HFile中,在MemStore的Flush过程中生成HFile,由于MemStore中存储的Cell遵循相同的排列顺序,因而Flush过程是顺序写,而磁盘的顺序写性能很高,因为不需要不停的移动磁盘指针。
在这里插入图片描述

(五)HBASE读的实现

(Ⅰ)流程

1、首先对新写入的Cell,它会存在于MemStore中;然后对之前已经Flush到HFile中的Cell,它会存在于某个或某些StoreFile(HFile)中;最后,对刚读取过的Cell,它可能存在于BlockCache中
2、既然相同的Cell可能存储在三个地方,在读取的时候只需要扫瞄这三个地方,然后将结果合并即可(Merge Read),在HBase中扫瞄的顺序依次是:BlockCache、MemStore、StoreFile(HFile)(这个扫描顺序的目的也是为了减少磁盘的I/O次数)
3、其中StoreFile的扫瞄先会使用Bloom Filter(布隆过滤算法)过滤那些不可能符合条件的HFile,然后使用Block Index快速定位Cell,并将其加载到BlockCache中,然后从BlockCache中读取。
4、一个HStore可能存在多个StoreFile(HFile),此时需要扫瞄多个HFile,如果HFile过多又是会引起性能问题
在这里插入图片描述

(Ⅱ)Compaction机制

1、MemStore每次Flush会创建新的HFile,而过多的HFile会引起读的性能问题。为了解决这个问题,HBase采用Compaction机制来解决这个问题。在HBase中Compaction分为两种:Minor Compaction和Major Compaction
2、Minor Compaction是指选取一些小的、相邻的StoreFile将他们合并成一个更大的StoreFile,在这个过程中不会处理已经Deleted或Expired的Cell。一次Minor Compaction的结果是减少Store File的数量但是产生更大的StoreFile
在这里插入图片描述
3、Major Compaction是指将所有的StoreFile合并成一个StoreFile,在这个过程中,标记为Deleted的Cell会被删除,而那些已经Expired的Cell会被丢弃,那些已经超过最多版本数的Cell会被丢弃。一次Major Compaction的结果是一个HStore只有一个StoreFile存在。Major Compaction可以手动或自动触发,然而由于它会引起很多的I/O操作而引起性能问题,因而它一般会被安排在周末、凌晨等集群比较闲的时间
在这里插入图片描述
4、实现Compaction的方式有2种:
a. 通过API:
// minor compact
admin.compact(“tab2”.getBytes());
// major compact
admin.majorCompact(“tab2”.getBytes());
b. 通过指令:
compact(‘tab2’) # minor compact
major_compact(‘tab2’) # major compact
5、Hbase默认用的是Minor compaction。之所以默认不用Major Compaction的原因是在于,Major Compaction可能会代理大量的磁盘I/O,从而阻塞Hbase其他的读写操作。所以对于Major Compactoin,一般选择在业务峰值低的时候执行

(六)BloomFilter

(Ⅰ)背景说明

1、Hash 函数在计算机领域,尤其是数据快速查找领域,加密领域用的极广
2、作用是将一个大的数据集映射到一个小的数据集上面(这些小的数据集叫做哈希值,或者散列值)
3、Hash table(散列表,也叫哈希表),是根据哈希值(Key value)而直接进行访问的数据结构。也就是说,它通过把哈希值映射到表中一个位置来访问记录,以加快查找的速度。下面是一个典型的示意图:
在这里插入图片描述
4、这种简单的Hash Table存在一定的问题,就是Hash冲突的问题。假设 Hash 函数是良好的,如果位阵列长度为 m 个点,那么如果想将冲突率降低到例如 1%, 这个散列表就只能容纳 m * 1% 个元素。显然这就不叫空间有效了(Space-efficient)。

(Ⅱ)Bloom Filter概述

1、Bloom Filter是1970年由布隆(Burton Howard Bloom)提出的
2、它实际上是一个很长的二进制向量和一系列随机映射函数(Hash函数)
3、布隆过滤器可以用于检索一个元素是否在一个集合中
4、它的优点是空间效率和查询时间都远远超过一般的算法
5、Bloom Filter广泛的应用于各种需要查询的场合中,如:Google 著名的分布式数据库 Bigtable 使用了布隆过滤器来查找不存在的行或列,以减少磁盘查找的IO次数
6、在很多Key-Value系统中也使用了布隆过滤器来加快查询过程,如 Hbase,Accumulo,Leveldb,一般而言,Value 保存在磁盘中,访问磁盘需要花费大量时间,然而使用布隆过滤器可以快速判断某个Key对应的Value是否存在,因此可以避免很多不必要的磁盘IO操作,只是引入布隆过滤器会带来一定的内存消耗

(Ⅲ)Bloom Filter 原理

1、如果想判断一个元素是不是在一个集合里,一般想到的是将所有元素保存起来,然后通过比较确定,链表,树等等数据结构都是这种思路.。但是随着集合中元素的增加,需要的存储空间越来越大,检索速度也越来越慢。
2、一个Bloom Filter是基于一个m位的位向量(b1,…bm),这些位向量的初始值为0。另外,还有一系列的hash函数(h1,…hk)(默认是3个哈希函数),这些hash函数的值域属于1~m。下图是一个bloom filter插入x,y,z并判断某个值w是否在该数据集的示意图:
在这里插入图片描述
3、布隆过滤器的缺点和优点一样明显。误算率(False Positive)是其中之一。随着存入的元素数量增加,误算率随之增加。但是如果元素数量太少,则使用散列表足矣gesge
4、总结:Bloom Filter 通常应用在一些需要快速判断某个元素是否属于集合,但是并不严格要求100%正确的场合。此外,引入布隆过滤器会带来一定的内存消耗

七、扩展(HBASE表设计)

(一)Rowkey设计

(Ⅰ)概述

1、Rowkey是不可分割的字节数,按字典排序由低到高存储在表中
2、在设计HBase表时,Rowkey设计是最重要的事情,应该基于预期的访问模式来为Rowkey建模。Rowkey决定了访问HBase表时可以得到的性能,原因有两个:
a. Region基于Rowkey为一个区间的行提供服务,并且负责区间的每一行
b. HFile在硬盘上存储有序的行
3、这两个因素是相互关联的。当Region将内存中数据刷写为HFile时,这些行已经排过序,也会有序地写到硬盘上。Rowkey的有序特性和底层存储格式可以保证HBase表在设计Rowkey之后的良好性能
4、关系型数据库可以在多列上建立索引,但是HBase只能在Rowkey上建立索引。(可以通过ES为Hbase的列建立索引)

(Ⅱ)设计方式

1、将Rowkey以字典顺序从大到小排序
原生HBase只支持从小到大的排序,但是现在有个需求想展现影片热度排行榜,这就要求实现从大到小排列,针对这种情况可以采用Rowkey=Integer.MAX_VALUE-Rowkey的方式将Rowkey进行转换,最大的变最小,最小的变最大,在应用层再转回来即可完成排序需求
2、RowKey尽量散列设计
最重要的是要保证散列,这样就会保证所有的数据都不是在一个Region上,从而避免读写的时候负载会集中在个别Region上。比如ROWKEY_Random
3、RowKey的长度尽量短
如果Rowkey太长,第一存储开销会增加,影响存储效率;第二内存中Rowkey字段过长,会导致内存的利用率降低,进而降低索引命中率
Rowkey是一个二进制码流,Rowkey的长度被很多开发者建议说设计在10~100个字节,不过建议是越短越好,不要超过16个字节
原因如下:
a. 数据的持久化文件HFile中是按照KeyValue存储的,如果Rowkey过长比如100个字节,1000万列数据光Rowkey就要占用100*1000万=10亿个字节,将近1G数据,这会极大影响HFile的存储效率
b. MemStore将缓存部分数据到内存,如果Rowkey字段过长内存的有效利用率会降低,系统将无法缓存更多的数据,这会降低检索效率。因此Rowkey的字节长度越短越好
4、RowKey唯一
5、RowKey建议用String类型
虽然行键在HBase中是以byte[]字节数组的形式存储的,但是建议在系统开发过程中将其数据类型设置为String类型,保证通用性。
常用的行键字符串有以下几种:
a. 纯数字字符串,譬如9559820140512
b. 数字+特殊分隔符,譬如95598-20140512
c. 数字+英文字母,譬如city20140512
d. 数字+英文字母+特殊分隔符,譬如city_20140512
6、RowKey设计得最好有意义
RowKey的主要作用是为了进行数据记录的唯一性标示,但是唯一性并不是其全部,具有明确意义的行键对于应用开发、数据检索等都具有特殊意义,譬如数字字符串:9559820140512,其实际意义是这样:95598(电网客服电话)+20140512(日期)
行键往往由多个值组合而成,而各个值的位置顺序将影响到数据存储和检索效率,所以在设计行键时,需要对日后的业务应用开发有比较深入的了解和前瞻性预测,才能设计出可尽量高效率检索的行键
7、具有定长性
行键具有有序性的基础便是定长,譬如20140512080500、20140512083000,这两个日期时间形式的字符串是递增的,不管后面的秒数是多少,我们都将其设置为14位数字形式,如果我们把后面的0去除了,那么201405120805将大于20140512083,其有序性发生了变更。所以行键一定要设计成定长的
此外,目前操作系统是都是64位系统,内存8字节对齐。控制在16个字节,8字节的整数倍利用操作系统的最佳特性

(二)列族的设计

1、在设计hbase表时候,列族不宜过多,尽量的要少使用列族
2、经常要在一起查询的数据最好放在一个列族中,尽量的减少跨列族的数据访问。

(三)案例

案例一 网络数据访问
数据库里有两张表:
用户表
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
行键设计
行键怎么设计,直接决定了查询语句怎么写,如果将行键设计为:日期_姓名这种形式:
2016-12-20_zhang
2016-11-10_zhang
2016-11-09_zhang
2016-12-20_wang
2016-12-20_wang
则在查询时,可以利用hbase提供的过滤器进行日期范围的查询:
Filter filter=new PrefixFilter(“2016-12-20”.getBytes());
也可以查询以人名为过滤条件的查询
所以在hbase中,行键的设计很重要,要结合具体业务。

八、Phoenix

(一)Phoenix介绍和安装

(Ⅰ)概述

1、HBase基础上架构的SQL中间件,使得可以通过SQL/JDBC来操作HBase

(Ⅱ)安装

1、上传/下载Phoenix安装包到linux服务器并解压
(1)上传
在这里插入图片描述
(2)解压并重命名

[root@hadoop01 software]# tar -zxvf apache-phoenix-4.8.1-HBase-0.98-bin.tar.gz 
root@hadoop01 software]# mv apache-phoenix-4.8.1-HBase-0.98-bin phoenix-4.8.1

这台linux服务器最好是Hbase Master节点。所以,如果是Hbase集群,则不需要在全部的服务节点上来安装Phoenix,只需要在HMaster节点上安装即可
2、 将Phoneix安装目录下的两个jar包,拷贝到Hbase安装目录下的lib目录

[root@hadoop01 phoenix-4.8.1]# cd /home/software/phoenix-4.8.1/
[root@hadoop01 phoenix-4.8.1]# cp phoenix-4.8.1-HBase-0.98-server.jar /home/software/hbase/lib/
[root@hadoop01 phoenix-4.8.1]# cd /home/software/phoenix-4.8.1/
[root@hadoop01 phoenix-4.8.1]# cp phoenix-4.8.1-HBase-0.98-client.jar /home/software/hbase/lib/

3、在 etc/profile文件中配置Hbase的目录路径

[root@hadoop01 phoenix-4.8.1]# vim /etc/profile
export HBASE_HOME=/home/software/hbase
export PATH=$JAVA_HOME/bin:$HBASE_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$KE_HOME/bin:$PATH
[root@hadoop01 phoenix-4.8.1]# source /etc/profile

4、重新生效,source /etc/profile
5、启动Hadoop、ZK!、HBase集群
6、进入Phoenix安装目录的bin目录
7、执行:./sqlline.py hadoop01,hadoop02,hadoop03:2181
第一次启动失败,解决方法见九(二),然后得以启动成功
在这里插入图片描述
如上图所以,证明Phoenix安装成功
此外,此时进入hbase,执行list查看,会发现多出如下的表:

8. 如果需要kill掉Phoenix进程,则执行: pstree  -p 
查看 py进程,杀掉Sqlline的父进程

(二)Phoenix使用

1、创建表:
create table tab1(id integer primary key,name varchar);

0: jdbc:phoenix:hadoop01,hadoop02,hadoop03:21> create table teacher(id integer primary key,name varchar);
No rows affected (0.562 seconds)
注:
①Phoenix建表必须有声明主键,否则报错
②Phoenix建表的表名,在hbase里的表名是大写的,此外,列名也是大写的。
③这条建表语句,并未声明表的列族,则默认就一个列族,且列族的名字为:0。
④在列族0中,除主键列外,其余的列都属于0列族里的列

2、查看所有表:
!tables

0: jdbc:phoenix:hadoop01,hadoop02,hadoop03:21> !tables
+------------+--------------+-------------+---------------+----------+---------+
| TABLE_CAT  | TABLE_SCHEM  | TABLE_NAME  |  TABLE_TYPE   | REMARKS  | TYPE_NA |
+------------+--------------+-------------+---------------+----------+---------+
|            | SYSTEM       | CATALOG     | SYSTEM TABLE  |          |         |
|            | SYSTEM       | FUNCTION    | SYSTEM TABLE  |          |         |
|            | SYSTEM       | SEQUENCE    | SYSTEM TABLE  |          |         |
|            | SYSTEM       | STATS       | SYSTEM TABLE  |          |         |
|            |              | TEACHER     | TABLE         |          |         |
+------------+--------------+-------------+---------------+----------+---------

在这里插入图片描述

3、插入数据:
upsert into tab1 values(1,‘hello’);
注:字符串类型用 ’ '包起来,不要用“ ”,否则报错。

0: jdbc:phoenix:hadoop01,hadoop02,hadoop03:21> upsert into teacher values(1,'zhangsan');
1 row affected (0.032 seconds)

4、查询数据:
select * from tab1;

0: jdbc:phoenix:hadoop01,hadoop02,hadoop03:21> select * from teacher;
+-----+-----------+
| ID  |   NAME    |
+-----+-----------+
| 1   | zhangsan  |
+-----+-----------+
1 row selected (0.035 seconds)

在这里插入图片描述
5、删除数据:
delete from tab1 where id=2;

0: jdbc:phoenix:hadoop01,hadoop02,hadoop03:21> upsert into teacher values(2,'wangwu');
1 row affected (0.012 seconds)
0: jdbc:phoenix:hadoop01,hadoop02,hadoop03:21> delete from teacher where id=1;
1 row affected (0.012 seconds)
0: jdbc:phoenix:hadoop01,hadoop02,hadoop03:21> select * from teacher;
+-----+---------+
| ID  |  NAME   |
+-----+---------+
| 2   | wangwu  |
+-----+---------+
1 row selected (0.034 seconds)

6、删除表:
drop table tab1;

0: jdbc:phoenix:hadoop01,hadoop02,hadoop03:21> drop table teacher;
No rows affected (1.656 seconds)
0: jdbc:phoenix:hadoop01,hadoop02,hadoop03:21> !tables
+------------+--------------+-------------+---------------+----------+---------+
| TABLE_CAT  | TABLE_SCHEM  | TABLE_NAME  |  TABLE_TYPE   | REMARKS  | TYPE_NA |
+------------+--------------+-------------+---------------+----------+---------+
|            | SYSTEM       | CATALOG     | SYSTEM TABLE  |          |         |
|            | SYSTEM       | FUNCTION    | SYSTEM TABLE  |          |         |
|            | SYSTEM       | SEQUENCE    | SYSTEM TABLE  |          |         |
|            | SYSTEM       | STATS       | SYSTEM TABLE  |          |         |
|            |              | teacher     | TABLE         |          |         |

7、创建小写的表名:
create table “tab2” (id integer primary key,name varchar);
select * from “tab2”;
注:CRUD都以 "tab2"为表名来操作

0: jdbc:phoenix:hadoop01,hadoop02,hadoop03:21> create table "teacher"(id integer primary key,name varchar);
No rows affected (0.363 seconds)
0: jdbc:phoenix:hadoop01,hadoop02,hadoop03:21> !tables
+------------+--------------+-------------+---------------+----------+---------+
| TABLE_CAT  | TABLE_SCHEM  | TABLE_NAME  |  TABLE_TYPE   | REMARKS  | TYPE_NA |
+------------+--------------+-------------+---------------+----------+---------+
|            | SYSTEM       | CATALOG     | SYSTEM TABLE  |          |         |
|            | SYSTEM       | FUNCTION    | SYSTEM TABLE  |          |         |
|            | SYSTEM       | SEQUENCE    | SYSTEM TABLE  |          |         |
|            | SYSTEM       | STATS       | SYSTEM TABLE  |          |         |
|            |              | TEACHER     | TABLE         |          |         |
|            |              | teacher     | TABLE         |          |         |
+------------+--------------+-------------+---------------+----------+---------+
0: jdbc:phoenix:hadoop01,hadoop02,hadoop03:21> upsert into "teacher" values(1,'xiaozhang');
1 row affected (0.014 seconds)
0: jdbc:phoenix:hadoop01,hadoop02,hadoop03:21> select * from "teacher";
+-----+------------+
| ID  |    NAME    |
+-----+------------+
| 1   | xiaozhang  |
+-----+------------+
1 row selected (0.037 seconds)

在这里插入图片描述
8、退出!exit

0: jdbc:phoenix:hadoop01,hadoop02,hadoop03:21> !exit
Closing: org.apache.phoenix.jdbc.PhoenixConnection

九、遇到的问题及解决方法

(一)导入Result包时一直报错

问题描述:在写语句Result rs = table.get(get);时,Result下面一直有红色的下划线,试了好多包都不成功。
解决方法:Ctrl + 1 然后直接回车,使用eclipse自带的快捷键来完成方法返回值的接收

(二)Phoenix启动失败

在这里插入图片描述
解决方法:
(1)停止hbase

[root@hadoop01 zookeeper-3.4.7]# cd /home/software/hbase/bin/
[root@hadoop01 bin]# ./stop-hbase.sh 
stopping hbase.....................

(2)在zookeeper中删除hbase节点

[root@hadoop01 bin]# cd /home/software/zookeeper-3.4.7/
[root@hadoop01 zookeeper-3.4.7]# sh ./bin/zkCli.sh
......
[zk: localhost:2181(CONNECTED) 0] ls /
[park01, cluster, brokers, zookeeper, admin, isr_change_notification, log_dir_event_notification, controller_epoch, consumers, latest_producer_id_block, config, hbase]
[zk: localhost:2181(CONNECTED) 1] rmr /hbase

(3)重启hbase

[root@hadoop01 zookeeper-3.4.7]# cd /home/software/hbase/bin/
[root@hadoop01 bin]# ./start-hbase.sh 
此时jps如下
[root@hadoop01 bin]# jps
2800 NodeManager
2049 QuorumPeerMain
2695 ResourceManager
10664 SqlLine
11177 HMaster
11402 Jps
2236 NameNode
2540 SecondaryNameNode
11311 HRegionServer
2367 DataNode

(4)重新启动Phoenix,结果如下:有86/86 (100%) Done

[root@hadoop01 bin]# cd /home/software/phoenix-4.8.1/bin/
[root@hadoop01 bin]# ./sqlline.py hadoop01,hadoop02,hadoop03:2181
Setting property: [incremental, false]
Setting property: [isolation, TRANSACTION_READ_COMMITTED]
issuing: !connect jdbc:phoenix:hadoop01,hadoop02,hadoop03:2181 none none org.apache.phoenix.jdbc.PhoenixDriver
Connecting to jdbc:phoenix:hadoop01,hadoop02,hadoop03:2181
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/home/software/phoenix-4.8.1/phoenix-4.8.1-HBase-0.98-client.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/home/software/hadoop-2.7.1/share/hadoop/common/lib/slf4j-log4j12-1.7.10.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
21/08/26 15:50:31 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
Connected to: Phoenix (version 4.8)
Driver: PhoenixEmbeddedDriver (version 4.8)
Autocommit status: true
Transaction isolation: TRANSACTION_READ_COMMITTED
Building list of tables and columns for tab-completion (set fastconnect to true to skip)...
86/86 (100%) Done
Done
sqlline version 1.1.9
0: jdbc:phoenix:hadoop01,hadoop02,hadoop03:21> 

十、小技巧

如果调用的方法有返回值,而那我们在自己查看返回值类型的时候有些麻烦;另一件麻烦的是就是要个返回值起名字,这个就很烦了。所以可以直接使用eclipse自带的快捷键来完成方法返回值的接收:Ctrl + 1 然后直接回车

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值