刚刚看了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坏时怎么修复。