表格存储提供了BatchWriteRow、BatchGetRow、GetRange和createRangeIterator等多行数据操作的接口。
如果需要了解表格存储各场景的应用案例,请参见快速玩转Tablestore入门与实战。
前提条件
- 已初始化OTSClient,详情请参见初始化。
- 已创建数据表并写入数据。
批量写(BatchWriteRow)
批量写接口用于在一次请求中进行批量的写入操作,也支持一次对多张表进行写入。BatchWriteRow操作由多个PutRow、UpdateRow、DeleteRow子操作组成,构造子操作的过程与使用PutRow接口、UpdateRow接口和DeleteRow接口时相同,也支持设置更新条件。
BatchWriteRow的各个子操作独立执行,表格存储会分别返回各个子操作的执行结果。
由于批量写入可能存在部分行失败的情况,失败行的Index及错误信息在返回的BatchWriteRowResponse中,但并不抛出异常。因此调用BatchWriteRow接口时,需要检查返回值,可通过BatchWriteRowResponse的isAllSucceed方法判断是否全部成功;如果不检查返回值,则可能会忽略掉部分操作的失败。
当服务端检查到某些操作出现参数错误时,BatchWriteRow接口可能会抛出参数错误的异常,此时该请求中所有的操作都未执行。
参数
详细参数说明请参见单行数据操作。
示例
一次BatchWriteRow请求,包含2个PutRow操作、1个UpdateRow操作和1个DeleteRow操作。
private static void batchWriteRow(SyncClient client) {
BatchWriteRowRequest batchWriteRowRequest = new BatchWriteRowRequest();
//构造rowPutChange1。
PrimaryKeyBuilder pk1Builder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
pk1Builder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString("pk1"));
RowPutChange rowPutChange1 = new RowPutChange(TABLE_NAME, pk1Builder.build());
//添加一些列。
for (int i = 0; i < 10; i++) {
rowPutChange1.addColumn(new Column("Col" + i, ColumnValue.fromLong(i)));
}
//添加到batch操作中。
batchWriteRowRequest.addRowChange(rowPutChange1);
//构造rowPutChange2。
PrimaryKeyBuilder pk2Builder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
pk2Builder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString("pk2"));
RowPutChange rowPutChange2 = new RowPutChange(TABLE_NAME, pk2Builder.build());
//添加一些列。
for (int i = 0; i < 10; i++) {
rowPutChange2.addColumn(new Column("Col" + i, ColumnValue.fromLong(i)));
}
//添加到batch操作中。
batchWriteRowRequest.addRowChange(rowPutChange2);
//构造rowUpdateChange。
PrimaryKeyBuilder pk3Builder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
pk3Builder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString("pk3"));
RowUpdateChange rowUpdateChange = new RowUpdateChange(TABLE_NAME, pk3Builder.build());
//添加一些列。
for (int i = 0; i < 10; i++) {
rowUpdateChange.put(new Column("Col" + i, ColumnValue.fromLong(i)));
}
//删除一列。
rowUpdateChange.deleteColumns("Col10");
//添加到batch操作中。
batchWriteRowRequest.addRowChange(rowUpdateChange);
//构造rowDeleteChange。
PrimaryKeyBuilder pk4Builder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
pk4Builder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString("pk4"));
RowDeleteChange rowDeleteChange = new RowDeleteChange(TABLE_NAME, pk4Builder.build());
//添加到batch操作中。
batchWriteRowRequest.addRowChange(rowDeleteChange);
BatchWriteRowResponse response = client.batchWriteRow(batchWriteRowRequest);
System.out.println("是否全部成功:" + response.isAllSucceed());
if (!response.isAllSucceed()) {
for (BatchWriteRowResponse.RowResult rowResult : response.getFailedRows()) {
System.out.println("失败的行:" + batchWriteRowRequest.getRowChange(rowResult.getTableName(), rowResult.getIndex()).getPrimaryKey());
System.out.println("失败原因:" + rowResult.getError());
}
/**
* 可以通过createRequestForRetry方法再构造一个请求对失败的行进行重试。此处只给出构造重试请求的部分。
* 推荐的重试方法是使用SDK的自定义重试策略功能,支持对batch操作的部分行错误进行重试。设置重试策略后,调用接口处无需增加重试代码。
*/
BatchWriteRowRequest retryRequest = batchWriteRowRequest.createRequestForRetry(response.getFailedRows());
}
}
详细代码请参见BatchWriteRow@GitHub。
批量读(BatchGetRow)
批量读接口用于一次请求读取多行数据,也支持一次对多张表进行读取。BatchGetRow由多个GetRow子操作组成。构造子操作的过程与使用GetRow接口时相同。
批量读取的所有行采用相同的参数条件,例如ColumnsToGet=[colA],则要读取的所有行都只读取colA列。
BatchGetRow的各个子操作独立执行,表格存储会分别返回各个子操作的执行结果。
由于批量读取可能存在部分行失败的情况,失败行的错误信息在返回的BatchGetRowResponse中,但并不抛出异常。因此调用BatchGetRow接口时,需要检查返回值,可通过BatchGetRowResponse的isAllSucceed方法判断是否所有行都获取成功;通过BatchGetRowResponse的getFailedRows方法获取失败行的信息。
参数
详细参数说明请参见单行数据操作。
示例
读取10行,设置版本条件、要读取的列、过滤器等。
private static void batchGetRow(SyncClient client) {
MultiRowQueryCriteria multiRowQueryCriteria = new MultiRowQueryCriteria(TABLE_NAME);
//加入10个要读取的行。
for (int i = 0; i < 10; i++) {
PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString("pk" + i));
PrimaryKey primaryKey = primaryKeyBuilder.build();
multiRowQueryCriteria.addRow(primaryKey);
}
//添加条件。
multiRowQueryCriteria.setMaxVersions(1);
multiRowQueryCriteria.addColumnsToGet("Col0");
multiRowQueryCriteria.addColumnsToGet("Col1");
SingleColumnValueFilter singleColumnValueFilter = new SingleColumnValueFilter("Col0",
SingleColumnValueFilter.CompareOperator.EQUAL, ColumnValue.fromLong(0));
singleColumnValueFilter.setPassIfMissing(false);
multiRowQueryCriteria.setFilter(singleColumnValueFilter);
BatchGetRowRequest batchGetRowRequest = new BatchGetRowRequest();
//BatchGetRow支持读取多个表的数据,一个multiRowQueryCriteria对应一个表的查询条件,可以添加多个multiRowQueryCriteria。
batchGetRowRequest.addMultiRowQueryCriteria(multiRowQueryCriteria);
BatchGetRowResponse batchGetRowResponse = client.batchGetRow(batchGetRowRequest);
System.out.println("是否全部成功:" + batchGetRowResponse.isAllSucceed());
if (!batchGetRowResponse.isAllSucceed()) {
for (BatchGetRowResponse.RowResult rowResult : batchGetRowResponse.getFailedRows()) {
System.out.println("失败的行:" + batchGetRowRequest.getPrimaryKey(rowResult.getTableName(), rowResult.getIndex()));
System.out.println("失败原因:" + rowResult.getError());
}
/**
* 可以通过createRequestForRetry方法再构造一个请求对失败的行进行重试。此处只给出构造重试请求的部分。
* 推荐的重试方法是使用SDK的自定义重试策略功能,支持对batch操作的部分行错误进行重试。设置重试策略后,调用接口处无需增加重试代码。
*/
BatchGetRowRequest retryRequest = batchGetRowRequest.createRequestForRetry(batchGetRowResponse.getFailedRows());
}
}
详细代码请参见BatchGetRow@GitHub。
范围读(GetRange)
范围读取接口用于读取一个范围内的数据。
范围读取接口支持按照确定范围进行正序读取和逆序读取,可以设置要读取的行数。如果范围较大,已扫描的行数或者数据量超过一定限制,会停止扫描,并返回已获取的行和下一个主键信息。您可以根据返回的下一个主键信息,继续发起请求,获取范围内剩余的行。
- 扫描的行数据大小之和达到4 MB。
- 扫描的行数等于5000。
- 返回的行数等于最大返回行数。
- 当前剩余的预留读吞吐量已全部使用,余量不足以读取下一条数据。
参数
参数 | 说明 |
---|---|
tableName | 数据表名称。 |
direction | 读取方向。
例如同一表中有两个主键A和B,A<B。如正序读取[A, B),则按从A至B的顺序返回主键大于等于A、小于B的行;逆序读取[B, A),则按从B至A的顺序返回大于A、小于等于B的数据。 |
inclusiveStartPrimaryKey | 本次范围读取的起始主键和结束主键,起始主键和结束主键需要是有效的主键或者是由INF_MIN和INF_MAX类型组成的虚拟点,虚拟点的列数必须与主键相同。
其中INF_MIN表示无限小,任何类型的值都比它大;INF_MAX表示无限大,任何类型的值都比它小。
数据表中的行按主键从小到大排序,读取范围是一个左闭右开的区间,正序读取时,返回的是大于等于起始主键且小于结束主键的所有的行。 |
exclusiveEndPrimaryKey | |
limit | 数据的最大返回行数,此值必须大于 0。
表格存储按照正序或者逆序返回指定的最大返回行数后即结束该操作的执行,即使该区间内仍有未返回的数据。此时可以通过返回结果中的nextStartPrimaryKey记录本次读取到的位置,用于下一次读取。 |
columnsToGet | 读取的列集合,列名可以是主键列或属性列。
如果不设置返回的列名,则返回整行数据。 说明
|
maxVersions | 最多读取的版本数。
说明 maxVersions与timeRange必须至少设置一个。
|
timeRange | 读取版本号范围或特定版本号的数据。更多信息,请参见TimeRange。
说明 maxVersions与timeRange必须至少设置一个。
timestamp和 时间戳的单位为毫秒,最小值为0,最大值为Long.MAX_VALUE。 |
filter | 使用过滤器,在服务端对读取结果再进行一次过滤,只返回符合过滤器中条件的数据行。更多信息,请参见过滤器。
说明 当columnsToGet和filter同时使用时,执行顺序是先获取columnsToGet指定的列,再在返回的列中进行条件过滤。
|
nextStartPrimaryKey | 根据返回结果中的nextStartPrimaryKey判断数据是否全部读取。
|
示例
- 示例1
按照确定范围进行正序读取,判断nextStartPrimaryKey是否为空,读取完范围内的全部数据。
private static void getRange(SyncClient client, String startPkValue, String endPkValue) { RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria(TABLE_NAME); //设置起始主键。 PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder(); primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString(startPkValue)); rangeRowQueryCriteria.setInclusiveStartPrimaryKey(primaryKeyBuilder.build()); //设置结束主键。 primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder(); primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString(endPkValue)); rangeRowQueryCriteria.setExclusiveEndPrimaryKey(primaryKeyBuilder.build()); rangeRowQueryCriteria.setMaxVersions(1); System.out.println("GetRange的结果为:"); while (true) { GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria)); for (Row row : getRangeResponse.getRows()) { System.out.println(row); } //如果NextStartPrimaryKey不为null,则继续读取。 if (getRangeResponse.getNextStartPrimaryKey() != null) { rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey()); } else { break; } } }
- 示例2
按照第一个主键列确定范围、第二主键列从最小值(INF_MIN)到最大值(INF_MAX)进行正序读取,判断nextStartPrimaryKey是否为null,读取完范围内的全部数据。
private static void getRange(SyncClient client, String startPkValue, String endPkValue) { RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria(TABLE_NAME); //设置起始主键,以两个主键为例。 PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder(); primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME1, PrimaryKeyValue.fromString(startPkValue));//确定值。 primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME2, PrimaryKeyValue.INF_MIN);//最小值。 rangeRowQueryCriteria.setInclusiveStartPrimaryKey(primaryKeyBuilder.build()); //设置结束主键。 primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder(); primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME1, PrimaryKeyValue.fromString(endPkValue));//确定值。 primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME2, PrimaryKeyValue.INF_MAX);//最大值。 rangeRowQueryCriteria.setExclusiveEndPrimaryKey(primaryKeyBuilder.build()); rangeRowQueryCriteria.setMaxVersions(1); System.out.println("GetRange的结果为:"); while (true) { GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria)); for (Row row : getRangeResponse.getRows()) { System.out.println(row); } //如果nextStartPrimaryKey不为null,则继续读取。 if (getRangeResponse.getNextStartPrimaryKey() != null) { rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey()); } else { break; } } }
- 示例3
读取主键范围为["pk:2020-01-01.log", "pk:2021-01-01.log")时Col1列的数据,并对该列的数据执行正则过滤。
private static void getRange(SyncClient client) { RangeRowQueryCriteria criteria = new RangeRowQueryCriteria(TABLE_NAME); // 设置主键范围为["pk:2020-01-01.log", "pk:2021-01-01.log"),读取范围为左闭右开的区间。 PrimaryKey pk0 = PrimaryKeyBuilder.createPrimaryKeyBuilder() .addPrimaryKeyColumn("pk", PrimaryKeyValue.fromString("2020-01-01.log")) .build(); PrimaryKey pk1 = PrimaryKeyBuilder.createPrimaryKeyBuilder() .addPrimaryKeyColumn("pk", PrimaryKeyValue.fromString("2021-01-01.log")) .build(); criteria.setInclusiveStartPrimaryKey(pk0); criteria.setExclusiveEndPrimaryKey(pk1); // 设置读取最新版本。 criteria.setMaxVersions(1); // 设置过滤器,当cast<int>(regex(Col1)) > 100时,返回该行。 RegexRule regexRule = new RegexRule("t1:([0-9]+),", VariantType.Type.VT_INTEGER); SingleColumnValueRegexFilter filter = new SingleColumnValueRegexFilter("Col1", regexRule,SingleColumnValueFilter.CompareOperator.GREATER_THAN,ColumnValue.fromLong(100)); criteria.setFilter(filter); while (true) { GetRangeResponse resp = client.getRange(new GetRangeRequest(criteria)); for (Row row : resp.getRows()) { // do something System.out.println(row); } if (resp.getNextStartPrimaryKey() != null) { criteria.setInclusiveStartPrimaryKey(resp.getNextStartPrimaryKey()) } } }
详细代码请参见GetRange@GitHub。
迭代读(createRangeIterator)
迭代读取数据。
private static void getRangeByIterator(SyncClient client, String startPkValue, String endPkValue) {
RangeIteratorParameter rangeIteratorParameter = new RangeIteratorParameter(TABLE_NAME);
//设置起始主键。
PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString(startPkValue));
rangeIteratorParameter.setInclusiveStartPrimaryKey(primaryKeyBuilder.build());
//设置结束主键。
primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString(endPkValue));
rangeIteratorParameter.setExclusiveEndPrimaryKey(primaryKeyBuilder.build());
rangeIteratorParameter.setMaxVersions(1);
Iterator<Row> iterator = client.createRangeIterator(rangeIteratorParameter);
System.out.println("使用Iterator进行GetRange的结果为:");
while (iterator.hasNext()) {
Row row = iterator.next();
System.out.println(row);
}
}
详细代码请参见GetRangeByIterator@GitHub。