记录一次HBase的scan的分页查询

修复前任代码中HBase查询慢的问题,通过添加时间戳范围限制提高查询效率,并采用自定义分页策略,避免一次性加载大量数据。调整Scan设置,结合PrefixFilter限制查询范围,实现按需加载数据。测试显示,查询速度得到显著提升。
摘要由CSDN通过智能技术生成

修改前任bug,Hbase查询过于慢了,以至于都查不出来了,看了代码发现使用的Scan只设置了withStartRow、withEndRow、setCaching扫描,拿到全部数据后存入集合再subList进行分页,但是HBase中存在某些数据有几百万条,根本scan不出来了。

前任设置如下: 其中start 和 end 拼接0和z是因为HBase中RowKey按照字典顺序排序,

String start = rowKey + "0";
String end = rowKey + "z";
Scan scan = new Scan();
scan.withStartRow(start.getBytes());
scan.withStopRow(end.getBytes());
scan.setCaching(1000);
scan.setCacheBlocks(false);

先根据业务逻辑找他的问题,首先没限定时间戳,然后这样分页数据量大就出现问题了。

1.加上时间戳限定范围

时间根据自己的时间来定,我这里是查询前一天的数据,所以就是零点到末尾

try {
	scan.setTimeRange(getStartOfDay("2021-11-23"), getEndOfDay("2021-11-23"));
} catch (IOException e) {
	e.printStackTrace();
}

日期代码网上找的,一搜一堆

    // 获取指定日期 的 零点时间戳
    public static Long getStartOfDay(String date) {
        if (StringUtils.isEmpty(date)) {
            date = date2String(new Date(), "yyyy-MM-dd");
        }
        DateTimeFormatter ofPattern = DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.CHINA);
        LocalDate parse = LocalDate.parse(date, ofPattern);
        return parse.atStartOfDay().toInstant(ZoneOffset.of("+8")).toEpochMilli();
    }
    // 获取指定日期 的 末尾时间戳
    public static Long getEndOfDay(String date) {
        if (StringUtils.isEmpty(date)) {
            date = date2String(new Date(), "yyyy-MM-dd");
        }
        DateTimeFormatter ofPattern = DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.CHINA);
        LocalDate parse = LocalDate.parse(date, ofPattern);
        return LocalDateTime.of(parse, LocalTime.MAX).toInstant(ZoneOffset.of("+8")).toEpochMilli();
    }
    
    public static String date2String(Date date, String format) {
        SimpleDateFormat yyyy_MM_dd = new SimpleDateFormat(format);
        return yyyy_MM_dd.format(date);
    }

	//获取前多少小时到现在的时间范围
	//可以将 cal1.add(11, -(hour + 2))进行修改,我这里是当前时间往前走俩小时,hour是前多少小时
	public static long[] getHourTimeLimit(int hour) {
        Calendar cal1 = Calendar.getInstance();
        Date date = new Date();
        cal1.setTime(date);
        cal1.add(11, -(hour + 2));
        Date d = cal1.getTime();
        return new long[]{d.getTime(), date.getTime()};
    }

2.修改分页查询

1.网上找到的实现方式

Hbase有一个PageFilter(x),设置一个数,往后查询多少个
但是有个问题就是需要设置具体的withStartRow,从withStartRow开始往后查询x个,需要先找到startrow,每次根据startrow往后查x,感觉有点冗余就没用,自己想了个思路。
链接贴上:想了解可以看看
https://blog.csdn.net/HANLIPENGHANLIPENG/article/details/53203646

2.自己实现的思路

scan的设置:

public Scan getScan(String rowKey){
	Scan scan = new Scan();
	String start = rowKey + "0";
	scan.withStartRow(start.getBytes());
	scan.setCaching(6000);
	scan.setCacheBlocks(false);
	scan.setMaxResultSize(2 * 1024 * 1024 * 100);
	try {
		scan.setTimeRange(getStartOfDay("2021-11-23"), getEndOfDay("2021-11-23"));
	} catch (IOException e) {
		e.printStackTrace();
	}
	scan.setFilter(new PrefixFilter(rowKey));
	return scan;
}
大概意思就是:使用scanner.next(pageSize)根据页数来轮询,轮询到当前页就将数据进行解析,结束循环返回。数据量大估计也需要挺长时间,测试了一下每页20条,1000页以内都还挺快的

前提是将scan.setCaching(6000);这个参数调到适合自己的集群,这将决定你多页查询的速度,或者将他设置为一个范围,根据当前页大小来做判断设置不同的大小,页数小就设置小点,页数大就相对大一些,尽量一次缓存能查找到,这样速度就会更快。

 scan.setCaching(pageSize * currentPage > 6000 ? 6000 : pageSize * currentPage);
Scan scan = getScan(rowKey);
//设置上要查询的列
for (int i = 0; i < theHour; i++) {
	long hour = this.timeUtil.getHour(i);
	scan.addColumn(Bytes.toBytes("D"), String.format("%02d", new Object[]{Long.valueOf(hour)}).getBytes());
}

List<xxxx> alls = new ArrayList<>();
long count = 0L;
String startRow;
try (Table table = this.hbaseConnection.getTable(TableName.valueOf("table"));
		ResultScanner scanner = table.getScanner(scan)) {
	Result[] results;
	int pageCount = 0;
	while ((results = scanner.next(pageSize)).length != 0) {
		pageCount++;
		if (pageCount < currentPage) {
			continue;
		}
		for (Result rs : results) {
			//在此处解析获取数据
			alls.add(xxxx)
		}
		break;
	}
} catch (Exception e) {
	log.error("", e);
}

我查询第600页每页10个,算上解析,用时2278ms,速度还可以吧,只能说相比前任已经是可以查出来了,后面在做优化吧,第一种方法没尝试,感觉上来讲第一种也是一个一个扫描的,不知道速度如何,有时间的时候在做下测试吧。

[root@11 scanHbaseTest]# java -jar scanHbaseTest-1.0-SNAPSHOT.jar xxxxxx 600 10 today
get Results time = 2278ms

Hbase分页查询还真不太好弄,除非能缓存一下每页的第一条数据,这样速度会快很多吧,但是不太现实。

目前也没想到特别好的办法,如果有可以分享给我。

HBase查询优化续集HBase并发查询

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
HBase是一种分布式的NoSQL数据库,它并不支持传统的SQL语句,但它提供了Scan类来进行数据的扫描和检索。在HBase中实现分页查询需要结合Scan类和分页参数来完成。 以下是在HBase中进行分页查询的步骤: 1. 创建Scan对象,设置起始行和结束行。 2. 设置分页参数,包括每页数据条数和当前页码。 3. 遍历Scan对象获取结果集,并计算偏移量和限制条数。 4. 对结果集进行分页处理,返回分页数据。 下面是一个示例代码,实现了在HBase中进行分页查询的功能: ```java // 创建Scan对象 Scan scan = new Scan(); scan.setStartRow(Bytes.toBytes(startRow)); scan.setStopRow(Bytes.toBytes(stopRow)); // 设置分页参数 int pageSize = 10; int pageNum = 1; // 计算偏移量和限制条数 int offset = (pageNum - 1) * pageSize; int limit = pageSize; // 遍历Scan对象获取结果集 ResultScanner scanner = table.getScanner(scan); Result[] results = scanner.next(limit + offset); // 对结果集进行分页处理 List<Map<String, Object>> pageData = new ArrayList<>(); for (int i = offset; i < results.length && i < offset + limit; i++) { Result result = results[i]; Map<String, Object> rowData = new HashMap<>(); for (Cell cell : result.rawCells()) { rowData.put(Bytes.toString(CellUtil.cloneQualifier(cell)), Bytes.toString(CellUtil.cloneValue(cell))); } pageData.add(rowData); } ``` 在上面的示例代码中,我们使用了HBase的Scan类来进行数据的扫描和检索,并结合分页参数和偏移量来完成分页查询的功能。注意,这里的分页查询是基于行级别的,而非基于列级别的。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值