表格存儲是一個分布式存儲系統,對于查詢請求的翻頁(分頁)有多種方式。本文詳細介紹如何實現分頁查詢。
表
如果只有表,沒有多元索引,您可以通過以下方式進行翻頁:
通過表主鍵查詢數據時不支持獲取整個范圍的行數和總頁數。
使用nextStartPrimaryKey翻頁:每次GetRange請求的Response中會有一個nextStartPrimaryKey,將該nextStartPrimaryKey設置到下一次請求的Request中,即可實現連續翻頁。
使用GetRangeIterator迭代器翻頁:通過
iterator.next()
方法持續獲取下一條數據。
通過表主鍵查詢數據時不支持使用offset進行跳頁查詢。如果有業務需要,請在客戶端通過nextStartPrimaryKey或Iterator迭代器模擬跳頁查詢。
多元索引
如果為數據表創建了多元索引,則您可以通過以下方式進行翻頁:
通過多元索引查詢數據時支持獲取總行數和總頁數,同時需要在Request中設置getTotalCount為true。其中總行數除以limit的值為總頁數。由于設置getTotalCount為true后查詢數據時會增大資源消耗,因此查詢性能會有所下降。
使用offset+limit方式翻頁:支持跳頁查詢,但是offset+limit最大值不能超過100000。如果超過限制,請使用next_token翻頁。
使用next_token翻頁:每次Search請求的Response中會有下一次的next_token,將該next_token設置到下一次請求的Request中,即可實現連續翻頁。
使用SearchIterator迭代器翻頁:通過
iterator.next()
方法持續獲取下一條數據。
表示例
以下示例用于實現一個分頁讀接口,該接口提供了offset過濾以及讀取指定頁數的數據。
/**
* 范圍查詢指定范圍內的數據,返回指定頁數大小的數據,并能根據offset跳過部分行。
*/
private static Pair<List<Row>, PrimaryKey> readByPage(SyncClient client, String tableName,
PrimaryKey startKey, PrimaryKey endKey, int offset, int pageSize) {
Preconditions.checkArgument(offset >= 0, "Offset should not be negative.");
Preconditions.checkArgument(pageSize > 0, "Page size should be greater than 0.");
List<Row> rows = new ArrayList<Row>(pageSize);
int limit = pageSize;
int skip = offset;
PrimaryKey nextStart = startKey;
// 如果查詢的數據量很大,則一次請求有可能不會返回所有的數據,需要流式查詢所有需要的數據。
while (limit > 0 && nextStart != null) {
// 構造GetRange的查詢參數。
// 注意:startPrimaryKey需要設置為上一次讀到的位點,從上一次未讀完的地方繼續往下讀,實現流式的范圍查詢。
RangeRowQueryCriteria criteria = new RangeRowQueryCriteria(tableName);
criteria.setInclusiveStartPrimaryKey(nextStart);
criteria.setExclusiveEndPrimaryKey(endKey);
criteria.setMaxVersions(1);
// 需要設置正確的limit,此處期望讀出的數據行數最多為完整的一頁數據以及需要過濾(offset)的數據。
criteria.setLimit(skip + limit);
GetRangeRequest request = new GetRangeRequest();
request.setRangeRowQueryCriteria(criteria);
GetRangeResponse response = client.getRange(request);
for (Row row : response.getRows()) {
if (skip > 0) {
skip--; // 對于offset之前的數據,需要過濾掉,采用的策略是讀出來后在客戶端進行過濾。
} else {
rows.add(row);
limit--;
}
}
// 設置下一次查詢的起始位點。
nextStart = response.getNextStartPrimaryKey();
}
return new Pair<List<Row>, PrimaryKey>(rows, nextStart);
}
以下示例用于使用分頁讀接口實現順序按頁讀取某個指定范圍內的所有數據。
private static void readByPage(SyncClient client, String tableName) {
int pageSize = 8;
int offset = 33;
PrimaryKeyBuilder primaryKeyBuilder= PrimaryKeyBuilder.createPrimaryKeyBuilder();
primaryKeyBuilder.addPrimaryKeyColumn("gid", PrimaryKeyValue.INF_MIN);
primaryKeyBuilder.addPrimaryKeyColumn("uid", PrimaryKeyValue.INF_MIN);
PrimaryKey startKey = primaryKeyBuilder.build();
primaryKeyBuilder= PrimaryKeyBuilder.createPrimaryKeyBuilder();
primaryKeyBuilder.addPrimaryKeyColumn("gid", PrimaryKeyValue.INF_MAX);
primaryKeyBuilder.addPrimaryKeyColumn("uid", PrimaryKeyValue.INF_MAX);
PrimaryKey endKey = primaryKeyBuilder.build();
// 讀第一頁,從范圍的offset=33的行開始讀起。
Pair<List<Row>, PrimaryKey> result = readByPage(client, tableName, startKey, endKey, offset, pageSize);
for (Row row : result.getFirst()) {
System.out.println(row);
}
System.out.println("Total rows count: " + result.getFirst().size());
// 順序翻頁,讀完范圍內的所有數據。
startKey = result.getSecond();
while (startKey != null) {
System.out.println("============= start read next page ==============");
result = readByPage(client, tableName, startKey, endKey, 0, pageSize);
for (Row row : result.getFirst()) {
System.out.println(row);
}
startKey = result.getSecond();
System.out.println("Total rows count: " + result.getFirst().size());
}
}
多元索引示例
更多信息,請參見排序和翻頁。