HBase快速入门【集群安装配置、读写过程、表模型、命令行、API】

2021-05-14 17:25:24 浏览数 (1)

HBase官方文档地址:http://hbase.apache.org

一、简单看看HBase与MySQL、Hive的区别

二、什么是HBase

HBASE是一个数据库----可以提供数据的实时随机读写

HBase与MySQL、Oralce、DB2、SQLServer等关系型数据库不同,它是一个NoSQL数据库(非关系型数据库)

  1. HBase的表模型与关系型数据库的表模型不同:
  2. HBase的表没有固定的字段定义;
  3. HBase的表中每行存储的都是一些key-value对
  4. HBase的表中有列族的划分,用户可以指定将哪些kv插入哪个列族
  5. HBase的表在物理存储上,是按照列族来分割的,不同列族的数据一定存储在不同的文件中
  6. HBase的表中的每一行都固定有一个行键,而且每一行的行键在表中不能重复
  7. HBase中的数据,包含行键,包含key,包含value,都是byte[ ]类型,hbase不负责为用户维护数据类型
  8. HBase对事务的支持很差

HBase相比于其他NoSQL数据库(MongoDB、Redis、cassendra、hazelcast)的特点:

HBase的表数据存储在HDFS文件系统中

从而,HBase具备如下特性:存储容量可以线性扩展;数据存储的安全性可靠性极高!

2.1    HBase的表模型

2.2    HBase集群架构简略图

2.3    HBase应用场景举例

三、HBase的安装

3.1    HBase架构安排

HBase是一个分布式系统

其中有一个管理角色:  HMaster(一般2台,一台active,一台backup)

其他的数据节点角色:  HRegionServer(很多台,看数据容量)

首先,要有一个HDFS集群,并正常运行;RegionServer应该跟HDFS中的DataNode在一起

其次,还需要一个zookeeper集群,并正常运行

然后,安装HBASE

角色分配如下:

代码语言:javascript复制
hdp-01: namenode  datanode  regionserver hmaster  zookeeper
hdp-02: datanode   regionserver  zookeeper
hdp-03: datanode   regionserver  zookeeper

3.2    HBase安装与配置

解压HBase安装包

修改hbase-env.sh

代码语言:javascript复制
#修改位置1
export JAVA_HOME=/root/apps/jdk1.8.0_201

#修改位置2  虽HBase内部有一套自己的zk,但之前配置了zk集群,所以这里zk自己手动启动
export HBASE_MANAGES_ZK=false

修改hbase-site.xml

代码语言:javascript复制
<configuration>
	<!-- 指定hbase在HDFS上存储的路径 -->
    <property>
       <name>hbase.rootdir</name>
       <value>hdfs://hdp-01:9000/hbase</value>
    </property>
	<!-- 指定hbase是分布式的 -->
    <property>
       <name>hbase.cluster.distributed</name>
       <value>true</value>
    </property>
	<!-- 指定zk的地址,多个用","分割 -->
    <property>
       <name>hbase.zookeeper.quorum</name>
       <value>hdp-01:2181,hdp-02:2181,hdp-03:2181</value>
    </property>
</configuration>

修改regionservers

代码语言:javascript复制
hdp-01
hdp-02
hdp-03

拷贝到其他机器:

代码语言:javascript复制
scp -r hbase-1.2.1/ hdp-02:$PWD
scp -r hbase-1.2.1/ hdp-03:$PWD

3.3    启动HBase集群

代码语言:javascript复制
bin/start-hbase.sh

启动完后,还可以在集群中找任意一台机器启动一个备用的master

代码语言:javascript复制
bin/hbase-daemon.sh start master

新启的这个master会处于backup状态

3.4    HBase图形化管理界面

http://hdp-01:16010

启动了3台RegionServer ,hdp-02为备用Master

hbase:meta中存储的是用户表regionServer的索引信息

3.5    启动hbase的命令行客户端

bin/hbase shell

Hbase> list     // 查看表

Hbase> status   // 查看集群状态

Hbase> version  // 查看集群版本

四、HBase客户端读写数据过程时的路由过程

客户端请求数据完整的过程:

1、Client先去zookeeper中查找meta表在哪个region server

2、Client去meta表中查找所要的数据在哪个region server

3、Client去目标region server上得到需要的数据

所以客户端查找数据并不需要经过Master,但一定需要经过zookeeper

五、HBase表模型

hbase的表模型跟mysql之类的关系型数据库的表模型差别巨大

hbase的表模型中有:行的概念,但没有字段的概念

行中存的都是key-value对,每行中的key-value对中的key可以是各种各样,每行中的key-value对的数量也可以是各种各样

hbase表模型的要点:

一个表,有表名

一个表可以分为多个(不同列族的数据会存储在不同文件中)

表中的每一行有一个“rowkey

表中的每一对kv数据称作一个

hbase可以对数据存储多个历史版本(历史版本数量可配置) 

整张表由于数据量过大,会被横向切分成若干个(用rowkey范围标识),不同region的数据也存储在不同文件中

hbase会对插入的数据按顺序存储:

/hbase/WALs为HBase的日志目录,HBase热点数据都在内存(MemStore)操作,同时对数据的操作会记录在日志中,防止宕机故障丢失数据

hbase的表中能存储什么数据类型?

hbase中只支持byte[] 

此处的byte[] 包括了:rowkey,key,value,列族名,表名

HBASE表的物理存储结构?

六、HBase命令行客户端操作

6.1    建表

代码语言:javascript复制
create 'user_info','base_info','extra_info'
          表名           列族名      列族名

6.2    插入数据

代码语言:javascript复制
                   put     表名    rowkey       列族:key         value      
hbase(main):011:0> put 'user_info','001','base_info:username','zhangsan'
0 row(s) in 0.2420 seconds

hbase(main):012:0> put 'user_info','001','base_info:age','18'
0 row(s) in 0.0140 seconds

hbase(main):013:0> put 'user_info','001','base_info:sex','female'
0 row(s) in 0.0070 seconds

hbase(main):014:0> put 'user_info','001','extra_info:career','it'
0 row(s) in 0.0090 seconds

hbase(main):015:0> put 'user_info','002','extra_info:career','actoress'
0 row(s) in 0.0090 seconds

hbase(main):016:0> put 'user_info','002','base_info:username','liuyifei'
0 row(s) in 0.0060 seconds

6.3    查询数据方式一:scan 扫描

代码语言:javascript复制
hbase(main):017:0> scan 'user_info'
ROW                           COLUMN CELL                                                                        
 001                          column=base_info:age, timestamp=1555558863113, value=18                            
 001                          column=base_info:sex, timestamp=1555558891370, value=female                        
 001                          column=base_info:username, timestamp=1555558850869, value=zhangsan                 
 001                          column=extra_info:career, timestamp=1555558940628, value=it                        
 002                          column=base_info:username, timestamp=1555558983813, value=liuyifei                 
 002                          column=extra_info:career, timestamp=1555558976367, value=actoress                  
2 row(s) in 0.0550 seconds

6.4    查询数据方式二:get 单行数据

代码语言:javascript复制
hbase(main):020:0> get 'user_info','001'
COLUMN                            CELL                                                                                            
 base_info:age                    timestamp=1496568160192, value=19                                                               
 base_info:sex                    timestamp=1496567934669, value=female                                                           
 base_info:username               timestamp=1496567889554, value=zhangsan                                                         
 extra_info:career                timestamp=1496567963992, value=it                                                               
4 row(s) in 0.0770 seconds

6.5    删除一个kv数据

代码语言:javascript复制
hbase(main):021:0> delete 'user_info','001','base_info:sex'
0 row(s) in 0.0390 seconds

删除整行数据:

代码语言:javascript复制
hbase(main):024:0> deleteall 'user_info','001'
0 row(s) in 0.0090 seconds

hbase(main):025:0> get 'user_info','001'
COLUMN                            CELL                                                                                            
0 row(s) in 0.0110 seconds

6.6    删除整个表

代码语言:javascript复制
hbase(main):028:0> disable 'user_info'
0 row(s) in 2.3640 seconds

hbase(main):029:0> drop 't_user_info'
0 row(s) in 1.2950 seconds

hbase(main):030:0> list
TABLE                                                                                                                             
0 row(s) in 0.0130 seconds

=> []

注意不能直接drop,否则会报错表正在启用中,应该先disable

6.7    Hbase重要特性--排序特性(行键)

插入到hbase中去的数据,hbase会自动排序存储:

排序规则: 首先看行键,然后看列族名,然后看列(key)名;按字典顺序

Hbase的这个特性跟查询效率有极大的关系

比如:一张用来存储用户信息的表,有名字,户籍,年龄,职业....等信息

然后,在业务系统中经常需要:

查询某个省的所有用户

经常需要查询某个省的指定姓的所有用户

思路:如果能将相同省的用户在hbase的存储文件中连续存储,并且能将相同省中相同姓的用户连续存储,那么,上述两个查询需求的效率就会提高!!!

做法:将查询条件拼到rowkey内

七、HBase Java API

DDL和DML如下:

DDL基本思路:

代码语言:javascript复制
//1、创建一个连接
Connection conn = ConnectionFactory.createConnection(conf);

//2、拿到一个DDL操作器:表管理器admin
Admin admin = conn.getAdmin();

//3、用表管理器的api去建表、删表、修改表定义
admin.createTable(HTableDescriptor descriptor);
// 停用表
admin.disableTable(TableName.valueOf("user_info"));
// 删除表
admin.deleteTable(TableName.valueOf("user_info"));
// 取出旧的表定义信息
HTableDescriptor tableDescriptor=admin.getTableDescriptor(TableName.valueOf("user_info"));
//修改表
admin.modifyTable(TableName.valueOf("user_info"), tableDescriptor);
代码语言:javascript复制
public class HBaseClientDemo {
    Connection connection = null;

    @Before
    public void getConnection() throws Exception {
        // 构建一个连接对象
        Configuration conf = HBaseConfiguration.create();  // 会自动加载hbase-site.xml
        conf.set("hbase.zookeeper.quorum", "hdp-01:2181,hdp-02:2181,hdp-03:2181");
        connection = ConnectionFactory.createConnection(conf);
    }

    /**
     * DDL操作
     */
    @Test
    public void testCreateTable() throws Exception {
        // 从连接中构造一个DDL操作器
        Admin admin = connection.getAdmin();

        // 创建一个表定义描述对象
        HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf("user_info"));

        // 创建列族定义描述对象
        HColumnDescriptor hColumnDescriptor_1 = new HColumnDescriptor("base_info");
        hColumnDescriptor_1.setMaxVersions(3); // 设置该列族中存储数据的最大版本数,默认是1

        HColumnDescriptor hColumnDescriptor_2 = new HColumnDescriptor("extra_info");

        // 将列族定义信息对象放入表定义对象中
        hTableDescriptor.addFamily(hColumnDescriptor_1);
        hTableDescriptor.addFamily(hColumnDescriptor_2);


        // 用ddl操作器对象:admin 来建表
        admin.createTable(hTableDescriptor);

        // 关闭连接
        admin.close();
        connection.close();
    }

    @Test
    public void testDropTable() throws Exception {
        Admin admin = connection.getAdmin();

        // 停用表
        admin.disableTable(TableName.valueOf("user_info"));
        // 删除表
        admin.deleteTable(TableName.valueOf("user_info"));


        admin.close();
        connection.close();
    }

    // 修改表定义--添加一个列族
    @Test
    public void testAlterTable() throws Exception{

        Admin admin = connection.getAdmin();

        // 取出旧的表定义信息
        HTableDescriptor tableDescriptor = admin.getTableDescriptor(TableName.valueOf("user_info"));


        // 新构造一个列族定义
        HColumnDescriptor hColumnDescriptor = new HColumnDescriptor("other_info");
        hColumnDescriptor.setBloomFilterType(BloomType.ROWCOL); // 设置该列族的布隆过滤器类型

        // 将列族定义添加到表定义对象中
        tableDescriptor.addFamily(hColumnDescriptor);


        // 将修改过的表定义交给admin去提交
        admin.modifyTable(TableName.valueOf("user_info"), tableDescriptor);


        admin.close();
        connection.close();

    }
    /**
     * ----------------------------------------------------------------------
     * DML -- 数据的增删改查
     */

    /**
	 * 增
	 * 改:put来覆盖
	 * @throws Exception 
	 */
	@Test
	public void testPut() throws Exception{
		
		// 获取一个操作指定表的table对象,进行DML操作
		Table table = conn.getTable(TableName.valueOf("user_info"));
		
		// 构造要插入的数据为一个Put类型(一个put对象只能对应一个rowkey)的对象
		Put put = new Put(Bytes.toBytes("001"));
		put.addColumn(Bytes.toBytes("base_info"), Bytes.toBytes("username"), Bytes.toBytes("张三"));
		put.addColumn(Bytes.toBytes("base_info"), Bytes.toBytes("age"), Bytes.toBytes("18"));
		put.addColumn(Bytes.toBytes("extra_info"), Bytes.toBytes("addr"), Bytes.toBytes("北京"));
		
		
		Put put2 = new Put(Bytes.toBytes("002"));
		put2.addColumn(Bytes.toBytes("base_info"), Bytes.toBytes("username"), Bytes.toBytes("李四"));
		put2.addColumn(Bytes.toBytes("base_info"), Bytes.toBytes("age"), Bytes.toBytes("28"));
		put2.addColumn(Bytes.toBytes("extra_info"), Bytes.toBytes("addr"), Bytes.toBytes("上海"));
	
		
		ArrayList<Put> puts = new ArrayList<>();
		puts.add(put);
		puts.add(put2);
		
		
		// 插进去
		table.put(puts);
		
		table.close();
		conn.close();
		
	}
	
	
	/**
	 * 循环插入大量数据
	 * @throws Exception 
	 */
	@Test
	public void testManyPuts() throws Exception{
		
		Table table = conn.getTable(TableName.valueOf("user_info"));
		ArrayList<Put> puts = new ArrayList<>();
		
		for(int i=0;i<100000;i  ){
			Put put = new Put(Bytes.toBytes("" i));
			put.addColumn(Bytes.toBytes("base_info"), Bytes.toBytes("username"), Bytes.toBytes("张三" i));
			put.addColumn(Bytes.toBytes("base_info"), Bytes.toBytes("age"), Bytes.toBytes((18 i) ""));
			put.addColumn(Bytes.toBytes("extra_info"), Bytes.toBytes("addr"), Bytes.toBytes("北京"));
			
			puts.add(put);
		}
		
		table.put(puts);
		
	}
	
	/**
	 * 删
	 * @throws Exception 
	 */
	@Test
	public void testDelete() throws Exception{
		Table table = conn.getTable(TableName.valueOf("user_info"));
		
		// 构造一个对象封装要删除的数据信息
		Delete delete1 = new Delete(Bytes.toBytes("001"));
		
		Delete delete2 = new Delete(Bytes.toBytes("002"));
		delete2.addColumn(Bytes.toBytes("extra_info"), Bytes.toBytes("addr"));
		
		ArrayList<Delete> dels = new ArrayList<>();
		dels.add(delete1);
		dels.add(delete2);
		
		table.delete(dels);
		
		
		table.close();
		conn.close();
	}
	
	/**
	 * 查
	 * @throws Exception 
	 */
	@Test
	public void testGet() throws Exception{
		
		Table table = conn.getTable(TableName.valueOf("user_info"));
		
		Get get = new Get("002".getBytes());
		
		Result result = table.get(get);
		
		// 从结果中取用户指定的某个key的value  result.getValue(family,qualifier);
		byte[] value = result.getValue("base_info".getBytes(), "age".getBytes());
		System.out.println(new String(value));
		
		System.out.println("-------------------------");
		
		// 遍历整行结果中的所有kv单元格
		CellScanner cellScanner = result.cellScanner();
		while(cellScanner.advance()){
			Cell cell = cellScanner.current();
			
			byte[] rowArray = cell.getRowArray();  //本kv所属的行键的字节数组
			byte[] familyArray = cell.getFamilyArray();  //列族名的字节数组
			byte[] qualifierArray = cell.getQualifierArray();  //列名的字节数据
			byte[] valueArray = cell.getValueArray(); // value的字节数组
			//不加偏移量和长度会全部查出来
			System.out.println("行键: " new String(rowArray,cell.getRowOffset(),cell.getRowLength()));
			System.out.println("列族名: " new String(familyArray,cell.getFamilyOffset(),cell.getFamilyLength()));
			System.out.println("列名: " new String(qualifierArray,cell.getQualifierOffset(),cell.getQualifierLength()));
			System.out.println("value: " new String(valueArray,cell.getValueOffset(),cell.getValueLength()));
			
		}
		
		table.close();
		conn.close();
		
	}
	
	
	/**
	 * 按行键范围查询数据
	 * @throws Exception 
	 */
	@Test
	public void testScan() throws Exception{
		
		Table table = conn.getTable(TableName.valueOf("user_info"));
		
		// 包含起始行键,不包含结束行键,但是如果真的想查询出末尾的那个行键,那么,可以在末尾行键上拼接一个不可见的字节(00)
		Scan scan = new Scan("10".getBytes(), "1000001".getBytes());
		
		ResultScanner scanner = table.getScanner(scan);
		
		Iterator<Result> iterator = scanner.iterator();
		
		while(iterator.hasNext()){
			
			Result result = iterator.next();
			// 遍历整行结果中的所有kv单元格
			CellScanner cellScanner = result.cellScanner();
			while(cellScanner.advance()){
				Cell cell = cellScanner.current();
				
				byte[] rowArray = cell.getRowArray();  //本kv所属的行键的字节数组
				byte[] familyArray = cell.getFamilyArray();  //列族名的字节数组
				byte[] qualifierArray = cell.getQualifierArray();  //列名的字节数据
				byte[] valueArray = cell.getValueArray(); // value的字节数组
				
				System.out.println("行键: " new String(rowArray,cell.getRowOffset(),cell.getRowLength()));
				System.out.println("列族名: " new String(familyArray,cell.getFamilyOffset(),cell.getFamilyLength()));
				System.out.println("列名: " new String(qualifierArray,cell.getQualifierOffset(),cell.getQualifierLength()));
				System.out.println("value: " new String(valueArray,cell.getValueOffset(),cell.getValueLength()));
			}
			System.out.println("----------------------");
		}
	}
	
	@Test
	public void test(){
		String a = "000";
		String b = "000";
		
		System.out.println(a);
		System.out.println(b);
		
		
		byte[] bytes = a.getBytes();
		byte[] bytes2 = b.getBytes();
		
		System.out.println("");
		
	}
}

0 人点赞