参数hive.cli.errors.ignore表示在cli的情况是否忽略错误,默认值是false,表示不忽略。
有的时候通过hive -f xx.sql执行的时候,希望即使前面的sql有问题,后面的也要继续执行,那么当遇到错误的时候就需要忽略掉。
此时只需要修改hive.cli.errors.ignore为true即可。

通过distcp可以实现在两个hadoop集群中同步数据,当两个hadoop集群的版本相同的时候比较简单,直接执行就可以了,比如:
hadoop distcp hdfs://192.168.0.138:9000/sunwg hdfs://192.168.0.139:9000/sunwg
执行时,会在启动mapreduce进行文件的拷贝,最后138上的sunwg下的文件全部会拷贝到139下面的sunwg下。
如果两个hadoop集群的hadoop版本不一致,就稍微会麻烦些。因为不同版本的hadoop是没有办法通过hdfs的协议直接访问的,此时需要采用hftp的方式。hftp是hadoop的一种只读的文件访问协议,没有办法进行更新。所以此时,如果想把0.19上的数据同步到0.20上,那么此时distcp要在对0.20有写权限的机器上发起;反之,要在0.19的机器上启动。比如:
hadoop distcp hftp://192.168.0.159:50070/sunwg hdfs://192.168.0.128:9000/sunwg/test01
这样就可以跨hadoop的版本来拷贝数据。

另,我测试是在虚拟机上进行的,所有的虚拟机都是一台虚拟机复制得到的,所有的虚拟机的hostname都是一样的,所以昨天测试的时候一直有问题,通过hftp访问具体文件的时候有下面的错误:
[hadoop@hadoop01 ~]$ hadoop fs -cat hftp://192.168.0.159:50070/sunwg/1.dat
Invalid input[hadoop@hadoop01 ~]$
一直报的Invalid input。这样的话使用distcp同步数据也是有问题的。后来修改了hostname,两台机器的名字不一样,错误就消失了。

hbase.regionserver.regionSplitLimit用来控制hbase集群中总的region的个数,当hbase集群中的region个数超过这个限制则会停止regionsplit操作。
hbase.regionserver.regionSplitLimit
Limit for the number of regions after which no more region splitting should take place. This is not a hard limit for the number of regions but acts as a guideline for the regionserver to stop splitting after a certain limit. Default is set to MAX_INT; i.e. do not block splitting.
Default: 2147483647

  private boolean shouldSplitRegion() {
    return (regionSplitLimit > server.getNumberOfOnlineRegions());
  }

hbase.client.write.buffer控制hbase的client端写buffer的大小.hbase文档对该参数的说明如下:
hbase.client.write.buffer
Default size of the HTable clien write buffer in bytes. A bigger buffer takes more memory — on both the client and server side since server instantiates the passed write buffer to process it — but a larger buffer size reduces the number of RPCs made. For an estimate of server-side memory-used, evaluate hbase.client.write.buffer * hbase.regionserver.handler.count
Default: 2097152

hbase.client.write.buffer设置大些会减少client和server端见的RPC次数,但相应的每次通信的数据量要增加。评估server端的内存使用为hbase.client.write.buffer * hbase.regionserver.handler.count。

client端的put操作的数据要首先写到hbase.client.write.buffer中,当满足一定的条件则将buffer中的数据传输到server端。相关代码如下:

  private void doPut(final List<Put> puts) throws IOException {
    int n = 0;
    for (Put put : puts) {
      validatePut(put);
      writeBuffer.add(put);
      currentWriteBufferSize += put.heapSize();

      // we need to periodically see if the writebuffer is full instead of waiting until the end of the List
      n++;
      if (n % DOPUT_WB_CHECK == 0 && currentWriteBufferSize > writeBufferSize) {
        flushCommits();
      }
    }
    if (autoFlush || currentWriteBufferSize > writeBufferSize) {
      flushCommits();
    }
  }

flushCommits的条件有两个,一个是autoFlush参数,另外一个是当前buffer的尺寸超过hbase.client.write.buffer。这也是当我们设置autoFlush参数为false对性能有提升的原因,减少了RPC的次数。

Bloom filters是一种基于hash的快速查找算法,可以实现o(1)的查找时间。但是有个问题,就是说会有误差。如果Bloom filters计算该值不在集合中,那么该值肯定不在集合中;反过来,如果Bloom filters计算该值在集合中,那该值有可能确实在,也有可能不在。所以用Bloom filters来实现查找,还是要看具体的应用场景的。
hbase中也是支持的Bloom filters查找,就是对于hfile的查找来说,先查找Bloom filters头,如果不在那么就没有必要搜索该文件。
创建支持的Bloom filters的表的代码如下:

import java.io.IOException;

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.MasterNotRunningException;
import org.apache.hadoop.hbase.ZooKeeperConnectionException;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.regionserver.StoreFile;
import org.apache.hadoop.hbase.util.Bytes;

public class test03 {

	public static void main(String[] args) throws IOException {

		Configuration conf = null;
		conf = HBaseConfiguration.create();
		conf.set("hbase.zookeeper.quorum", "192.168.0.128");
		conf.set("hbase.zookeeper.property.clientPort", "2181");

		HBaseAdmin admin = new HBaseAdmin(conf);

		HTableDescriptor tableDesc = new HTableDescriptor("sunwg01");

		HColumnDescriptor cd = new HColumnDescriptor(Bytes.toBytes("f1"));
		cd.setBloomFilterType(StoreFile.BloomType.ROW);

		tableDesc.addFamily(cd);

		admin.createTable(tableDesc);

	}

}

插入几条记录,并flush
hbase(main):005:0> put ‘sunwg01′,’100′,’f1:k1′,’a01′
0 row(s) in 0.0580 seconds

hbase(main):006:0> put ‘sunwg01′,’101′,’f1:k1′,’a02′
0 row(s) in 0.0170 seconds

hbase(main):007:0> flush ‘sunwg01′
0 row(s) in 0.1160 seconds

查看sunwg01的hfile
[hadoop@hadoop01 hadoop]$ hbase org.apache.hadoop.hbase.io.hfile.HFile -m -f hdfs://hadoop01:9000/hbase/sunwg01/641cfd6c6074e94b9f85074cfd6b7df5/f1/ed8c507c0f3e4d58bcf269784c105e02
12/05/05 23:12:33 INFO hfile.CacheConfig: Allocating LruBlockCache with maximum size 24.6m
12/05/05 23:12:33 INFO hfile.CacheConfig: sunwg LruBlockCache
Block index size as per heapsize: 288
reader=hdfs://hadoop01:9000/hbase/sunwg01/641cfd6c6074e94b9f85074cfd6b7df5/f1/ed8c507c0f3e4d58bcf269784c105e02,
compression=none,
cacheConf=CacheConfig:enabled [cacheDataOnRead=true] [cacheDataOnWrite=false] [cacheIndexesOnWrite=false] [cacheBloomsOnWrite=false] [cacheEvictOnClose=false] [cacheCompressed=false],
firstKey=100/f1:k1/1336230632597/Put,
lastKey=101/f1:k1/1336230637114/Put,
avgKeyLen=19,
avgValueLen=3,
entries=2,
length=844
Trailer:
fileinfoOffset=194,
loadOnOpenDataOffset=114,
dataIndexCount=1,
metaIndexCount=0,
totalUncomressedBytes=788,
entryCount=2,
compressionCodec=NONE,
uncompressedDataIndexSize=32,
numDataIndexLevels=1,
firstDataBlockOffset=0,
lastDataBlockOffset=0,
comparatorClassName=org.apache.hadoop.hbase.KeyValue$KeyComparator,
version=2
Fileinfo:
BLOOM_FILTER_TYPE = ROW
KEY_VALUE_VERSION = \x00\x00\x00\x01
LAST_BLOOM_KEY = 101
MAJOR_COMPACTION_KEY = \x00
MAX_MEMSTORE_TS_KEY = \x00\x00\x00\x00\x00\x00\x00\x00
MAX_SEQ_ID_KEY = 57751
TIMERANGE = 1336230632597….1336230637114
hfile.AVG_KEY_LEN = 19
hfile.AVG_VALUE_LEN = 3
hfile.LASTKEY = \x00\x03101\x02f1k1\x00\x00\x017\x1D\x8Dn:\x04
Mid-key: \x00\x03100\x02f1k1\x00\x00\x017\x1D\x8D\\x95\x04
Bloom filter:
BloomSize: 4
No of Keys in bloom: 2
Max Keys for bloom: 3
Percentage filled: 67%
Number of chunks: 1
Comparator: ByteArrayComparator
最后一部分就是关于Bloom filter的相关信息

hbase的文档对于hfile.index.block.max.size的说明如下:
When the size of a leaf-level, intermediate-level, or root-level index block in a multi-level block index grows to this size, the block is written out and a new block is started.
Default: 131072
这个参数控制hfile中索引块的大小,默认值是128K,也就是说当索引的信息超过128K后,就会新分配一个索引块。hbase对于hfile的访问都是通过索引块来实现的,通过索引来定位所要查的数据到底在哪个数据块里面。hfile中的索引块可以分成三中,根索引块,枝索引块,叶索引块。根索引块是一定会有的,但是如果hfile中的数据块比较少的话,枝索引块和叶索引块就可能不存在。当单个的索引块中没有办法存储全部的数据块的信息时,索引块就会分裂,会产生叶索引块和根索引块,根索引块是对叶索引块的索引,如果数据块继续增加就会产生枝索引块,整个索引结果的层次也会加深。
想象一下,如果整个hfile中只有根索引块,那么访问真正的数据的路径是,首先查根索引块定位数据块的位置,然后去查询数据块找到需要的数据。整个过程涉及到一次对索引块的扫描和一次对数据块的扫描。
如果hfile总块比较多,整个索引结构有2次的话,访问的路径是,首先访问根索引块定位叶索引块,访问叶索引块定位数据块,整个过程涉及到两次对索引块的扫描和一次对数据块的扫描。
整个索引树的深度越深,那么访问过程就越长,相应的扫描的时间也会越长。
那是不是把hfile.index.block.max.size设置得越大越好呢?也不是的,如果索引块太大了,对索引块本身的扫描时间就会显著的增加的。
最后补充一句,根索引块一定是被缓存到内存中的,这个是在hfile打开的时候就缓存的。

blockcache前面已经有文章简单的进行了介绍。blockcache主要是为了提高对storefile的访问效率而增加的内存结构,通过将常访问的数据缓存在内存中来实现。
其实hbase中除了常常介绍的lrublockcache以外,还有一种是slabcache。
先简单的说明下slabcache。如果对oracle的cache管理比较熟悉的话就很容易理解slabcache,他们的基本原理是相同的。将cache根据不同的slabsize分成若干小的cache,当block数据cache到slabcache中的时候,首先要计算该block的大小,如果该block太大,找不到合适的slab则不进行缓存,直接丢弃。如果找到合适的slab,那么就将该数据存储在相应的slab中。在插入的过程中通过大小来判断,而在对slabsize则利用另外的索引结构,通过BlockCacheKey结构来查询,该结构由hfileName和offset组成。slabcache结构不是使用的lru算法进行数据的淘汰,而是简单的先进先出队列。可以想象,slabcache的结构比较简单,存储效率高,但是淘汰算法比较差。
插一句,默认情况下,hbase选择的是lrublockcache,而当我们设置了hbase.offheapcache.percentage等参数后,会启用DoubleBlockCache,实际上就是lrublockcache和slabcache的混合。
在说平时最常使用lrublockcache。根据最近使用的情况来进行淘汰,经常使用的会被保留,而不经常使用会被淘汰。hbase中的lrublockcache分成三个层次:memory,single,multi。
memory级别不会受到其他两个级别的影响,只有那些指定blockcache为memory的表的数据才会设置成memory,系统表.META.和-ROOT-默认的会被指定为memory。
single级别是那些第一被访问并放在在blockcache中的数据的级别,而当single中的数据第二次被访问后级别会升到multi级别。这样可以很好的防止单次大数据量访问对blockcache的清洗,至于multi级别的数据可以免于被清洗掉。
默认情况下,三者的分配比例为,memory为25%,single为25%,multi为50%。
淘汰数据的进程会一直的运行,找到那些需要被淘汰的数据。该进程分别统计各个级别的数据,如果总量超过DEFAULT_ACCEPTABLE_FACTOR(0.85)的限制,就会被强制的进行淘汰,直到到达安全线以下,该安全线应该是DEFAULT_MIN_FACTOR(0.75)指定的。

memstore在hbase中是很重要的部分,对于hbase的写操作会首先写WAL(可以通过参数设置取消写WAL),然后在写memstore。不管是否写WAL,memstore是一定要写的,而且memstore也是hbase写操作性能的保证,可以想象如果直接写hdfs,不可能有目前的速度和效率。
在向memstore写数据一段时候,在达到参数hbase.hregion.memstore.flush.size限制的时候,就需要将memstore中的数据写到hdfs上,生成一个新的storefile。这个写的过程,是首先在tmp文件夹下写临时的文件,当memstore中需要刷新到hdfs上的数据全部写完毕后,在将该文件从tmp文件夹下转移到相应的store文件夹下面。
那么,如何确定memstore中到底哪些数据要被写到hdfs上呢,整个flush的过程是否运行新的针对该store的写操作呢?
在memstore中有两个很重要的结构:kvset和snapshot,他们的类型都是KeyValueSkipListSet。我们对于hbase所有的写操作,都是直接对kvset的操作,而snapshot是为了实现flush操作而创建的。大概的flush过程如下:
当该memstore收到进行flush的请求后,首先把kvset中的数据复制到snapshot中,这个复制的过程对store是要加写锁的,此时不能写,但这个过程是内存中的拷贝,速度比较快。当拷贝完毕后,kvset被清空。写锁释放,kvset接受新的kv的写入请求,那么snapshot中的全部数据就是需要flush到hdfs中的数据。此时需要查询该结构,然后将数据写到hdfs即可,全部写完后snapshot也要清空,为下次的flush做准备。在flush的过程,会进行一些逻辑上的操作,删掉一些无效的数据,比如超过table指定最大version的记录。
进一步思考,在flush的开始,会把数据从kvset拷贝到snapshot上,此时如果有对memstore的查询操作怎么办呢?答案是,在对memstore的读操作的时候,会同时读取kvset和snapshot,然后把取到的结果做比较后返回。

hbase.client.keyvalue.maxsize用来控制keyvalue的长度,当keyvalue的长度超过参数限制的时候会如下的错误:
ERROR: java.lang.IllegalArgumentException: KeyValue size too large

默认的情况下,hbase对keyvalue的长度是没有限制的.
而当我们设置了hbase.client.keyvalue.maxsize,那么hbase就会在put执行前先检查下keyvalue的长度时候超过了该限制.
keyvalue的长度包括,key,column,value的总长度.

假如hbase.client.keyvalue.maxsize参数设置的过小,那么hbase的.META.,-ROOT-可能也会出现问题,此时你没有办法做相关的操作,
在region server的logs中可以看到同样的错误,java.lang.IllegalArgumentException: KeyValue size too large

split可以由hbase自动触发,也可以由手工的触发。自动触发或者手工触发不指定split key的时候,hbase会自己计算split key。
大概规则是:先查找该region所包含的最大的store,然后在查找最大store中最大的storefile,而split key就是最大的storefile的midkey。

根据上面的规则就会有些问题,如果同一个region的不同的store的范围不大一样,那么进行split的效果并不是太好。

看下面的例子:

hbase(main):029:0> create ‘sunwg01′,’f1′,’f2′
0 row(s) in 1.0570 seconds

hbase(main):030:0> put ‘sunwg01′,’100′,’f1:k1′,’aa’
0 row(s) in 0.0370 seconds

hbase(main):031:0> put ‘sunwg01′,’101′,’f1:k1′,’bb’
0 row(s) in 0.0160 seconds

hbase(main):032:0> put ‘sunwg01′,’102′,’f1:k1′,’cc’
0 row(s) in 0.0140 seconds

hbase(main):033:0> put ‘sunwg01′,’108′,’f2:k2′,’dd’
0 row(s) in 0.0450 seconds

hbase(main):034:0> put ‘sunwg01′,’109′,’f2:k2′,’ee’
0 row(s) in 0.0190 seconds

hbase(main):035:0> flush ‘sunwg01′
0 row(s) in 0.1390 seconds

创建个表sunwg01,有两个列组f1和f2,f1中的storefile有3条记录,大概666字节;f2中的storefile有2条记录,大概为636字节。根据前面的规则,那么当这个region进行split的时候,split key是由f1中的storefile决定的。

hbase(main):036:0> split ‘sunwg01′
0 row(s) in 0.0730 seconds

查看该表的region split情况,发现split key为100。按照100来对f2进行split的意义不大。
这也就是要求我们如果创建多个列组的时候,各个列组的分布范围要大概相似。
当然,最好还是象hbase建议的,列组尽量少,最好只有1个。

另一方面,split的单位是region,当split发生的时候,不管你的各个列组中到底有多少记录都要跟着split。比如,列组1有1亿条记录,列组2有100条记录,当列组1需要分裂的时候,即使列组2只有100记录也要跟着分裂的。这要求我们,如果创建多个列组的时候,各个列组的数据量最好也要相当。
在说一次hbase的建议,列组尽量少,最好只有1个。

© 2011 ORATEA Suffusion WordPress theme by Sayontan Sinha