美文网首页
HBase的使用

HBase的使用

作者: lizb | 来源:发表于2019-05-31 15:24 被阅读0次

    HBase是一个在HDFS上开发的面向列的分布式数据库。如果需要实时地随机访问超大规模数据集,就可以使用HBase。本篇介绍HBase的基础知识,包括安装配置、部署运行、表的创建和Java api的使用。

    一、安装

    1.1 下载HBase
    http://www.apache.org/dyn/closer.cgi/hbase/
    下载需要的版本。

    1.2 安装HBase
    将下载的压缩包解压到安装目录。

    tar -zxvf 压缩包 安装目录
    

    1.3 配置HBase
    HBase有两个运行模式:单机模式和分布式模式。而分布式模式有分为伪分布式模式(所有守护进程都运行在单个节点上)和完全分布式模式(进程运行在物理服务器集群中)。
    分布式模式依赖Hadoop分布式文件系统,即HDFS。所以在配置之前,一定要确保有一个合适的,正在工作的HDFS集群。这里我们采用完全分布式模式。
    1)首先进入HBase的安装目录,编辑 /conf/hbase-site.xml文件,修改内容如下:

    <property>
        <name>hbase.rootdir</name> 
        <value>hdfs://probd/hbase</value> 
        <description>probd is the cluster name of hdfs.</description> 
    </property>
    
    <property>
        <name>hbase.cluster.distributed</name> 
        <value>true</value> 
     </property>
    

    这里添加hbase.cluster.distributed属性并设置为true,添加hbase.rootdir并设置为HDFS中NameNode的访问地址,HBase会将数据写到NameNode主机上的/hbase目录下。
    2)配置region服务器。编辑/conf/regionservers文件,该文件列出了所有运行HRegionServer守护进程的主机,每个主机独占一行(类似Hadoop中的slaves文件):

    probd01
    probd02
    probd03
    probd04
    

    HBase集群启动和关闭时会按照该文件中罗列的主机逐一执行。

    3)配置zookeeper。分布式的HBase依赖于zookeeper集群,所有的节点和客户端都必须能够正常访问zookeeper。HBase默认管理一个单点的zookeeper集群,用户通过启动和关闭脚本就可以把zookeeper当做HBase的一部分来启动和关闭进程。用户也可以不依赖于HBase管理zookeeper集群,只需为HBase指出需要使用的集群即可。这里我们使用后一种,即使用已有的zookeeper集群。我们需要在/conf/hbase-env.sh文件中将HBASE_MANAGES_ZK属性设置为false:

    export HBASE_MANAGES_ZK=false
    

    然后,修改hbase-site.xml文件,设置zookeeper的连接地址与客户端端口号:

            <property>
                    <name>hbase.zookeeper.quorum</name> 
                    <value>probd01:2181,probd02:2181,probd03:2181</value> 
            </property> 
            <property>
                    <name>hbase.zookeeper.property.dataDir</name> 
                    <value>/probd/zookeeper-3.4.6/data/data</value>
            </property>
            <property> 
                    <name>hbase.zookeeper.property.clientPort</name>
                    <value>2181</value>  
            </property>
       
    

    zookeeper的地址与端口号请参照自己集群的实际值。

    这些配置完成后,需要复制conf目录到集群的其它节点上以完成同步集群的配置。

    1.4 配置文件介绍
    该小结不属于相关操作,只是介绍一下上面配置过程中所涉及的3个文件。
    1)hbase-site.xml
    在Hadoop中,如果用户需要增加HDFS的特定配置就要添加到hdfs-site.xml文件中。与此类似,在HBase中,用户需要增加配置信息就需要将配置添加到conf/hbase-site.xml文件中。配置参数可以查看HBase 目录 src/main/resources中的源文件hbase-default.xml,在doc目录中也有配置参数信息的HTML文档(但也并非所有的配置信息都罗列在hbase-default.xml中,配置中有些参数并不常用并且只在源码中存在,因此,唯一的办法就是通过阅读源码来查找这些配置参数的作用)。
    进程启动后,服务器会读取hbase-default.xml文件,然后读取hbase-site.xml文件,hbase-site.xml文件的内容会覆盖hbase-default.xml中的内容。
    2)hbase-env.sh
    HBase的环境变量等信息需要在这个文件中设置,例如,HBase守护进程的JVM启动参数;Java堆大小和垃圾回收策略等。在这个文件中还可以设置HBase配置文件的目录、日志目录、SSH选项、进程pid文件的目录等。
    3)regionservers
    这个文件罗列了所有region服务器的主机名,它是纯文本文件,文件中的每一行都是主机名。HBase的运维脚本会依次迭代访问每一行来启动所有region服务器进程。

    1.5 部署运行HBase
    当我们配置好HBase后,接下来就要咋集群上部署HBase。在启动HBase之前,首先要确保HDFS已经启动并处于工作状态。由于我们不依赖HBase管理zookeeper,所以还要确保zookeeper已经运行,否则HBase会把zookeeper作为进程的一部分启动。之后我们进入bin目录下,执行脚本,启动HBase:

    start-hbase.sh
    

    HBase的运行日志写入了logs目录的子目录中。接下来,我们将介绍如何建表、添加数据、扫描已插入的数据、禁用表和删除表等操作。

    二、HBase的基本操作

    首先,启动HBase的交互环境Shell:

    $HBASE_HOME/bin/hbase shell
    

    现在来创建一个简单的表并增加几行数据:

    hbase(main):002:0> create 'testtable','colfam1'
    0 row(s) in 0.2930 seconds
    hbase(main):003:0> list 'testtable'
    TABLE
    testtable
    1 row(s) in 0.0520 seconds
    hbase(main):004:0> put 'testtable','myrow-1','colfam1:q1','value-1'
    0 row(s) in 0.1020 seconds
    hbase(main):005:0> put 'testtable','myrow-2','colfam1:q2','value-2'
    0 row(s) in 0.0410 seconds
    hbase(main):006:0> put 'testtable','myrow-2','colfam1:q3','value-3'
    0 row(s) in 0.0380 seconds
    
    

    通过一条命令,我们创建了一张带有一个列族的表,我们可以通过list命令来检查这张表是否已经存在。然后我们存放了几行数据:通过两个不同的行健myrow-1和myrow-2把新增数据添加到两个不同的行中。有了一个名为colfam1的列族之后,还有添加一个任意限定符才能形成实际的列,如colfam1:q1、colfam1:q2、colfam1:q3。
    接下来,我们看新增的数据是否能被检索,这里用到scan命令:

    hbase(main):007:0> scan 'testtable'
    ROW          COLUMN_CELL
      myrow-1    column=colfam1:q1,timestamp=1559206303975,value= value-1
      myrow-2    column=colfam1:q2,timestamp=1559206304013,value= value-2
      myrow-2    column=colfam1:q3,timestamp=1559206304069,value= value-3
    
    2 row(s) in 0.1400 seconds
    
    

    我们可以看到HBase打印数据时是通过面向单元格的方式分别输出每一列数据。可以看到确实打印了两次myrow-2,和预期的一样,后面还显示了每一列的实际数值。
    如果我们想要获取单行数据,可以使用get命令:

    hbase(main):008:0>  get 'testtable','myrow-1'
    ROW          COLUMN_CELL
    colfam1:q1   timestamp=1559206752410,value= value-1
    
    1 row(s) in 0.0480 seconds
    
    

    删除数据也是基本操作之一,我们来看一下删除一个具体的单元格,并检查数据是否真的删除了:

    hbase(main):009:0>  delete 'testtable','myrow-2','colfam1:q2'
    0 row(s) in 0.0390 seconds
    
    hbase(main):010:0> scan 'testtable'
    ROW          COLUMN_CELL
      myrow-1    column=colfam1:q1,timestamp=1559207110853,value= value-1
      myrow-2    column=colfam1:q3,timestamp=1559207110868,value= value-3
    
    2 row(s) in 0.0620 seconds
    

    我们看到,通过delete命令确实删除了表中的数据。接下来,我们删除这张表:

    hbase(main):011:0> disable 'testtable'
    0 row(s) in 2.1250 seconds
    
    hbase(main):012:0> drop 'testtable'
    0 row(s) in 2878 seconds
    
    

    删除表之前需要禁用表,然后再删除。
    然后,通过输入exit命令关闭Shell并返回命令行窗口:

    hbase(main):013:0> exit
    $ _
    

    最后,运行stop-hbase.sh脚本关闭HBase系统:

    $HBASE_HOME/bin/stop-hbase.sh
    stopping hbase..........
    

    一旦启动了这个脚本,将会看到一条描述集群正在停止的信息,该信息会周期性的打印“.”字符,这仅仅表明脚本正在运行,并不是运行进度的反馈或隐藏的有用信息。关闭脚本大概需要几分钟完成。如果集群中机器的数量很多,那么执行的时间可能更长。我们在关闭Hadoop集群之前一定要确认HBase已经被正常关闭了。

    三、HBase API的使用

    3.1 HBaseConfiguration:这是每个hbase client都会使用到的对象,它代表hbase的配置信息。具有两个构造函数:

    /**
       * Instantiating HBaseConfiguration() is deprecated. Please use
       * HBaseConfiguration#create() to construct a plain Configuration
       * @deprecated Please use create() instead.
       */
      @Deprecated
      public HBaseConfiguration() {
        //TODO:replace with private constructor, HBaseConfiguration should not extend Configuration
        super();
        addHbaseResources(this);
        LOG.warn("instantiating HBaseConfiguration() is deprecated. Please use"
            + " HBaseConfiguration#create() to construct a plain Configuration");
      }
    
      /**
       * Instantiating HBaseConfiguration() is deprecated. Please use
       * HBaseConfiguration#create(conf) to construct a plain Configuration
       * @deprecated Please user create(conf) instead.
       */
      @Deprecated
      public HBaseConfiguration(final Configuration c) {
        //TODO:replace with private constructor
        this();
        merge(this, c);
      }
    

    可以看到两个构造函数在高版本中已经过时了,取而代之的是两个静态方法create:

        /**
       * Creates a Configuration with HBase resources
       * @return a Configuration with HBase resources
       */
      public static Configuration create() {
        Configuration conf = new Configuration();
        // In case HBaseConfiguration is loaded from a different classloader than
        // Configuration, conf needs to be set with appropriate class loader to resolve
        // HBase resources.
        conf.setClassLoader(HBaseConfiguration.class.getClassLoader());
        return addHbaseResources(conf);
      }
    
      /**
       * @param that Configuration to clone.
       * @return a Configuration created with the hbase-*.xml files plus
       * the given configuration.
       */
      public static Configuration create(final Configuration that) {
        Configuration conf = create();
        merge(conf, that);
        return conf;
      }
    

    默认的构造函数会加载hbase-default.xml和hbase-site.xml中的配置信息:

        public static Configuration addHbaseResources(Configuration conf) {
            conf.addResource("hbase-default.xml");
            conf.addResource("hbase-site.xml");
            checkDefaultsVersion(conf);
            return conf;
        }
    

    如果classpath没有这两个文件,就需要你自己设置配置:

    Configuration conf = new Configuration();
    conf.set(“hbase.zookeeper.quorum”, “zkServer”);
    conf.set(“hbase.zookeeper.property.clientPort”, “2181″);
    HBaseConfiguration hbaseConf = new HBaseConfiguration(conf);
    

    3.2 创建表
    创建表之前首先需要连接上HBase:

    Configuration conf = HBaseConfiguration.create();
    //创建hbase的连接,这是一个分布式连接
    Connection conn = ConnectionFactory.createConnection(conf);
    

    连接上HBase后,可以通过Connection来获取HBaseAdmin实例:

    //获取HBaseAdmin实例
    HBaseAdmin admin = (HBaseAdmin) conn.getAdmin();
    

    创建表是通过HBaseAdmin对象来操作的,HBaseAdmin负责表的META信息处理。它提供了createTable这个方法:

    public void createTable(TableDescriptor desc)
    public void createTable(TableDescriptor desc, byte [] startKey,byte [] endKey, int numRegions)
    public void createTable(final TableDescriptor desc, byte [][] splitKeys)
    

    下面,我们来创建3个列族的表:

    
    Configuration conf = HBaseConfiguration.create();
    //创建hbase的连接,这是一个分布式连接
    Connection conn = ConnectionFactory.createConnection(conf);
    //这个admin是管理table时使用的,比如说创建表
    HBaseAdmin admin = (HBaseAdmin) conn.getAdmin();
    
    //创建表名
    TableName tableName = TableName.valueOf("user");
    //创建列族
    //ColumnFamilyDescriptor:代表的是列族的schema
    ColumnFamilyDescriptor family1 = ColumnFamilyDescriptorBuilder.of("info1");
    ColumnFamilyDescriptor family2 = ColumnFamilyDescriptorBuilder.of("info2");
    ColumnFamilyDescriptor family3 = ColumnFamilyDescriptorBuilder.of("info3");
    
    //TableDescriptor:代表的是表的schema
    TableDescriptor table = TableDescriptorBuilder.newBuilder(tableName)
           .addColumnFamily(family1)
           .addColumnFamily(family2)
           .addColumnFamily(family3)
           .build();
    
    //创建表
    admin.createTable(table);
    
    

    3.3 插入和修改数据

    table = conn.getTable(TableName.valueOf("user"));
    //构造参数是row_key,必传
    Put put = new Put(Bytes.toBytes("zhangsan_123"));
    //这里的参数依次为:列族名,列名,值
    put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("name"),Bytes.toBytes("lisi"));
    put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("age"),Bytes.toBytes(22 ));
    put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("sex"),Bytes.toBytes("男"));
    put.addColumn(Bytes.toBytes("info2"),Bytes.toBytes("address"),Bytes.toBytes("天堂" ));
    table.put(put);
    //table.put(List<Put>); //通过一个List集合,可以添加一个集合
    

    3.4 查询数据

    1. 查询单条数据
    String rowKey = "zhangsan_123";
    Get get = new Get(Bytes.toBytes(rowKey));
    Result result = table.get(get);
    byte[] address = result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("address")); 
    byte[] name = result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("name")); 
    byte[] sex = result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("sex"));
    byte[] age = result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("age")); 
    System.out.print(Bytes.toString(name) + ",");
    System.out.print(Bytes.toString(sex) + ",");
    System.out.print(Bytes.toString(address) + ",");
    System.out.print(Bytes.toInt(age) + ",");
    System.out.println();
    
    1. 全表扫描
      完整的全表扫描要慎用,不过全表扫描中可以指定很多过滤器,我们可以很好的使用它。
    Scan scan = new Scan();
    ResultScanner resultScanner = table.getScanner(scan);
    printResult(resultScanner);
    
    1. 区间扫描
      区间扫描我们用处也很多,因为row key是按照字典序来排列的,我们可以根据这个特性,查找某一个用户指定时间段的数据,比如查询用户A昨天到今天的数据在查询的过程中,我们还可以指定返回的结果,比如指定返回一个列族,以及返回指定的列,这样可以增加查询速度
    Scan scan = new Scan();
    scan.withStartRow(Bytes.toBytes("zhangsan_1232")); //设置开始行
    scan.withStopRow(Bytes.toBytes("zhangsan_12352")); //设置结束行
    scan.addFamily(Bytes.toBytes("info2"));//查询指定列族
    ResultScanner resultScanner = table.getScanner(scan);
    printResult(resultScanner);
    
    1. 列值过滤器
      列值过滤器也是我们很常用的操作,它提供了一种甚至值的条件查询。类似sql中的where field = 'xxx',这极大的扩展了hbase的查询方式,因为如果只是根据row key来查询,那么很多产景都不适用hbase。
    Scan scan = new Scan();
    /*
    * 第一个参数: 列族
    * 第二个参数: 列名
    * 第三个参数: 是一个枚举类型
    *              CompareOp.EQUAL  等于
    *              CompareOp.LESS  小于
    *              CompareOp.LESS_OR_EQUAL  小于或等于
    *              CompareOp.NOT_EQUAL  不等于
    *              CompareOp.GREATER_OR_EQUAL  大于或等于
    *              CompareOp.GREATER  大于
    */
    SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(Bytes.toBytes("info2"), Bytes.toBytes("name"), CompareFilter.CompareOp.GREATER_OR_EQUAL, Bytes.toBytes("zhangsan8"));
    //这个方法很重要,需要注意,当此过滤器过滤时,如果遇到该列值为NULL的情况,如果设置的参数为true,则会过滤掉这一行,如果设置的参数为false,那么则会把这一行的结果返回,默认为false
    singleColumnValueFilter.setFilterIfMissing(true);
    scan.setFilter(singleColumnValueFilter);
    
    ResultScanner resultScanner = table.getScanner(scan);
    printResult(resultScanner);
    
    1. 前缀过滤器
      前缀过滤器和表里面的内容没有关系,它只是用来匹配指定的列的,比如有这样两个列 name1 和name2 ,通过这个过滤器,就会查询这两个列的所有数据,当然,其实这个方式和scan.addColumn差不多,并且它会匹配到多个列族。
    ColumnPrefixFilter columnPrefixFilter = new ColumnPrefixFilter(Bytes.toBytes("name"));
    Scan scan = new Scan();
    scan.setFilter(columnPrefixFilter);
    
    ResultScanner resultScanner = table.getScanner(scan);
    printResult(resultScanner);
    
    1. row_key正则
      row_key正则查找也是hbase查找中经常用到的功能。
    //查找以指定内容开头的
    Filter rowKeyFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, new RegexStringComparator("^zhangsan_1239"));
    Scan scan = new Scan();
    scan.setFilter(rowKeyFilter);
    
    ResultScanner resultScanner = table.getScanner(scan);
    printResult(resultScanner);
    
    1. 组合过滤器
      我们可能有这样的情况,我们想扫描指定row key范围的,又想指定address为深圳的,或者是更多的一些条件,这个时候我们就需要多个过滤器组合使用。
    /*
    * 我们需要注意Operator这个参数,这是一个枚举类型,里面有两个类型
    *   Operator.MUST_PASS_ALL   需要通过全部的条件,也就是并且,and &&
    *   Operator.MUST_PASS_ONE   任何一个条件满足都可以,也就是或者,or ||
    */
    FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL);
    //row key正则表达式的过滤器
    Filter rowKeyFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, new RegexStringComparator("^zhangsan_1239"));
    //列值过滤器
    SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter(Bytes.toBytes("info1"), Bytes.toBytes("name"), CompareFilter.CompareOp.EQUAL, Bytes.toBytes("zhangsan9"));
    //把两个filter添加进filterList中
    filterList.addFilter(rowKeyFilter);
    filterList.addFilter(singleColumnValueFilter);
    
    Scan scan = new Scan();
    scan.setFilter(filterList);
    
    ResultScanner resultScanner = table.getScanner(scan);
    printResult(resultScanner);
    

    最后附上printResult方法:

    private void printResult(ResultScanner resultScanner) {
      for (Result result : resultScanner) {
          byte[] address = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("address")); 
          byte[] name = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("name")); 
          byte[] sex = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("sex")); 
          byte[] age = result.getValue(Bytes.toBytes("info1"), Bytes.toBytes("age"));
          byte[] rowKey = result.getRow();
          System.out.print(Bytes.toString(rowKey) + ",");
          System.out.print(Bytes.toString(name) + ",");
          System.out.print(Bytes.toString(sex) + ",");
          System.out.print(Bytes.toString(address) + ",");
          System.out.print((age == null ? null : Bytes.toInt(age)) + ",");
          System.out.println();
       }
    }
    

    3.5 删除数据
    删除数据使用的是Delete对象,我们可以删除一行,或者删除一个列族,或者删除一个列族中的指定列:

    //删除一行
    Delete deleteRow = new Delete(Bytes.toBytes("zhangsan_1235")); 
    
    Delete deleteCol = new Delete(Bytes.toBytes("zhangsan_1235"));
    //删除该行的指定列
    deleteCol.addFamily(Bytes.toBytes("info1"));列族
    //删除指定的一个单元
    deleteCol.addColumn(Bytes.toBytes("info1"),Bytes.toBytes("name"));
    
    table.delete(deleteCol);
    
    table.delete(deleteRow);
    
    //table.delete(List<Delete>); //通过添加一个list集合,可以删除多个
    

    3.6 删除表

    //删除表之前需要禁用表
    admin.disableTable(TableName.valueOf(tablename));
    admin.deleteTable(TableName.valueOf(tablename));
     
    

    相关文章

      网友评论

          本文标题:HBase的使用

          本文链接:https://www.haomeiwen.com/subject/bflmaqtx.html