Use Tablestore SDK for Python to scan matching data in a search index in parallel and export a complete unordered result set.
Prerequisites
Install the Tablestore SDK for Python and initialize a client.
Description
Parallel scan exports all rows that match a query in a search index. Results are not globally ordered, and sorting and aggregation are not supported. To sort or aggregate results or return search results to end users, use the Search API. A single worker is simpler to configure, whereas multiple workers read multiple splits concurrently and typically provide higher throughput.
The workflow is: Call compute_splits to obtain the maximum concurrency splits_size and session identifier session_id. Configure each worker with the same query, session_id, and max_parallel, and a unique current_parallel_id. Each worker calls parallel_scan and uses next_token to read subsequent pages. Finally, wait for all workers and merge their unordered results.
Workers under the same session_id establish a data snapshot when the first scan starts. Dynamic schema updates, failover, or load balancing can expire a session early and return OTSSessionExpired; network failures can also interrupt a scan. In these cases, discard incomplete results, call compute_splits again, and restart all workers from the beginning. Up to 10 parallel scan jobs can run concurrently on one search index.
The following example computes splits and then scans all data by using one worker.
splits = client.compute_splits("example_table", "example_index")
next_token = None
rows = []
while True:
scan_query = ScanQuery(
MatchAllQuery(),
limit=2000,
next_token=next_token,
current_parallel_id=0,
max_parallel=1,
alive_time=60,
)
response = client.parallel_scan(
"example_table",
"example_index",
scan_query,
splits.session_id,
ColumnsToGet(return_type=ColumnReturnType.ALL_FROM_INDEX),
)
rows.extend(response.rows)
next_token = response.next_token
if not next_token:
break
print(len(rows))
Parameters
Compute splits
compute_splits(table_name, index_name) contains the following parameters.
|
Name |
Type |
Description |
|
table_name (required) |
|
The name of the data table. |
|
index_name (required) |
|
The name of the search index. |
Scan request
parallel_scan contains the following parameters.
|
Name |
Type |
Description |
|
table_name (required) |
|
The name of the data table. |
|
index_name (required) |
|
The name of the search index. |
|
scan_query (required) |
|
The scan condition, pagination, and concurrency configurations. |
|
session_id (required) |
|
The session identifier returned by |
|
columns_to_get (optional) |
|
The return column configuration. If omitted, only primary key columns are returned. |
|
timeout_s (optional) |
|
The request timeout in seconds. |
Scan configuration
scan_query is of the ScanQuery type and contains the following parameters.
|
Name |
Type |
Description |
|
query (required) |
|
The query condition that defines the scan scope. Use |
|
limit (required) |
|
The maximum number of rows per request. We recommend the default value |
|
next_token (required) |
|
The pagination token. Set it to |
|
current_parallel_id (required) |
|
The current worker ID. Valid values: |
|
max_parallel (required) |
|
The job concurrency, which cannot exceed |
|
alive_time (optional) |
|
The maximum validity period between two page requests, in seconds. Valid values: 1 to 600. Default value: |
Return columns
columns_to_get is of the ColumnsToGet type and contains the following parameters.
|
Name |
Type |
Description |
|
column_names (optional) |
|
The names of search index fields to return. Specify this parameter only when |
|
return_type (optional) |
|
The return column mode. Parallel scan supports |
Response
Split information
compute_splits returns split information.
|
Field |
Type |
Description |
|
session_id |
|
The job session identifier. |
|
splits_size |
|
The maximum concurrency supported by the search index. |
Scan result
parallel_scan returns a scan result.
|
Field |
Type |
Description |
|
rows |
|
The rows returned by the request. |
|
next_token |
|
The token for the next page. An empty value indicates that the current worker has finished. |
Tuple-compatible responses
Parallel scan is supported starting from Tablestore SDK for Python 5.2.0, which returns response objects. In version 5.2.1 and later, you can call ComputeSplitsResponse.v1_response() and ParallelScanResponse.v1_response() to obtain tuples. For new code, access response object attributes directly.
session_id, splits_size = splits.v1_response()
rows, next_token = response.v1_response()
Examples
Scan with multiple workers
The following example creates workers based on splits_size. The thread pool does not exceed the number of client CPU cores, and all workers share the session and maximum concurrency.
from concurrent.futures import ThreadPoolExecutor
import os
def scan_split(parallel_id, max_parallel, session_id):
rows = []
next_token = None
while True:
scan_query = ScanQuery(
MatchAllQuery(),
limit=2000,
next_token=next_token,
current_parallel_id=parallel_id,
max_parallel=max_parallel,
alive_time=60,
)
response = client.parallel_scan(
"example_table",
"example_index",
scan_query,
session_id,
ColumnsToGet(return_type=ColumnReturnType.ALL_FROM_INDEX),
)
rows.extend(response.rows)
next_token = response.next_token
if not next_token:
return rows
splits = client.compute_splits("example_table", "example_index")
worker_count = min(splits.splits_size, os.cpu_count() or 1)
with ThreadPoolExecutor(max_workers=worker_count) as executor:
futures = [
executor.submit(
scan_split,
parallel_id,
splits.splits_size,
splits.session_id,
)
for parallel_id in range(splits.splits_size)
]
all_rows = [row for future in futures for row in future.result()]
print(len(all_rows))