hbase使用、运维、改进(不断更新)

    刚刚看了google jeff dean 在新的一年里发表的演讲,讲到各种系统配合使用完成海量数据处理任务。深有感触:http://t.cn/zYE89gn 。2013年,依然会专注hbase研究,并以此为基础扩展,对hbase衍生的一些特性和产品进行深入研究,例如replication、phoenix。从传统的离线计算(hadoop、hive),到实时流式计算系统(storm、s4),创新应用性接口(Impala、phoenix),这些hbase的上下游系统、接口扩展等都要进行全面的了解。

       以hbase为基础,让各个面向不同使用场景的系统配合起来共同工作,打造大数据处理体系架构。需要学习的东西很多,2013年,加油!

      

       1、hbase的client端hostname错误导致“Thu Mar 07 13:21:11 CST 2013, org.apache.hadoop.hbase.client.ScannerCallable@79ee2c2c, java.net.UnknownHostException”

 理论上说,hbase的client端会去连接hbase cluster,通过集群返回查询结果,自身的hostname不会影响查询。直到遇到这个异常,通过代码发现client在做scan时会调用一个函数

/**
   * compare the local machine hostname with region server's hostname
   * to decide if hbase client connects to a remote region server
   * @throws UnknownHostException.
   */
  private void checkIfRegionServerIsRemote() throws UnknownHostException {
    String myAddress = DNS.getDefaultHost("default", "default");
    if (this.location.getHostname().equalsIgnoreCase(myAddress)) {
      isRegionServerRemote = false;
    } else {
      isRegionServerRemote = true;
    }
  }

该函数检查连接的region是否就是本地。所以client端去尝试本机的host时会报UnknownHostException异常


       2、hbase的coprocessor中传递自定义类报不支持错误。在coprocessor中传递的对象,需要传递,所以需要序列化和反序列化

      在ExecResult中,序列化和反序列化都依赖一个类HbaseObjectWritable,而这个类中需要声明的有以下3个参数

      Class<?>declaredClass

      Object instance

      Configuration

       同时,HbaseObjectWritable还规定了能够被识别的对象code,没有注册code的也依然会报错。

static {
    ////////////////////////////////////////////////////////////////////////////
    // WARNING: Please do not insert, remove or swap any line in this static  //
    // block.  Doing so would change or shift all the codes used to serialize //
    // objects, which makes backwards compatibility very hard for clients.    //
    // New codes should always be added at the end. Code removal is           //
    // discouraged because code is a short now.                               //
    ////////////////////////////////////////////////////////////////////////////

    int code = NOT_ENCODED + 1;
    // Primitive types.
    addToMap(Boolean.TYPE, code++);
    addToMap(Byte.TYPE, code++);
    addToMap(Character.TYPE, code++);
    addToMap(Short.TYPE, code++);
    addToMap(Integer.TYPE, code++);
    addToMap(Long.TYPE, code++);
    addToMap(Float.TYPE, code++);
    addToMap(Double.TYPE, code++);
    addToMap(Void.TYPE, code++);

    // Other java types
    addToMap(String.class, code++);
    addToMap(byte [].class, code++);
    addToMap(byte [][].class, code++);

    // Hadoop types
    addToMap(Text.class, code++);
    addToMap(Writable.class, code++);
    addToMap(Writable [].class, code++);
    addToMap(HbaseMapWritable.class, code++);
    addToMap(NullInstance.class, code++);

    // Hbase types
    addToMap(HColumnDescriptor.class, code++);
    addToMap(HConstants.Modify.class, code++);

    // We used to have a class named HMsg but its been removed.  Rather than
    // just axe it, use following random Integer class -- we just chose any
    // class from java.lang -- instead just so codes that follow stay
    // in same relative place.
    addToMap(Integer.class, code++);
    addToMap(Integer[].class, code++);

    addToMap(HRegion.class, code++);
    addToMap(HRegion[].class, code++);
    addToMap(HRegionInfo.class, code++);
    addToMap(HRegionInfo[].class, code++);
    addToMap(HServerAddress.class, code++);
    addToMap(HServerInfo.class, code++);
    addToMap(HTableDescriptor.class, code++);
    addToMap(MapWritable.class, code++);

    //
    // HBASE-880
    //
    addToMap(ClusterStatus.class, code++);
    addToMap(Delete.class, code++);
    addToMap(Get.class, code++);
    addToMap(KeyValue.class, code++);
    addToMap(KeyValue[].class, code++);
    addToMap(Put.class, code++);
    addToMap(Put[].class, code++);
    addToMap(Result.class, code++);
    addToMap(Result[].class, code++);
    addToMap(Scan.class, code++);

    addToMap(WhileMatchFilter.class, code++);
    addToMap(PrefixFilter.class, code++);
    addToMap(PageFilter.class, code++);
    addToMap(InclusiveStopFilter.class, code++);
    addToMap(ColumnCountGetFilter.class, code++);
    addToMap(SingleColumnValueFilter.class, code++);
    addToMap(SingleColumnValueExcludeFilter.class, code++);
    addToMap(BinaryComparator.class, code++);
    addToMap(BitComparator.class, code++);
    addToMap(CompareFilter.class, code++);
    addToMap(RowFilter.class, code++);
    addToMap(ValueFilter.class, code++);
    addToMap(QualifierFilter.class, code++);
    addToMap(SkipFilter.class, code++);
    addToMap(WritableByteArrayComparable.class, code++);
    addToMap(FirstKeyOnlyFilter.class, code++);
    addToMap(DependentColumnFilter.class, code++);

    addToMap(Delete [].class, code++);

    addToMap(HLog.Entry.class, code++);
    addToMap(HLog.Entry[].class, code++);
    addToMap(HLogKey.class, code++);

    addToMap(List.class, code++);

    addToMap(NavigableSet.class, code++);
    addToMap(ColumnPrefixFilter.class, code++);

    // Multi
    addToMap(Row.class, code++);
    addToMap(Action.class, code++);
    addToMap(MultiAction.class, code++);
    addToMap(MultiResponse.class, code++);

    // coprocessor execution
    addToMap(Exec.class, code++);
    addToMap(Increment.class, code++);

    addToMap(KeyOnlyFilter.class, code++);

    // serializable
    addToMap(Serializable.class, code++);

    addToMap(RandomRowFilter.class, code++);

    addToMap(CompareOp.class, code++);

    addToMap(ColumnRangeFilter.class, code++);

    addToMap(HServerLoad.class, code++);

    addToMap(RegionOpeningState.class, code++);

    addToMap(HTableDescriptor[].class, code++);

    addToMap(Append.class, code++);

    addToMap(RowMutations.class, code++);

    addToMap(Message.class, code++);

    //java.lang.reflect.Array is a placeholder for arrays not defined above
    GENERIC_ARRAY_CODE = code++;
    addToMap(Array.class, GENERIC_ARRAY_CODE);

    // make sure that this is the last statement in this static block
    NEXT_CLASS_CODE = code;
  }

那么上面代码中涉及的类,默认就能够在coprocessor中传递了,这也是我们在使用coprocessor传递普通类型对象时无需干预的原因。但是一旦需要传递自定义的对象,就需要了解coprocessor的对象传递机制。HbaseObjectWritable对每类对象都进行了编码,code。所以如果自定义对象需要在coprocessor中传递,需要能够属于HbaseObjectWritable已编码类型的对象,或者修改HbaseObjectWritable。这里推荐采用前一种,只需要在自定义的类中implements Serializable接口即可。


      3、hbase常用运维命令      

一,基本命令:

    建表:create 'testtable','coulmn1','coulmn2'

     也可以建表时加coulmn的属性如:create 'testtable',{NAME => 'coulmn1', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', VERSIONS => '10', COMPRESSION => 'LZO', TTL => '30000', IN_MEMORY => 'false', BLOCKCACHE => 'false'}, {NAME => 'coulmn', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', VERSIONS => '30', COMPRESSION => 'LZO', TTL => '30000', IN_MEMORY => 'true'}  (其中的属性有versions:设置历史版本数,TTL:过期时间,COMPRESSION:压缩方式,当配置lzo的情况)

    删除表:drop 'testtable'   (删除表之前先要禁用表,命令disable 'testtable')

    启用和禁用表: enable 'testtable' 和disable 'testtable'

    其它的基本命令:describe 'testtable'(查看表结构),alert 修改表结构,list 列出所有表。

二,日常维护的命令

    1,major_compact 'testtable',通常生产环境会关闭自动major_compact(配置文件中hbase.hregion.majorcompaction设 为0),选择一个晚上用户少的时间窗口手工major_compact,如果hbase更新不是太频繁,可以一个星期对所有表做一次 major_compact,这个可以在做完一次major_compact后,观看所有的storefile数量,如果storefile数量增加到 major_compact后的storefile的近二倍时,可以对所有表做一次major_compact,时间比较长,操作尽量避免高锋期。

    2,flush 'testtable',将所有memstore刷新到hdfs,通常如果发现regionserver的内存使用过大,造成该机的 regionserver很多线程block,可以执行一下flush操作,这个操作会造成hbase的storefile数量剧增,应尽量避免这个操 作,还有一种情况,在hbase进行迁移的时候,如果选择拷贝文件方式,可以先停写入,然后flush所有表,拷贝文件。

    3,balance_switch true或者balance_switch flase,配置master是否执行平衡各个regionserver的region数量,当我们需要维护或者重启一个regionserver时,会 关闭balancer,这样就使得region在regionserver上的分布不均,这个时候需要手工的开启balance。

三,重启一个regionserver

    bin/graceful_stop.sh --restart --reload --debug nodename

    这个操作是平滑的重启regionserver进程,对服务不会有影响,他会先将需要重启的regionserver上面的所有 region迁移到其它的服务器,然后重启,最后又会将之前的region迁移回来,但我们修改一个配置时,可以用这种方式重启每一台机子,这个命令会关 闭balancer,所以最后我们要在hbase shell里面执行一下balance_switch true,对于hbase regionserver重启,不要直接kill进程,这样会造成在zookeeper.session.timeout这个时间长的中断,也不要通过 bin/hbase-daemon.sh stop regionserver去重启,如果运气不太好,-ROOT-或者.META.表在上面的话,所有的请求会全部失败。

四,关闭下线一台regionserver

    bin/graceful_stop.sh  nodename

    和上面一样,系统会在关闭之前迁移所有region,然后stop进程,同样最后我们要手工balance_switch true,开启master的region均衡。

五,检查region是否正常以及修复

    bin/hbase hbck  (检查)

    bin/hbase hbck -fix  (修复)

    会返回所有的region是否正常挂载,如没有正常挂载可以使用下一条命令修复,如果还是不能修复,那需要看日志为什么失败,手工处理。

六,hbase的迁移

    1,copytable方式

    bin/hbase org.apache.hadoop.hbase.mapreduce.CopyTable --peer.adr=zookeeper1,zookeeper2,zookeeper3:/hbase 'testtable'

    目前0.92之前的版本的不支持多版本的复制,0.94已经支持多个版本的复制。当然这个操作需要添加hbase目录里的conf/mapred-site.xml,可以复制hadoop的过来。

    2,Export/Import

    bin/hbase org.apache.hadoop.hbase.mapreduce.Export testtable /user/testtable [versions] [starttime] [stoptime]

    bin/hbase org.apache.hadoop.hbase.mapreduce.Import testtable  /user/testtable

    跨版本的迁移,我觉得是一个不错的选择,而且copytable不支持多版本,而export支持多版本,比copytable更实用一些。

    3,直接拷贝hdfs对应的文件

    首先拷贝hdfs文件,如bin/hadoop distcp hdfs://srcnamenode:9000/hbase/testtable/ hdfs://distnamenode:9000/hbase/testtable/

    然后在目的hbase上执行bin/hbase org.jruby.Main bin/add_table.rb /hbase/testtable

    生成meta信息后,重启hbase

    这个操作是简单的方式,操作之前可以关闭hbase的写入,执行flush所有表(上面有介绍),再distcp拷贝,如果hadoop版本不一致,可以用hftp接口的方式,我推荐使用这种方式,成本低。

    先总结这么多,有空我再详细写一下region坏时怎么修复。


你可能感兴趣的:(hbase使用、运维、改进(不断更新))