hbase scan中使用setTimeRange要注意的两个地方

在hbase scan中为了提高效率,一般采用setStartRow和setStopRow或者采用filter的方式来减少结果集。还有一种方式就是用setTimeRange,它可以指定返回指定时间范围内的columns数据。对setTimeRange函数的使用,有两个特别需要注意的地方:

(1)  setTimeRange指定的是columns(列,也叫qualify)的时间范围,而不是columns family(列族)的范围。

这样如果一个表的多个columns的时间不一致,那么scan的返回结果就可能只包含一个columns的数据,其他columns的数据并不会返回。原因是:hbase中插入数据时,数据的列和值是存在KeyValue中,KeyValue的时间就是当前时间。KeyValue会被加入到columns family中,而columns family是不带时间戳的。

例如,t1表的数据为:

hbase(main):070:0> scan 't1'
ROW                                      COLUMN+CELL                                                                                                         
 row1                                    column=f1:a, timestamp=1362672126412, value=value1                                                                  
 row1                                    column=f1:b, timestamp=1362672134437, value=value2                                                                  
 row1                                    column=f2:a, timestamp=1362672143116, value=value3                                                                  
 row1                                    column=f2:b, timestamp=1362672148839, value=value4                                                                  
1 row(s) in 0.0120 seconds

按照不同的时间范围,返回的columns是不一样的:

hbase(main):073:0> scan 't1', { TIMERANGE => [1362672126412, 1362672134437]}
ROW                                      COLUMN+CELL                                                                                                         
 row1                                    column=f1:a, timestamp=1362672126412, value=value1                                                                  
1 row(s) in 0.0060 seconds

hbase(main):074:0> scan 't1', { TIMERANGE => [1362672126412, 1362672134438]}
ROW                                      COLUMN+CELL                                                                                                         
 row1                                    column=f1:a, timestamp=1362672126412, value=value1                                                                  
 row1                                    column=f1:b, timestamp=1362672134437, value=value2                                                                  
1 row(s) in 0.0070 seconds

对于这种情况,在程序中需要特别注意不要遗漏某个column的数据。

(2)  scan如果指定了TimeRange,那么可能返回旧版本的数据。

先创建一张表,注意在创建表的时候故意只指定了一个版本:

create 'baoniu',{NAME=>'cf',VERSION=>1} // 一个版本
put 'baoniu','row1','cf:a','value1'
put 'baoniu','row2','cf:a','value2'
put 'baoniu','row3','cf:a','value3'
put 'baoniu','row3','cf:a','value4'

hbase(main):045:0> scan 'baoniu'
ROW                                      COLUMN+CELL
 row1                                    column=cf:a, timestamp=1362663065599, value=value1
 row2                                    column=cf:a, timestamp=1362663072949, value=value2
 row3                                    column=cf:a, timestamp=1362663086941, value=value4
3 row(s) in 0.0170 seconds

在 baoniu 这张表中,有一个cf列族,包含a列,row3这行记录是插入了两条。显然在scan的时候是返回最新的一条了。

如果指定了时间范围呢?试一下:

hbase(main):046:0> scan 'baoniu',{TIMERANGE=>[1362663065599,1362663086941]}
ROW                                      COLUMN+CELL                                                                                                         
 row1                                    column=cf:a, timestamp=1362663065599, value=value1                                                                  
 row2                                    column=cf:a, timestamp=1362663072949, value=value2                                                                  
 row3                                    column=cf:a, timestamp=1362663084573, value=value3                                                                  
3 row(s) in 0.0110 seconds

可以看到,row3这条记录返回的值是value3。再试一下:

hbase(main):048:0> scan 'baoniu',{TIMERANGE=>[1362663065599,1362663086942]}
ROW                                      COLUMN+CELL                                                                                                         
 row1                                    column=cf:a, timestamp=1362663065599, value=value1                                                                  
 row2                                    column=cf:a, timestamp=1362663072949, value=value2                                                                  
 row3                                    column=cf:a, timestamp=1362663086941, value=value4                                                                  
3 row(s) in 0.0100 seconds

这时是和预期的一样,row3这条记录返回的是最新的值value4了。

为什么明明只指定了一个版本,但是设置不同的时间条件,竟然可以分别返回两个版本的数据?这是因为(row3, value3)这行记录虽然被hbase标记为”需要删除”,但是真正的删除在compaction阶段才会进行。(row3, value3)这条记录通过get和普通的scan都是无法访问到的,只有当设置了TimeRange才能访问到。

所以,对于scan时设置了TimeRange的程序来说,要注意可能会得到一些旧版本的数据。

此外:scan的时候可以通过 setMaxVersions() 来对每个column返回指定版本个数的数据

Result.getColumn()是返回指定列上的所有版本数据

Result.getColumnLatest可以返回最新版本的数据

附一些测试代码:

import java.util.List;

import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.util.Bytes;

public class TestScanTimeRange {

	public static void main(String[] args) throws Exception {
		HTable htable = new HTable(HBaseConfiguration.create(), "baoniu");
		Scan scan = new Scan();
		scan.setStartRow(Bytes.toBytes("row1"));
		scan.setStopRow(Bytes.toBytes("row4"));
		scan.setTimeRange(1362663065599L, 1362663086941L);
		// scan.setMaxVersions(1);
		ResultScanner rs = htable.getScanner(scan);
		for (Result res : rs) {
			List<KeyValue> kv = res.getColumn((Bytes.toBytes("cf")), Bytes.toBytes("a"));
			System.out.println("kv size=" + kv.size());
			for (KeyValue keyValue : kv) {
				System.out.print(new String(keyValue.getRow()) + " ");
				System.out.print(new String(keyValue.getFamily()) + ":");
				System.out.print(new String(keyValue.getQualifier()) + " ");
				System.out.print(keyValue.getTimestamp() + " ");
				System.out.println(new String(keyValue.getValue()));
			}
		}
	}
}

参考:http://blog.iamzsx.me/show.html?id=193002

发表评论

电子邮件地址不会被公开。 必填项已用*标注