All Products
Search
Document Center

Tablestore:Parallel scan

Last Updated:Jul 29, 2026

You can use Tablestore SDK for Java to scan matching rows in a search index concurrently and export the complete result set when result order is not required.

Prerequisites

Install Tablestore SDK for Java and initialize a client.

Parallel scan requires Tablestore SDK for Java 5.6.0 or later. For version information, see Version history.

How it works

Parallel scan scans all rows that match a query in a search index. The scan does not guarantee the global order of results and does not support sorting or aggregation. If you need ordered results, aggregation, or search results for end users, use the Search operation.

A single-worker scan is simpler to configure. A multi-worker scan reads multiple splits concurrently and typically provides higher scan throughput than a single-worker scan.

A parallel scan consists of the following steps:

  1. Call computeSplits to obtain the maximum parallelism splitsSize and the task session ID sessionId of the search index.

  2. Configure ParallelScanRequest. For a single-worker scan, you can omit maxParallel and currentParallelId. For a multi-worker scan, use the same query, sessionId, and maxParallel for all workers, and specify a different currentParallelId for each worker.

  3. Call createParallelScanIterator to automatically read all pages, or call parallelScan and use nextToken to manually retrieve subsequent pages.

  4. Wait for all workers to finish and merge their unordered results.

Within a sessionId, the data snapshot is fixed when parallelScan is called for the first time. Rows that are added or updated while the task is running are not included in the snapshot. You can omit sessionId. However, if server-side load balancing or a similar change occurs during the scan, the results may contain a small number of duplicate rows. We recommend that you call computeSplits first and include the returned sessionId in subsequent requests.

Important

The session may expire early when a dynamic schema change switches the index or when server-side failover or load balancing occurs. In this case, the server returns OTSSessionExpired. A client-side network error can also interrupt the scan. If one of these errors occurs, discard the incomplete results, call computeSplits again, and restart the entire scan task from the beginning. A maximum of 10 parallel scan tasks can run concurrently on a search index. For other limits, see Search index limits.

Call computeSplits to compute splits. Call parallelScan to manually retrieve pages, or call createParallelScanIterator to automatically retrieve all pages.

ComputeSplitsResponse computeSplits(ComputeSplitsRequest request)
ParallelScanResponse parallelScan(ParallelScanRequest request)
RowIterator createParallelScanIterator(ParallelScanRequest request)

The following sample scans all rows by using one worker and returns the category and price fields. RowIterator automatically retrieves subsequent pages.

String tableName = "example_table";
String indexName = "example_index";

ComputeSplitsRequest splitsRequest = ComputeSplitsRequest.newBuilder()
        .tableName(tableName)
        .splitsOptions(new SearchIndexSplitsOptions(indexName))
        .build();
ComputeSplitsResponse splitsResponse =
        client.computeSplits(splitsRequest);

ScanQuery scanQuery = ScanQuery.newBuilder()
        .query(QueryBuilders.matchAll())
        .limit(2000)
        .build();
ParallelScanRequest request = ParallelScanRequest.newBuilder()
        .tableName(tableName)
        .indexName(indexName)
        .scanQuery(scanQuery)
        .addColumnsToGet("category", "price")
        .sessionId(splitsResponse.getSessionId())
        .build();

RowIterator iterator = client.createParallelScanIterator(request);
while (iterator.hasNext()) {
    Row row = iterator.next();
    System.out.println(row);
}

Parameters

Split request

splitsRequest is of the ComputeSplitsRequest type and contains the following parameters.

Name

Type

Description

tableName (required)

String

The name of the data table.

splitsOptions (required)

SplitsOptions

The split configuration. For a search index, set this parameter to SearchIndexSplitsOptions.

Search index split configuration

splitsRequest.splitsOptions is of the SearchIndexSplitsOptions type and contains the following parameter.

Name

Type

Description

indexName (required)

String

The name of the search index.

Scan request

request is of the ParallelScanRequest type and contains the following parameters.

Name

Type

Description

tableName (required)

String

The name of the data table.

indexName (required)

String

The name of the search index.

scanQuery (required)

ScanQuery

The scan condition, number of rows returned per request, and parallelism configuration.

columnsToGet (optional)

SearchRequest.ColumnsToGet

The columns to return. If this parameter is not specified, only primary key columns are returned.

sessionId (optional)

byte[]

The task session ID returned by computeSplits. We recommend that you specify this parameter to use the same data snapshot throughout the scan.

timeoutInMillisecond (optional)

int

The request-level timeout period in milliseconds. The default value is -1, which specifies that no request-level timeout is set.

Scan configuration

request.scanQuery is of the ScanQuery type and contains the following parameters.

Name

Type

Description

query (required)

Query

The query condition that defines the scan scope. Parallel scan supports term, match, range, geo, nested, and other queries. Configure the query in the same way as for the Search operation. To scan all rows in the search index, set the query type to MatchAllQuery.

limit (optional)

Integer

The maximum number of rows returned per request. Default value: 2000. We recommend that you use the default value.

maxParallel (optional)

Integer

The parallelism of the scan task. The value cannot exceed ComputeSplitsResponse.splitsSize. Default value: 1.

currentParallelId (optional)

Integer

The ID of the current worker. This parameter is required if maxParallel is greater than 1. Assign each worker a unique value in the range [0, maxParallel).

aliveTime (optional)

Integer

The maximum interval between two page requests for the scan task, in seconds. Valid values: 1 to 600. Default value: 60. The validity period is refreshed each time rows are successfully retrieved.

token (optional)

byte[]

The pagination token. Omit this parameter from the first request. For manual pagination, set it to nextToken from the previous response. The SDK manages this parameter when you use createParallelScanIterator.

Note

The maximum limit value supported by the server is 10000. We recommend that you do not set limit to this value.

Columns to return

request.columnsToGet is of the SearchRequest.ColumnsToGet type and contains the following parameters.

Name

Type

Description

columns (optional)

List<String>

The names of search index fields to return. A field that exists only in the data table but is not included in the search index cannot be returned. Date, Geo-point, IP, Vector, JSON/Nested, and array fields can be returned.

returnAllFromIndex (optional)

boolean

Specifies whether to return all fields in the search index. Default value: false. If you set this parameter to true, you do not need to specify columns.

returnAll (optional)

boolean

Parallel scan does not support this parameter. Do not set it to true.

Return values

Split information

computeSplits returns ComputeSplitsResponse, which contains the following fields.

Name

Type

Description

sessionId

byte[]

The task session ID that is used to scan rows in the same data snapshot.

splitsSize

Integer

The maximum parallelism supported by the search index.

Scan results

parallelScan returns ParallelScanResponse, which contains the following fields.

Name

Type

Description

rows

List<Row>

The rows returned in the current response.

nextToken

byte[]

The token for the next page. If this value is null, the current worker has retrieved all rows.

bodyBytes

long

The size of the response body in bytes.

createParallelScanIterator returns RowIterator. The iterator automatically uses nextToken to retrieve subsequent pages and returns one Row per iteration. It does not support retrieving the total number of matching rows.

Examples

Scan with multiple workers

The following sample creates multiple scan tasks based on splitsSize. Each task uses a unique currentParallelId, and all tasks share the same sessionId and maxParallel. The thread pool does not exceed the number of CPU cores on the client to avoid excessive client load.

String tableName = "example_table";
String indexName = "example_index";

ComputeSplitsResponse splitsResponse = client.computeSplits(
        ComputeSplitsRequest.newBuilder()
                .tableName(tableName)
                .splitsOptions(new SearchIndexSplitsOptions(indexName))
                .build());
int maxParallel = splitsResponse.getSplitsSize();
int workerCount = Math.min(
        maxParallel, Runtime.getRuntime().availableProcessors());

ExecutorService executor = Executors.newFixedThreadPool(workerCount);
List<Future<Integer>> futures = new ArrayList<Future<Integer>>();
try {
    for (int parallelId = 0; parallelId < maxParallel; parallelId++) {
        final int currentParallelId = parallelId;
        futures.add(executor.submit(new Callable<Integer>() {
            @Override
            public Integer call() {
                ScanQuery scanQuery = ScanQuery.newBuilder()
                        .query(QueryBuilders.matchAll())
                        .limit(2000)
                        .maxParallel(maxParallel)
                        .currentParallelId(currentParallelId)
                        .build();
                ParallelScanRequest request =
                        ParallelScanRequest.newBuilder()
                                .tableName(tableName)
                                .indexName(indexName)
                                .scanQuery(scanQuery)
                                .returnAllColumnsFromIndex(true)
                                .sessionId(splitsResponse.getSessionId())
                                .build();

                int rowCount = 0;
                RowIterator iterator =
                        client.createParallelScanIterator(request);
                while (iterator.hasNext()) {
                    Row row = iterator.next();
                    System.out.println(row);
                    rowCount++;
                }
                return rowCount;
            }
        }));
    }

    long totalRows = 0;
    for (Future<Integer> future : futures) {
        totalRows += future.get();
    }
    System.out.println("Total rows: " + totalRows);
} finally {
    executor.shutdown();
}

Manually retrieve pages

The following sample directly calls parallelScan and passes nextToken from each response to the next request until the current worker has retrieved all rows.

String tableName = "example_table";
String indexName = "example_index";

ComputeSplitsResponse splitsResponse = client.computeSplits(
        ComputeSplitsRequest.newBuilder()
                .tableName(tableName)
                .splitsOptions(new SearchIndexSplitsOptions(indexName))
                .build());
ScanQuery scanQuery = ScanQuery.newBuilder()
        .query(QueryBuilders.matchAll())
        .limit(2000)
        .maxParallel(1)
        .currentParallelId(0)
        .build();
ParallelScanRequest request = ParallelScanRequest.newBuilder()
        .tableName(tableName)
        .indexName(indexName)
        .scanQuery(scanQuery)
        .addColumnsToGet("category", "price")
        .sessionId(splitsResponse.getSessionId())
        .build();

long totalRows = 0;
do {
    ParallelScanResponse response = client.parallelScan(request);
    for (Row row : response.getRows()) {
        System.out.println(row);
        totalRows++;
    }
    scanQuery.setToken(response.getNextToken());
} while (scanQuery.getToken() != null);
System.out.println("Total rows: " + totalRows);