HBase 入门篇

HBase 入门篇

一、HBase 简介

1.1 HBase的起源

HBase的原型是Google的BigTable论文,受到了该论文思想的启发,目前作为Hadoop的子项目来开发维护,用于支持结构化的数据存储。

官方网站:http://hbase.apache.org

— 2006年Google发表BigTable白皮书

— 2006年开始开发HBase

— 2008年北京成功开奥运会,程序员默默地将HBase弄成了Hadoop的子项目

— 2010年HBase成为Apache顶级项目

— 现在很多公司二次开发出了很多发行版本,你也开始使用了。

1.2 HBase的角色

(1)HMaster

功能:

  1. 监控RegionServer

  2. 处理RegionServer故障转移

  3. 处理元数据的变更

  4. 处理region的分配或移除

  5. 在空闲时间进行数据的负载均衡

  6. 通过Zookeeper发布自己的位置给客户端

(2)RegionServer

功能:

  1. 负责存储HBase的实际数据

  2. 处理分配给它的Region

  3. 刷新缓存到HDFS

  4. 维护HLog

  5. 执行压缩

  6. 负责处理Region分片

1.3 HBase的架构

HBase一种是作为存储的分布式文件系统,另一种是作为数据处理模型的MR框架。因为日常开发人员比较熟练的是结构化的数据进行处理,但是在HDFS直接存储的文件往往不具有结构化,所以催生出了HBase在HDFS上的操作。如果需要查询数据,只需要通过键值便可以成功访问。

1) Write-Ahead logs

HBase的修改记录,当对HBase读写数据的时候,数据不是直接写进磁盘,它会在内存中保留一段时间(时间以及数据量阈值可以设定)。但把数据保存在内存中可能有更高的概率引起数据丢失,为了解决这个问题,数据会先写在一个叫做Write-Ahead logfile的文件中,然后再写入内存中。所以在系统出现故障的时候,数据可以通过这个日志文件重建。

2) HFile

这是在磁盘上保存原始数据的实际的物理文件,是实际的存储文件。

3) Store

HFile存储在Store中,一个Store对应HBase表中的一个列族。

4) MemStore

顾名思义,就是内存存储,位于内存中,用来保存当前的数据操作,所以当数据保存在WAL中之后,RegsionServer会在内存中存储键值对。

5) Region

Hbase表的分片,HBase表会根据RowKey值被切分成不同的region存储在RegionServer中,在一个RegionServer中可以有多个不同的region。

HBase内置有Zookeeper,但一般我们会有其他的Zookeeper集群来监管master和regionserver,Zookeeper通过选举,保证任何时候,集群中只有一个活跃的HMaster,HMaster与HRegionServer 启动时会向ZooKeeper注册,存储所有HRegion的寻址入口,实时监控HRegionserver的上线和下线信息。并实时通知给HMaster,存储HBase的schema和table元数据,默认情况下,HBase 管理ZooKeeper 实例,Zookeeper的引入使得HMaster不再是单点故障。一般情况下会启动两个HMaster,非Active的HMaster会定期的和Active HMaster通信以获取其最新状态,从而保证它是实时更新的,因而如果启动了多个HMaster反而增加了Active HMaster的负担。一个RegionServer可以包含多个HRegion,每个RegionServer维护一个HLog,和多个HFiles以及其对应的MemStore。RegionServer运行于DataNode上,数量可以与DatNode数量一致,请参考如下架构图:

《HBase 入门篇》 2019022702.png

1.4 读写流程

HBase读数据流程

1) HRegionServer保存着meta表以及表数据,要访问表数据,首先Client先去访问zookeeper,从zookeeper里面获取meta表所在的位置信息,即找到这个meta表在哪个HRegionServer上保存着。

2) 接着Client通过刚才获取到的HRegionServer的IP来访问Meta表所在的HRegionServer,从而读取到Meta,进而获取到Meta表中存放的元数据。

3) Client通过元数据中存储的信息,访问对应的HRegionServer,然后扫描所在HRegionServer的Memstore和Storefile来查询数据。

4) 最后HRegionServer把查询到的数据响应给Client。

HBase写数据流程

1) Client也是先访问zookeeper,找到Meta表,并获取Meta表信息。

2) 确定当前将要写入的数据所对应的RegionServer服务器和Region。

3) Client向该RegionServer服务器发起写入数据请求,然后RegionServer收到请求并响应。

4) Client先把数据写入到HLog,以防止数据丢失。

5) 然后将数据写入到Memstore。

6) 如果Hlog和Memstore均写入成功,则这条数据写入成功。在此过程中,如果Memstore达到阈值,会把Memstore中的数据flush到StoreFile中。

7) 当Storefile越来越多,会触发Compact合并操作,把过多的Storefile合并成一个大的Storefile。当Storefile越来越大,Region也会越来越大,达到阈值后,会触发Split操作,将Region一分为二。

尖叫提示:因为内存空间是有限的,所以说溢写过程必定伴随着大量的小文件产生。

二、HBase 安装部署

(1)准备集群

正常启动zookeeper集群
/opt/zk/zookeeper-3.4.10/bin/zkServer.sh start

正常启动Hadoop集群
/opt/hadoop/hadoop-2.5.0/sbin/start-dfs.sh
/opt/hadoop/hadoop-2.5.0/sbin/start-yarn.sh

(2)下载、解压

下载地址http://archive.apache.org/dist/hbase

wget http://archive.apache.org/dist/hbase/1.3.1/hbase-1.3.1-bin.tar.gz

tar -zxvf hbase-1.3.1-bin.tar.gz -C /opt/hbase/

(3)改配置

vim hbase-env.sh

export JAVA_HOME=/opt/BASIC/jdk1.8.0_181

export HBASE_MANAGES_ZK=false

vim hbase-site.xml

<configuration>
    <!-- 注意端口号与Hadoop中core-site.xml里的fs.defaultFS保持一致  -->
    <property>     
        <name>hbase.rootdir</name>     
        <value>hdfs://hmq01:9000/hbase</value>   
    </property>

    <property>   
        <name>hbase.cluster.distributed</name>
        <value>true</value>
    </property>

    <!-- 0.98后的新变动,之前版本没有.port,默认端口为60000 -->
    <property>
        <name>hbase.master.port</name>
        <value>16000</value>
    </property>

    <property>   
        <name>hbase.zookeeper.quorum</name>
        <value>hmq01:2181,hmq02:2181,hmq03:2181</value>
    </property>
    
    <!-- zk配置文件指定的data目录 -->
    <property>   
        <name>hbase.zookeeper.property.dataDir</name>
        <value>/opt/zk/zookeeper-3.4.10/data</value>
    </property>
</configuration>

vim regionservers

hmq01
hmq02
hmq03

(4)替换jar包

由于HBase需要依赖Hadoop,所以替换HBase的lib目录下的jar包,以解决兼容问题。

删除hbase原有jar

rm -rf /opt/hbase/hbase-1.3.1/lib/hadoop-*
rm -rf /opt/hbase/hbase-1.3.1/lib/zookeeper-3.4.6.jar

替换成你目前使用的hadoop版本下的相关jar,先用find命令找出,再拷贝。涉及到的jar有

hadoop-annotations-2.7.2.jar
hadoop-auth-2.7.2.jar
hadoop-client-2.7.2.jar (该jar包有的版本的hadoop不存在,mvn hadoop-client-*.*.*.jar手动下载即可)
hadoop-common-2.7.2.jar
hadoop-hdfs-2.7.2.jar
hadoop-mapreduce-client-app-2.7.2.jar
hadoop-mapreduce-client-common-2.7.2.jar
hadoop-mapreduce-client-core-2.7.2.jar
hadoop-mapreduce-client-hs-2.7.2.jar
hadoop-mapreduce-client-hs-plugins-2.7.2.jar
hadoop-mapreduce-client-jobclient-2.7.2.jar
hadoop-mapreduce-client-jobclient-2.7.2-tests.jar
hadoop-mapreduce-client-shuffle-2.7.2.jar
hadoop-yarn-api-2.7.2.jar
hadoop-yarn-applications-distributedshell-2.7.2.jar
hadoop-yarn-applications-unmanaged-am-launcher-2.7.2.jar
hadoop-yarn-client-2.7.2.jar
hadoop-yarn-common-2.7.2.jar
hadoop-yarn-server-applicationhistoryservice-2.7.2.jar
hadoop-yarn-server-common-2.7.2.jar
hadoop-yarn-server-nodemanager-2.7.2.jar
hadoop-yarn-server-resourcemanager-2.7.2.jar
hadoop-yarn-server-tests-2.7.2.jar
hadoop-yarn-server-web-proxy-2.7.2.jar
zookeeper-3.4.5.jar

查找并拷贝

find /opt/hadoop/hadoop-2.5.0/share/hadoop/common/ -name hadoop-annotations* -exec cp {} /opt/hbase/hbase-1.3.1/lib/ \;

也可以全部复制,在删除不必要的文件
find /opt/hadoop/hadoop-2.5.0/ -name hadoop-*  -exec cp {} /opt/hbase/hbase-1.3.1/lib/ \;

(5)HBase软连接Hadoop配置

[root@hmq01 lib]# ln -s /opt/hadoop/hadoop-2.5.0/etc/hadoop/core-site.xml /opt/hbase/hbase-1.3.1/conf/core-site.xml
[root@hmq01 lib]# ln -s /opt/hadoop/hadoop-2.5.0/etc/hadoop/hdfs-site.xml /opt/hbase/hbase-1.3.1/conf/hdfs-site.xml

(6)HBase远程scp到其他机子

scp -r /opt/hbase/hbase-1.3.1 root@hmq02:/opt/hbase/
scp -r /opt/hbase/hbase-1.3.1 root@hmq03:/opt/hbase/

(7)HBase服务的启动,hmq01启动一个HMaster,hmq01、hmq02、hmq03各自启动一个HRegionserver

启动方式一
$ bin/hbase-daemon.sh start master
$ bin/hbase-daemon.sh start regionserver
启动方式二
$ bin/start-hbase.sh
$ bin/stop-hbase.sh

注:启动报错

  1. 如果集群之间的节点时间不同步,会导致regionserver无法启动,抛出ClockOutOfSyncException异常。

    解决:Linux配置ntp时间服务器 https://blog.csdn.net/vic_qxz/article/details/80343351

[root@hmq01 logs]# tail -200 hbase-root-master-hmq01.out
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=128m; support was removed in 8.0
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/hbase/hbase-1.3.1/lib/slf4j-log4j12-1.7.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/hadoop/hadoop-2.5.0/share/hadoop/common/lib/slf4j-log4j12-1.7.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]

解决 vim vim hbase-env.sh

若你使用的是JDK1.8,则要注释掉下面两行

# export HBASE_MASTER_OPTS="$HBASE_MASTER_OPTS -XX:PermSize=128m -XX:MaxPermSize=128m"
# export HBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS -XX:PermSize=128m -XX:MaxPermSize=128m"

jar冲突,改名即可

mv slf4j-log4j12-1.7.5.jar slf4j-log4j12-1.7.5.jar-copy

(8)访问HBase管理页面

http://hmq01:16010

《HBase 入门篇》 2019022701.png

三、命令简单使用

3.1 基本操作

(1)进入HBase客户端命令行

$ bin/hbase shell

(2)查看帮助命令

hbase(main)> help

(3)查看当前数据库中有哪些表

hbase(main)> list

3.2 表的操作

(1)创建表

hbase(main)> create 'student','info'

(2)插入数据到表

hbase(main) > put 'student','1001','info:name','Thomas'
hbase(main) > put 'student','1001','info:sex','male'
hbase(main) > put 'student','1001','info:age','18'
hbase(main) > put 'student','1002','info:name','Janna'
hbase(main) > put 'student','1002','info:sex','female'
hbase(main) > put 'student','1002','info:age','20'

(3)扫描查看表数据

hbase(main) > scan 'student'
hbase(main) > scan 'student',{STARTROW => '1001', STOPROW  => '1001'}
hbase(main) > scan 'student',{STARTROW => '1001'}

(4)查看表结构

hbase(main):012:0> describe 'student'

(5)更新指定字段的数据,当列族版本号为1时是替换,超过1时则为追加

hbase(main) > put 'student','1001','info:name','Nick'
hbase(main) > put 'student','1001','info:age','100'

(6)查看“指定行”或“指定列族:列”的数据

hbase(main) > get 'student','1001'
hbase(main) > get 'student','1001','info:name'

(7)删除数据

删除某rowkey的全部数据:
hbase(main) > deleteall 'student','1001'

删除某rowkey的某一列数据:
hbase(main) > delete 'student','1002','info:sex'

(8)清空表数据

hbase(main) > truncate 'student'

提示:清空表的操作顺序为先disable,然后再truncating。

(9)删除表

首先需要先让该表为disable状态:
hbase(main) > disable 'student'

然后才能drop这个表:
hbase(main) > drop 'student'

提示:如果直接drop表,会报错:Drop the named table. Table must first be disabled
ERROR: Table student is enabled. Disable it first.

(10)统计表数据行数,统计的rowkey数量

hbase(main) > count 'student'

(11)变更表信息

将info列族中的数据存放3个版本:
hbase(main) > alter 'student',{NAME=>'info',VERSIONS=>3}

四、Java API使用

(1)首先需要获取Configuration对象

    private static Configuration configuration;

    static {
        // 单例实例化
        configuration = HBaseConfiguration.create();
        // 配置zk集群的leader角色的ip
        configuration.set("hbase.zookeeper.quorum", "192.168.114.120");
        configuration.set("hbase.zookeeper.property.clientPort", "2181");
    }

(2)判断表是否存在

    public boolean isTableExist(String tableName) throws IOException {
        // 在HBase中管理、访问表需要先创建HBaseAdmin对象
//        Connection connection = ConnectionFactory.createConnection(configuration);
//        HBaseAdmin admin = (HBaseAdmin)connection.getAdmin();

        HBaseAdmin hBaseAdmin = new HBaseAdmin(configuration);
        return hBaseAdmin.tableExists(tableName);
    }

(3)查看有哪些表

    public void listTableName() throws IOException {
        HBaseAdmin hBaseAdmin = new HBaseAdmin(configuration);
        TableName[] tableNames = hBaseAdmin.listTableNames();
        for (TableName tableName : tableNames) {
            System.out.println("---" + tableName.getNameAsString() + "---");
        }
    }

(4)创建表,至少有一个列族

    public void createTable(String tableName, String... columnFamily) throws IOException {
        HBaseAdmin hBaseAdmin = new HBaseAdmin(configuration);
        if (hBaseAdmin.tableExists(tableName)) {
            System.err.println("---该表已经存在---");
        } else {
            //创建表属性对象,表名需要转字节
            HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf(tableName));
            for (String cf : columnFamily) {
                hTableDescriptor.addFamily(new HColumnDescriptor(cf));
            }
            hBaseAdmin.createTable(hTableDescriptor);
            System.out.println("---表创建成功---");
        }
    }

(5)删除表

    public void dropTable(String tableName) throws IOException {
        HBaseAdmin hBaseAdmin = new HBaseAdmin(configuration);
        if (hBaseAdmin.tableExists(tableName)) {
            hBaseAdmin.disableTable(tableName);
            hBaseAdmin.deleteTable(tableName);
            System.out.println("---表删成功---");
        } else {
            System.out.println("---表不存在---");
        }
    }

(6)插入数据

    public void addRowData(String tableName, String rowKey, String columnFamily, String columnName, String columnValue) throws IOException {
        //创建HTable对象
        HTable hTable = new HTable(configuration, tableName);
        Put put = new Put(Bytes.toBytes(rowKey));
        put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(columnName), Bytes.toBytes(columnValue));
        hTable.put(put);
        hTable.close();
        System.out.println("---插入数据成功---");
    }

(7)删除数据成功

public void deleteRows(String tableName, String... rowKeys) throws IOException {
        HTable hTable = new HTable(configuration, tableName);
        List<Delete> deleteList = new ArrayList<>();
        for (String rowKey : rowKeys) {
            Delete delete = new Delete(Bytes.toBytes(rowKey));
            deleteList.add(delete);
        }
        hTable.delete(deleteList);
        hTable.close();
        System.out.println("---删除数据成功---");
    }

(8)得到所有数据

    public void getAllRows(String tableName) throws IOException {
        HTable hTable = new HTable(configuration, tableName);
        //得到用于扫描region的对象
        Scan scan = new Scan();
        //使用HTable得到resultcanner实现类的对象
        ResultScanner resultScanner = hTable.getScanner(scan);
        for (Result result : resultScanner) {
            Cell[] cells = result.rawCells();
            for (Cell cell : cells) {
                //得到rowkey
                System.out.println("行键:" + Bytes.toString(CellUtil.cloneRow(cell)));
                //得到列族
                System.out.println("列族" + Bytes.toString(CellUtil.cloneFamily(cell)));
                System.out.println("列:" + Bytes.toString(CellUtil.cloneQualifier(cell)));
                System.out.println("值:" + Bytes.toString(CellUtil.cloneValue(cell)));
            }
        }
    }

(9)获取一行的所有数据

    public void getRow(String tableName, String rowKey) throws IOException {
        HTable table = new HTable(configuration, tableName);
        Get get = new Get(Bytes.toBytes(rowKey));
        //get.setMaxVersions();显示所有版本
        //get.setTimeStamp();显示指定时间戳的版本
        Result result = table.get(get);
        for (Cell cell : result.rawCells()) {
            System.out.println("行键:" + Bytes.toString(result.getRow()));
            System.out.println("列族" + Bytes.toString(CellUtil.cloneFamily(cell)));
            System.out.println("列:" + Bytes.toString(CellUtil.cloneQualifier(cell)));
            System.out.println("值:" + Bytes.toString(CellUtil.cloneValue(cell)));
            System.out.println("时间戳:" + cell.getTimestamp());
        }
    }

(10)获取某一行指定”列族:列”的数据

    public static void getRowQualifier(String tableName, String rowKey, String family, String qualifier) throws IOException {
        HTable table = new HTable(configuration, tableName);
        Get get = new Get(Bytes.toBytes(rowKey));
        get.addColumn(Bytes.toBytes(family), Bytes.toBytes(qualifier));
        Result result = table.get(get);
        for (Cell cell : result.rawCells()) {
            System.out.println("行键:" + Bytes.toString(result.getRow()));
            System.out.println("列族" + Bytes.toString(CellUtil.cloneFamily(cell)));
            System.out.println("列:" + Bytes.toString(CellUtil.cloneQualifier(cell)));
            System.out.println("值:" + Bytes.toString(CellUtil.cloneValue(cell)));
        }
    }
    原文作者:换煤气哥哥
    原文地址: https://www.jianshu.com/p/02465f349a18
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞