下面记录一下Hbase的简单命令,以及相关的API操作,Hbase虽然也是一种数据库,但是基本命令和MySQL不一样。

基本概念

Hbase因为是列存储,因此出现了一些新的概念,分别是行键RowKey、列族Column Family、列Column、单元Cell和命名空间NameSpace。大部分概念可以通过excel表格来理解,以下是它们的示意图。

(1)行键

行键类似MySQL中的主键,但hbase中没有主键的概念,它默认按照字典序排列,这个是分布式存储的基础。建表的时候是不需要指定行键的,它是在插入数据的指定。

(2)列族和列

一个表中至少包含一个列族,如图它相当如一个大的类,将包含很多小类,小类就是列。列族下面可以有0到多个列,列在建表的时候无需指定,在添加数据时可以动态增删。从上面示意图可以看出,增删列时最后会形成一个稀疏的数据结构,它不同MySQL那么紧凑密集。

(3)单元

hbase是可以增删改查数据的,实际上它的数据是存储在hdfs上,理论上是不能修改的,为了能做到更新数据,hbase存储的数据将有一个时间戳来标识,不同时间戳的数据就存储在一个个的不同单元中,默认只显示最新时间戳的数据,给人一种可以修改数据的感觉。因此单元里就是保存不同时间戳的数据。

(4)命名空间

命名空间相当如MySQL中的database,在hbase中使用namespace,均是可以区分同名表名的,hbase中如果不指定namespace就默认是default的命名空间。

基本命令

基本命令包含DDL(增删表)和DML(增删改查表数据),其中DDL命令需要经过HMaster,DML只需要经过HRegionServer,下面记录一下。

DDL相关

(1)新建一个表,下面指定了命名空间的操作。

# 新建命名空间,默认是default
hbase(main):002:0> create_namespace "hbasedemo"
2020-01-11 11:43:35,074 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-0.98.17-hadoop2/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 2.0560 seconds
# 查看命名空间
hbase(main):003:0> list_namespace
NAMESPACE
default
hbase
hbasedemo
3 row(s) in 0.0620 seconds
# 新建表,至少需要指定表名、列族,basic和data就是列族,这是省略写法
hbase(main):004:0> create \'hbasedemo:star\',\'basic\',\'data\'
0 row(s) in 1.0880 seconds

=> Hbase::Table - hbasedemo:star
# 查看表是否建立,如果是默认就用list命令查看表
hbase(main):005:0> list_namespace_tables \'hbasedemo\'
TABLE
star
1 row(s) in 0.0640 seconds

(2)新建一个表,如果不指定命令空间,默认就是在default命名空间下,下面使用建表的完整写法。

# 完整写法
hbase(main):015:0> create \'star\',{NAME=>\'basic\'},{NAME=>\'data\'}
0 row(s) in 0.4320 seconds

=> Hbase::Table - star
# 查看表结构
hbase(main):017:0> desc \'star\'
Table star is ENABLED
star
COLUMN FAMILIES DESCRIPTION
{NAME => \'basic\', BLOOMFILTER => \'ROW\', VERSIONS => \'1\', IN_MEMORY => \'false\', KEEP_DELETED_CELLS => \'FALSE\', D
ATA_BLOCK_ENCODING => \'NONE\', TTL => \'FOREVER\', COMPRESSION => \'NONE\', MIN_VERSIONS => \'0\', BLOCKCACHE => \'true
\', BLOCKSIZE => \'65536\', REPLICATION_SCOPE => \'0\'}
{NAME => \'data\', BLOOMFILTER => \'ROW\', VERSIONS => \'1\', IN_MEMORY => \'false\', KEEP_DELETED_CELLS => \'FALSE\', DA
TA_BLOCK_ENCODING => \'NONE\', TTL => \'FOREVER\', COMPRESSION => \'NONE\', MIN_VERSIONS => \'0\', BLOCKCACHE => \'true\'
, BLOCKSIZE => \'65536\', REPLICATION_SCOPE => \'0\'}
2 row(s) in 0.0650 seconds

(3)删除一个表,删除刚才命名空间下建立的表。

# 删除表,需要先disable让其不可用,反之使用enable使其可用
hbase(main):007:0> disable \'hbasedemo:star\'
0 row(s) in 1.3800 seconds
# 查看是否已经禁用
hbase(main):010:0> is_enabled \'hbasedemo:star\'
false
0 row(s) in 0.0370 seconds
# 删除表,指定了命名空间前面
hbase(main):011:0> drop \'hbasedemo:star\'
0 row(s) in 0.2540 seconds
# 查看是否删除
hbase(main):012:0> list_namespace_tables \'hbasedemo\'
TABLE
0 row(s) in 0.0130 seconds

(4)删除一个表,如果删除default命名空间下的表。

hbase(main):019:0> disable \'star\'
0 row(s) in 1.3080 seconds
# 删除表
hbase(main):020:0> drop \'star\'
0 row(s) in 0.2010 seconds
# exists命令查看表是否存在
hbase(main):021:0> exists \'star\'
Table star does not exist
0 row(s) in 0.0240 seconds

另外,删除了表后,就可以删除命名空间了,否则会提示命名空间下不为空。

hbase(main):006:0> drop_namespace \'hbasedemo\'

ERROR: org.apache.hadoop.hbase.constraint.ConstraintException: Only empty namespaces can be removed. Namespace hbasedemo has 1 tables

删除了表后,就可以删除命名空间。

# 删除命名空间
hbase(main):013:0> drop_namespace \'hbasedemo\'
0 row(s) in 0.0520 seconds
# 已删除
hbase(main):014:0> list_namespace
NAMESPACE
default
hbase

DML相关

以下DML相关操作,在默认命名空间下进行,如果需要指定命名空间,表名前加命名空间即可,即\’命名空间:表名\’来指定。

(1)插入数据 ,使用命令\’\’put 表名,行键,\’列族:列\’,数据\’\’的格式来添加。

# 插入两条数据
hbase(main):024:0> put \'star\',\'s1\',\'basic:name\',\'messi\'
0 row(s) in 0.2160 seconds

hbase(main):025:0> put \'star\',\'s1\',\'data:score\',51
0 row(s) in 0.0160 seconds

(2)查找数据,主要为get和scan命令,其中scan为全表扫描,get是指定列族和列的查询方式。

get命令

# 查询s1行键的所有数据
hbase(main):026:0> get \'star\',\'s1\'
COLUMN                        CELL
 basic:name                   timestamp=1578716219238, value=messi
 data:score                   timestamp=1578716244483, value=51
2 row(s) in 0.4040 seconds
# 查询s1行键的指定列族的数据
hbase(main):027:0> get \'star\',\'s1\',{COLUMN=>\'basic\'}
COLUMN                        CELL
 basic:name                   timestamp=1578716219238, value=messi
1 row(s) in 0.0210 seconds
# 查询s1行键的指定列族的数据,简写方式
hbase(main):028:0> get \'star\',\'s1\',\'data\'
COLUMN                        CELL
 data:score                   timestamp=1578716244483, value=51
1 row(s) in 0.0100 seconds
# 查询s1行键的指定列族和列的数据
hbase(main):029:0> get \'star\',\'s1\',{COLUMN=>\'basic:name\'}
COLUMN                        CELL
 basic:name                   timestamp=1578716219238, value=messi
1 row(s) in 0.0090 seconds
# 查询s1行键的指定列族和列的数据,简写方式
hbase(main):028:0> get \'star\',\'s1\',\'data:score\'
COLUMN                        CELL
 data:score                   timestamp=1578716244483, value=51
1 row(s) in 0.0110 seconds

scan命令

# 先插入一条数据
hbase(main):034:0> put \'star\',\'s1\',\'data:assists\',10
0 row(s) in 0.0650 seconds
# 全表查询
hbase(main):035:0> scan \'star\'
ROW                           COLUMN+CELL
 s1                           column=basic:name, timestamp=1578716219238, value=messi
 s1                           column=data:assists, timestamp=1578717093292, value=10
 s1                           column=data:score, timestamp=1578716244483, value=51
1 row(s) in 0.0230 seconds
# 指定列族
hbase(main):036:0> scan \'star\',{COLUMNS=>[\'basic\',\'data\']}
ROW                           COLUMN+CELL
 s1                           column=basic:name, timestamp=1578716219238, value=messi
 s1                           column=data:assists, timestamp=1578717093292, value=10
 s1                           column=data:score, timestamp=1578716244483, value=51
1 row(s) in 0.0200 seconds
# 指定列
hbase(main):037:0> scan \'star\',{COLUMNS=>[\'basic:name\',\'data:score\']}
ROW                           COLUMN+CELL
 s1                           column=basic:name, timestamp=1578716219238, value=messi
 s1                           column=data:score, timestamp=1578716244483, value=51
1 row(s) in 0.0180 seconds

(3)更新数据

更新数据,其实也是插入数据,只是默认只显示最新时间戳的数据。后续再补充可以显示历史版本数据的操作。

# 查看梅西当前进球数
hbase(main):038:0> get \'star\',\'s1\',\'data:score\'
COLUMN                        CELL
 data:score                   timestamp=1578716244483, value=51
1 row(s) in 0.0210 seconds
# 更新梅西进球数
hbase(main):039:0> put \'star\',\'s1\',\'data:score\',60
0 row(s) in 0.0260 seconds
# 再次查看,显示更新后的数据
hbase(main):040:0> get \'star\',\'s1\',\'data:score\'
COLUMN                        CELL
 data:score                   timestamp=1578717295077, value=60
1 row(s) in 0.0110 seconds

(4)删除数据

删除数据后还未从HDFS上完全抹掉,需要HFile在major compact后,标记为删除的数据才会被真正丢弃。

# 先查看一下
hbase(main):041:0> scan \'star\'
ROW                           COLUMN+CELL
 s1                           column=basic:name, timestamp=1578716219238, value=messi
 s1                           column=data:assists, timestamp=1578717093292, value=10
 s1                           column=data:score, timestamp=1578717295077, value=60
1 row(s) in 0.0180 seconds
# 删除指定行键下的列
hbase(main):042:0> delete \'star\',\'s1\',\'data:assists\'
0 row(s) in 0.0390 seconds
# 已删除梅西的助攻数
hbase(main):043:0> scan \'star\'
ROW                           COLUMN+CELL
 s1                           column=basic:name, timestamp=1578716219238, value=messi
 s1                           column=data:score, timestamp=1578717295077, value=60
1 row(s) in 0.0100 seconds
# 删除整个行键数据
hbase(main):044:0> deleteall \'star\',\'s1\'
0 row(s) in 0.0230 seconds
# 已删除
hbase(main):045:0> scan \'star\'
ROW                           COLUMN+CELL
0 row(s) in 0.0070 seconds

如果要能查看历史版本的数据,建表时需要指定能查看的历史版本数。

# 新建表,指定列族能提供的历史版本数
hbase(main):047:0> create \'star1\',{NAME=>\'basic\',VERSIONS=>3},{NAME=>\'data\',VERSIONS=>2}
0 row(s) in 0.4300 seconds
# 插入数据
=> Hbase::Table - star1
hbase(main):048:0> put \'star1\',\'s1\',\'basic:name\',\'ronald\'
0 row(s) in 0.1110 seconds

hbase(main):049:0> put \'star1\',\'s1\',\'basic:age\',34
0 row(s) in 0.0380 seconds

hbase(main):050:0> put \'star1\',\'s1\',\'data:score\',45
0 row(s) in 0.0190 seconds

hbase(main):051:0> put \'star1\',\'s1\',\'data:score\',46
0 row(s) in 0.0080 seconds

hbase(main):052:0> put \'star1\',\'s1\',\'data:score\',47
0 row(s) in 0.0250 seconds

hbase(main):053:0> put \'star1\',\'s1\',\'data:score\',48
0 row(s) in 0.0150 seconds
# 默认只显示最新信息
hbase(main):054:0> scan \'star1\'
ROW                           COLUMN+CELL
 s1                           column=basic:age, timestamp=1578718726764, value=34
 s1                           column=basic:name, timestamp=1578718715616, value=ronald
 s1                           column=data:score, timestamp=1578718771868, value=48
1 row(s) in 0.0770 seconds
# 指定历史版本数量,然后指定了3条,但是建表只提供2条,因此就显示2条
hbase(main):055:0> scan \'star1\',{COLUMNS=>\'data:score\',VERSIONS=>3}
ROW                           COLUMN+CELL
 s1                           column=data:score, timestamp=1578718771868, value=48
 s1                           column=data:score, timestamp=1578718769656, value=47
1 row(s) in 0.0340 seconds
# 只有1条,虽然能提供3条,但是也只显示1条
hbase(main):056:0> scan \'star1\',{COLUMNS=>\'basic\',VERSIONS=>3}
ROW                           COLUMN+CELL
 s1                           column=basic:age, timestamp=1578718726764, value=34
 s1                           column=basic:name, timestamp=1578718715616, value=ronald
1 row(s) in 0.0180 seconds
# get命令也可以指定版本数
hbase(main):058:0> get \'star1\',\'s1\',{COLUMNS=>\'basic\',VERSIONS=>3}
COLUMN                        CELL
 basic:age                    timestamp=1578718726764, value=34
 basic:name                   timestamp=1578718715616, value=ronald
2 row(s) in 0.0350 seconds
# 可以指定时间戳
hbase(main):059:0> get \'star1\',\'s1\',{COLUMNS=>\'data:score\',TIMESTAMP=>1578718769656}
COLUMN                        CELL
 data:score                   timestamp=1578718769656, value=47
1 row(s) in 0.0090 seconds

基本API操作 

下面是相关API基本操作,覆盖上面基础的的DDL和DML操作,执行过程中使用了log4j来打印日志,log4j的配置文件需放在src目录下。

log4j内容

log4j.rootLogger = info,stdout

log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

View Code

(1)新建表

package com.boe;

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.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.filter.CompareFilter;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.RegexStringComparator;
import org.apache.hadoop.hbase.filter.RowFilter;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * HBase DDL和DML相关API操作
 */
public class HBaseDemo {

    Configuration conf=null;

    @Before
    public void before(){
       conf= HBaseConfiguration.create();
       //通过连接zookeeper,获取hbase的连接地址
       //可以多写几个zookeeper节点,一个节点可能会有宕机的可能,导致连接不上
       conf.set("hbase.zookeeper.quorum","hadoop01:2181,hadoop02:2181,hadoop03:2181");
    }

    //1 创建表
    @Test
    public void createTable() throws IOException {
        //连接HBase
        //Configuration conf=HBaseConfiguration.create();
        //通过连接zookeeper获取HBase的连接地址
        //conf.set("hbase.zookeeper.quorum","node01:2181,node02:2181,node03:2181");//担心一个节点挂掉不保险,可以全写上
        //连接HBase
        HBaseAdmin admin=new HBaseAdmin(conf);
        //创建表
        //准备HTableDescriptor
        HTableDescriptor table=new HTableDescriptor(TableName.valueOf("employer"));//指定表名
        //准备列族
        HColumnDescriptor basic=new HColumnDescriptor("basic");
        HColumnDescriptor other=new HColumnDescriptor("info");
        //表添加列族
        table.addFamily(basic);table.addFamily(other);
        //创建表
        admin.createTable(table);
        //关闭管理权
        admin.close();
    }

}

View Code

执行后新建表employer成功。

(2)添加数据

    //2 向表中添加数据
    @Test
    public void putData() throws IOException {
        HTable table=new HTable(conf,TableName.valueOf("employer"));
        //添加数据
        //将每一条数据封装为put对象
        Put put=new Put("e1".getBytes());//给定rowKey
        put.add("basic".getBytes(),"name".getBytes(),"clyang".getBytes());
        put.add("basic".getBytes(),"age".getBytes(),"28".getBytes());
        //添加数据
        table.put(put);
        //关流
        table.close();

    }

查看表中数据,插入成功。

可以测试插入100万条数据的时间,本次是在mac下完成,内存8G。 

    //2.1 添加100万条数据,计算时长,本机32秒插入完成
    //hbase shell查看结果发现,rowKey默认是按照字典序排列
    @Test
    public void testMillion() throws IOException {
        HTable table=new HTable(conf,TableName.valueOf("employer"));
        //添加100万条数据
        List<Put> list=new ArrayList<>();
        long start = System.currentTimeMillis();
        for (int i = 0; i <1000000 ; i++) {
            Put put=new Put(("e"+i).getBytes());//给定rowKey
            put.add("basic".getBytes(),"number".getBytes(),(i+"").getBytes());
            list.add(put);
            if(list.size()==1000){
                //添加数据
                table.put(list);
                //关流
                list.clear();
            }

        }
        long end = System.currentTimeMillis();
        System.err.println("运行时间为"+(end-start));
        //关流
        table.close();

    }

查看控制台,运行时间为32秒左右,插入100万条数据已经算快了,可能本机配置不行,好一点的机器估计只要几秒。

(3)删除数据

    //3 从表中删除数据
    @Test
    public void deleteData() throws IOException {
        HTable table=new HTable(conf,TableName.valueOf("employer"));
        Delete del=new Delete("e999999".getBytes());
        //删除整行
        table.delete(del);
        //关流
        table.close();
    }

删除成功。

(4)查询数据

查询数据get和scan也要对应的API。

    //4 查询数据
    //a 字段来查
    @Test
    public void getData() throws IOException {
        HTable table=new HTable(conf,TableName.valueOf("employer"));
        //获取数据
        Get get=new Get("e1".getBytes());
        Result result = table.get(get);
        //指定列族family和列名qualifier
        byte[] name = result.getValue("basic".getBytes(), "name".getBytes());
        byte[] age = result.getValue("basic".getBytes(), "age".getBytes());
        //打印
        System.err.println(new String(name)+":"+new String(age));
        //关流
        table.close();
    }

    //b 全表来查
    @Test
    public void scanData() throws IOException {
        HTable table=new HTable(conf,TableName.valueOf("employer"));
        //获取扫描器
        //Scan scan=new Scan();//全表扫描
        Scan scan=new Scan("e8848".getBytes(),"e8849".getBytes());//指定起始和结束rowKey,部分扫描

        ResultScanner scanner = table.getScanner(scan);
        //将扫描器转换为迭代器后才能遍历
        Iterator<Result> iterator = scanner.iterator();
        while(iterator.hasNext()){
            Result result = iterator.next();
            //从result中拿值
            byte[] value = result.getValue("basic".getBytes(), "number".getBytes());
            System.err.println(new String(value));
        }
        table.close();
    }

按字段查控制台

按全表查控制台,全表查还可以指定起始和结束行键,发现默认是按照字典序排列。

查询时,还可以根据行键来过滤,需要使用filter,API中提供了多种选择,这里使用行过滤器,过滤出含有8848行键的数据。

    //c 查询数据,使用filter过滤
    @Test
    public void filterData() throws IOException{
        HTable table=new HTable(conf,TableName.valueOf("employer"));
        //获取扫描器
        Scan scan=new Scan();
        //获取过滤器
        Filter filter=new RowFilter(CompareFilter.CompareOp.EQUAL,new RegexStringComparator(".*8848.*"));
        //设置过滤器
        scan.setFilter(filter);
        //扫描数据
        ResultScanner scanner = table.getScanner(scan);
        Iterator<Result> iterator = scanner.iterator();
        while(iterator.hasNext()){
            Result result = iterator.next();
            //打印result中的值
            System.err.println(new String(result.getValue("basic".getBytes(),"number".getBytes())));
        }
    }

查看控制台,过滤成功。

(5)删除表

    //5 删除表
    @Test
    public void dropTable() throws IOException {
        HBaseAdmin admin=new HBaseAdmin(conf);
        //禁用
        admin.disableTable("employer".getBytes());
        //删除表
        admin.deleteTable("employer".getBytes());
        //关流
        admin.close();
    }

查看删除成功。

以上就是对HBase基础命令和API的记录,后续补充HBase原理相关的内容。

 

参考博文:

(1)https://www.cnblogs.com/dato/p/7049343.html

(2)https://www.cnblogs.com/youngchaolin/p/12130306.html

版权声明:本文为youngchaolin原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/youngchaolin/p/12179320.html