Tablestore provides multiple methods to migrate or synchronize data between tables. You can use Tunnel Service, DataWorks, DataX, or the command line interface to synchronize data from one table to another.
Prerequisites
-
Obtain the instance names, endpoints, and region IDs for the source and target tables.
-
Create an AccessKey for your Alibaba Cloud account or a RAM user with Tablestore permissions.
Synchronize data using an SDK
You can use Tunnel Service to synchronize data between tables. This method supports data synchronization within the same region, across different regions, and across different accounts. Tunnel Service captures and synchronizes data changes to the target table in real time. The following example shows how to use the Java SDK to implement this synchronization.
Before you run the code, replace the placeholders for the source and target table names, instance names, and endpoints with your actual values. You must also set the AccessKey ID and AccessKey Secret as environment variables.
import com.alicloud.openservices.tablestore.*;
import com.alicloud.openservices.tablestore.core.auth.DefaultCredentials;
import com.alicloud.openservices.tablestore.core.auth.ServiceCredentials;
import com.alicloud.openservices.tablestore.model.*;
import com.alicloud.openservices.tablestore.model.tunnel.*;
import com.alicloud.openservices.tablestore.tunnel.worker.IChannelProcessor;
import com.alicloud.openservices.tablestore.tunnel.worker.ProcessRecordsInput;
import com.alicloud.openservices.tablestore.tunnel.worker.TunnelWorker;
import com.alicloud.openservices.tablestore.tunnel.worker.TunnelWorkerConfig;
import com.alicloud.openservices.tablestore.writer.RowWriteResult;
import com.alicloud.openservices.tablestore.writer.WriterConfig;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
public class TableSynchronization {
// Source table configurations: table name, instance name, endpoint, AccessKey ID, AccessKey Secret
final static String sourceTableName = "sourceTableName";
final static String sourceInstanceName = "sourceInstanceName";
final static String sourceEndpoint = "sourceEndpoint";
final static String sourceAccessKeyId = System.getenv("SOURCE_TABLESTORE_ACCESS_KEY_ID");
final static String sourceKeySecret = System.getenv("SOURCE_TABLESTORE_ACCESS_KEY_SECRET");
// Target table configurations: table name, instance name, endpoint, AccessKey ID, AccessKey Secret
final static String targetTableName = "targetTableName";
final static String targetInstanceName = "targetInstanceName";
final static String targetEndpoint = "targetEndpoint";
final static String targetAccessKeyId = System.getenv("TARGET_TABLESTORE_ACCESS_KEY_ID");
final static String targetKeySecret = System.getenv("TARGET_TABLESTORE_ACCESS_KEY_SECRET");
// Tunnel name
static String tunnelName = "source_table_tunnel";
// TablestoreWriter: a tool for high-concurrency data writes.
static TableStoreWriter tableStoreWriter;
// Statistics for successful and failed rows
static AtomicLong succeedRows = new AtomicLong();
static AtomicLong failedRows = new AtomicLong();
public static void main(String[] args) {
// Create the target table.
createTargetTable();
System.out.println("Create target table Done.");
// Initialize TunnelClient.
TunnelClient tunnelClient = new TunnelClient(sourceEndpoint, sourceAccessKeyId, sourceKeySecret, sourceInstanceName);
// Create a data tunnel.
String tunnelId = createTunnel(tunnelClient);
System.out.println("Create tunnel Done.");
// Initialize TablestoreWriter.
tableStoreWriter = createTablesStoreWriter();
// Synchronize data through the data tunnel.
TunnelWorkerConfig config = new TunnelWorkerConfig(new SimpleProcessor());
TunnelWorker worker = new TunnelWorker(tunnelId, tunnelClient, config);
try {
System.out.println("Connect tunnel and working ...");
worker.connectAndWorking();
// Monitor the tunnel status. The data synchronization is complete when the tunnel status changes from full data synchronization to incremental data synchronization.
while (true) {
if (tunnelClient.describeTunnel(new DescribeTunnelRequest(sourceTableName, tunnelName)).getTunnelInfo().getStage().equals(TunnelStage.ProcessStream)) {
break;
}
Thread.sleep(5000);
}
// Synchronization result
System.out.println("Data Synchronization Completed.");
System.out.println("* Succeed Rows Count: " + succeedRows.get());
System.out.println("* Failed Rows Count: " + failedRows.get());
// Delete the tunnel.
tunnelClient.deleteTunnel(new DeleteTunnelRequest(sourceTableName, tunnelName));
// Shut down resources.
worker.shutdown();
config.shutdown();
tunnelClient.shutdown();
tableStoreWriter.close();
}catch(Exception e){
e.printStackTrace();
worker.shutdown();
config.shutdown();
tunnelClient.shutdown();
tableStoreWriter.close();
}
}
private static void createTargetTable() throws ClientException {
// Query the source table information.
SyncClient sourceClient = new SyncClient(sourceEndpoint, sourceAccessKeyId, sourceKeySecret, sourceInstanceName);
DescribeTableResponse response = sourceClient.describeTable(new DescribeTableRequest(sourceTableName));
// Create the target table.
SyncClient targetClient = new SyncClient(targetEndpoint, targetAccessKeyId, targetKeySecret, targetInstanceName);
TableMeta tableMeta = new TableMeta(targetTableName);
response.getTableMeta().getPrimaryKeyList().forEach(
item -> tableMeta.addPrimaryKeyColumn(new PrimaryKeySchema(item.getName(), item.getType()))
);
TableOptions tableOptions = new TableOptions(-1, 1);
CreateTableRequest request = new CreateTableRequest(tableMeta, tableOptions);
targetClient.createTable(request);
// Shut down resources.
sourceClient.shutdown();
targetClient.shutdown();
}
private static String createTunnel(TunnelClient client) {
// Create a data tunnel and return the tunnel ID.
CreateTunnelRequest request = new CreateTunnelRequest(sourceTableName, tunnelName, TunnelType.BaseAndStream);
CreateTunnelResponse response = client.createTunnel(request);
return response.getTunnelId();
}
private static class SimpleProcessor implements IChannelProcessor {
@Override
public void process(ProcessRecordsInput input) {
if(input.getRecords().isEmpty())
return;
System.out.print("* Begin consume " + input.getRecords().size() + " records ... ");
for (StreamRecord record : input.getRecords()) {
switch (record.getRecordType()) {
// Write row data.
case PUT:
RowPutChange putChange = new RowPutChange(targetTableName, record.getPrimaryKey());
putChange.addColumns(getColumnsFromRecord(record));
tableStoreWriter.addRowChange(putChange);
break;
// Update row data.
case UPDATE:
RowUpdateChange updateChange = new RowUpdateChange(targetTableName, record.getPrimaryKey());
for (RecordColumn column : record.getColumns()) {
switch (column.getColumnType()) {
// Add an attribute column.
case PUT:
updateChange.put(column.getColumn().getName(), column.getColumn().getValue(), System.currentTimeMillis());
break;
// Delete a version of an attribute column.
case DELETE_ONE_VERSION:
updateChange.deleteColumn(column.getColumn().getName(),
column.getColumn().getTimestamp());
break;
// Delete an attribute column.
case DELETE_ALL_VERSION:
updateChange.deleteColumns(column.getColumn().getName());
break;
default:
break;
}
}
tableStoreWriter.addRowChange(updateChange);
break;
// Delete row data.
case DELETE:
RowDeleteChange deleteChange = new RowDeleteChange(targetTableName, record.getPrimaryKey());
tableStoreWriter.addRowChange(deleteChange);
break;
}
}
// Flush data from the buffer.
tableStoreWriter.flush();
System.out.println("Done");
}
@Override
public void shutdown() {
}
}
public static List<Column> getColumnsFromRecord(StreamRecord record) {
List<Column> retColumns = new ArrayList<>();
for (RecordColumn recordColumn : record.getColumns()) {
// Replace the data version with the current timestamp to prevent the maximum version deviation from being exceeded.
Column column = new Column(recordColumn.getColumn().getName(), recordColumn.getColumn().getValue(), System.currentTimeMillis());
retColumns.add(column);
}
return retColumns;
}
private static TableStoreWriter createTablesStoreWriter() {
WriterConfig config = new WriterConfig();
// A row-level callback to count the number of successful and failed rows and print information about the rows that failed to be synchronized.
TableStoreCallback<RowChange, RowWriteResult> resultCallback = new TableStoreCallback<RowChange, RowWriteResult>() {
@Override
public void onCompleted(RowChange rowChange, RowWriteResult rowWriteResult) {
succeedRows.incrementAndGet();
}
@Override
public void onFailed(RowChange rowChange, Exception exception) {
failedRows.incrementAndGet();
System.out.println("* Failed Rows: " + rowChange.getTableName() + " | " + rowChange.getPrimaryKey() + " | " + exception.getMessage());
}
};
ServiceCredentials credentials = new DefaultCredentials(targetAccessKeyId, targetKeySecret);
return new DefaultTableStoreWriter(targetEndpoint, credentials, targetInstanceName,
targetTableName, config, resultCallback);
}
}
Synchronize data using DataWorks
DataWorks provides a visual Data Integration service that allows you to configure synchronization tasks between Tablestore tables through a graphical interface. You can also use other tools, such as DataX, to synchronize data between Tablestore tables.
Step 1: Preparations
-
Create the target table. Make sure that its primary key structure, including the data types and order of the primary key columns, is the same as that of the source table.
-
Activate DataWorks and create a workspace in the region where the source or target table is located.
-
Create a serverless resource group and bind it to the workspace. For information about billing, see Serverless resource group billing.
If the source and target tables are in different regions, you must create a VPC peering connection to establish cross-region network connectivity.
Step 2: Add a Tablestore data source
Add a Tablestore data source for both the source table instance and the target table instance.
-
Log on to the DataWorks console. Switch to the target region. In the left-side navigation pane, choose . In the drop-down list, select the desired workspace and click Go to Data Integration.
-
In the left-side navigation pane, click Data Source.
-
On the Data Sources page, click Add Data Source.
-
In the Add Data Source dialog box, search for and select Tablestore as the data source type.
-
In the Add OTS Data Source dialog box, configure the data source parameters as described in the following table.
Parameter
Description
Data source name
The name of the data source. The name can contain letters, digits, and underscores (_), but cannot start with a digit or an underscore (_).
Data source description
A brief description of the data source. The description can be up to 80 characters in length.
Region
Select the region where the Tablestore instance is located.
Tablestore Instance Name
The name of the Tablestore instance.
Endpoint
The endpoint of the Tablestore instance. We recommend that you use the VPC Address.
AccessKey ID
The AccessKey ID and AccessKey Secret of your Alibaba Cloud account or RAM user.
AccessKey Secret
-
Test the resource group connectivity.
You must test the resource group's connectivity to the data source. The synchronization task cannot run if the resource group cannot connect to the data source.
-
In the Connection Configuration section, click Test Network Connectivity in the Connection Status column for the resource group.
-
After the connectivity test passes, the Connection Status changes to Connected. Click Complete. You can view the new data source in the data source list.
If the connectivity test result is Failed, you can use the Network Connectivity Diagnostic Tool to resolve the issue yourself.
-
Step 3: Configure and run the sync task
Create a task node
-
Go to the Data Development page.
-
Log on to the DataWorks console.
-
In the top navigation bar, select the resource group and region.
-
In the left-side navigation pane, choose .
-
Select the desired workspace and click Go To Data Studio.
-
-
On the Data Development page of the Data Studio console, move the pointer over the
icon next to Workspace Directory and choose . -
In the Create Node dialog box, select a Path. For both the data source and destination, select Tablestore. Enter a name and click OK.
Configure the synchronization task
Under Workspace Directory, click to open the created Batch Synchronization node. You can configure the synchronization task in the Codeless UI or Code editor.
Codeless UI (default)
Configure the following items:
-
Data source: Select the source and destination data sources.
-
Runtime Resource: Select a resource group. After you select a resource group, the connectivity of the data source is automatically tested.
-
Data Source:
-
Table: From the drop-down list, select the source table.
-
Primary Key Range Start: The starting primary key for data reads, specified as a JSON array.
inf_minrepresents negative infinity.When the primary key consists of an
intcolumnidand astringcolumnname, a sample configuration is as follows:Specific primary key range
Full data
[ { "type": "int", "value": "000" }, { "type": "string", "value": "aaa" } ][ { "type": "inf_min" }, { "type": "inf_min" } ] -
Primary key range (end): The end primary key for the data read, specified as a JSON array where
inf_maxrepresents infinity.When the primary key consists of an
intcolumnidand astringcolumnname, a sample configuration is as follows:Specific primary key range
Full data
[ { "type": "int", "value": "999" }, { "type": "string", "value": "zzz" } ][ { "type": "inf_max" }, { "type": "inf_max" } ] -
Splitting Configuration: The custom split configuration, specified as a JSON array. We recommend that you do not configure this parameter under normal circumstances (set it to
[]).If hot partitions exist in Tablestore and the automatic splitting policy of Tablestore Reader does not take effect, we recommend that you use custom splitting rules. A splitting rule specifies the split points within the range of the start and end primary keys. You need to configure only the split key, not all primary keys.
-
-
Destination:
-
Table: From the drop-down list, select the destination table.
-
Primary Key Information: The primary key information of the destination table. The value must be in a JSON array format.
When the primary key contains one
intprimary key columnidand onestringprimary key columnname, the sample configuration is as follows:[ { "name": "id", "type": "int" }, { "name": "name", "type": "string" } ] -
Write Mode: The mode in which data is written to Tablestore. The following modes are supported:
-
PutRow: Writes row data. If the target row does not exist, a new row is added. If the target row exists, the existing row is overwritten.
-
UpdateRow: Updates row data. If the row does not exist, a new row is added. If the row exists, the values of specified columns in this row are added, modified, or deleted based on the request.
-
-
-
Destination Field Mapping: Configure the field mapping from the source table to the destination table. Each line represents a field, specified in JSON format.
-
Source Field: The value must include the primary key information of the source table.
When the primary key contains an
intprimary key columnidand astringprimary key columnname, and the attribute column contains anintfieldage, the sample configuration is as follows:{"name":"id","type":"int"} {"name":"name","type":"string"} {"name":"age","type":"int"} -
Target Field: The value does not need to include the primary key information of the target table.
If the primary key consists of an
intcolumnidand astringcolumnname, and the attribute column contains anintfieldage, the example configuration is as follows:{"name":"age","type":"int"}
-
After completing the configuration, click Save at the top of the page.
Code editor
Click Code Editor at the top of the page. On the page that appears, edit the script.
The following example shows the configuration for a table where the primary key consists of anintcolumnidand astringcolumnname, and the attribute columns include anintfieldage. When you configure the settings, replace thedatasourceandtablename in the sample script.
Full data
{
"type": "job",
"version": "2.0",
"steps": [
{
"stepType": "ots",
"parameter": {
"datasource": "source_data",
"column": [
{
"name": "id",
"type": "int"
},
{
"name": "name",
"type": "string"
},
{
"name": "age",
"type": "int"
}
],
"range": {
"begin": [
{
"type": "inf_min"
},
{
"type": "inf_min"
}
],
"end": [
{
"type": "inf_max"
},
{
"type": "inf_max"
}
],
"split": []
},
"table": "source_table",
"newVersion": "true"
},
"name": "Reader",
"category": "reader"
},
{
"stepType": "ots",
"parameter": {
"datasource": "target_data",
"column": [
{
"name": "age",
"type": "int"
}
],
"writeMode": "UpdateRow",
"table": "target_table",
"newVersion": "true",
"primaryKey": [
{
"name": "id",
"type": "int"
},
{
"name": "name",
"type": "string"
}
]
},
"name": "Writer",
"category": "writer"
}
],
"setting": {
"errorLimit": {
"record": "0"
},
"speed": {
"concurrent": 2,
"throttle": false
}
},
"order": {
"hops": [
{
"from": "Reader",
"to": "Writer"
}
]
}
}
Specific primary key range
{
"type": "job",
"version": "2.0",
"steps": [
{
"stepType": "ots",
"parameter": {
"datasource": "source_data",
"column": [
{
"name": "id",
"type": "int"
},
{
"name": "name",
"type": "string"
},
{
"name": "age",
"type": "int"
}
],
"range": {
"begin": [
{
"type": "int",
"value": "000"
},
{
"type": "string",
"value": "aaa"
}
],
"end": [
{
"type": "int",
"value": "999"
},
{
"type": "string",
"value": "zzz"
}
],
"split": []
},
"table": "source_table",
"newVersion": "true"
},
"name": "Reader",
"category": "reader"
},
{
"stepType": "ots",
"parameter": {
"datasource": "target_data",
"column": [
{
"name": "age",
"type": "int"
}
],
"writeMode": "UpdateRow",
"table": "target_table",
"newVersion": "true",
"primaryKey": [
{
"name": "id",
"type": "int"
},
{
"name": "name",
"type": "string"
}
]
},
"name": "Writer",
"category": "writer"
}
],
"setting": {
"errorLimit": {
"record": "0"
},
"speed": {
"concurrent": 2,
"throttle": false
}
},
"order": {
"hops": [
{
"from": "Reader",
"to": "Writer"
}
]
}
}
After editing the script, click Save at the top of the page.
Run the synchronization task
Click Run at the top of the page to start the synchronization task. When you run the task for the first time, you must confirm the Debug Configuration.
Step 4: View the synchronization result
After the task runs, view its execution status in the logs and the synchronized data in the Tablestore console.
-
View the task execution status and result at the bottom of the page. The following log information indicates that the synchronization task is successful.
2025-11-18 11:16:23 INFO Shell run successfully! 2025-11-18 11:16:23 INFO Current task status: FINISH 2025-11-18 11:16:23 INFO Cost time is: 77.208s -
View the data in the target table.
-
Go to the Tablestore console. In the top navigation bar, select the resource group and region.
-
Click the instance alias. On the Tables page, click the target table.
-
Click Query Data to view the data in the target table.
-
Synchronize data using the CLI
Using the command line interface requires you to manually export data from the source table to a local JSON file and then import that file into the target table. This method is suitable only for small-scale data migration.
Step 1: Preparations
-
Create the target table. Make sure that its primary key structure, including the names, data types, and order of the primary key columns, is the same as that of the source table.
Step 2: Export data from the source table
-
Start the command line interface and run the config command to configure the access information for the instance where the source table is located. For more information, see Start the tool and configure access information.
Before you run the command, replace endpoint, instance, id, and key with the endpoint, instance name, AccessKey ID, and AccessKey Secret of the instance where the source table is located.
config --endpoint https://myinstance.cn-hangzhou.ots.aliyuncs.com --instance myinstance --id NTSVL******************** --key 7NR2**************************************** -
Export the data.
-
Run the
usecommand to use the source table. This topic usessource_tableas an example.use --wc -t source_table -
Export data from the source table to a local JSON file. For more information, see Export data.
scan -o /tmp/sourceData.json
-
Step 3: Import data to the target table
-
Run the config command to configure the access information for the instance where the target table is located.
Before you run the command, replace endpoint, instance, id, and key with the endpoint, instance name, AccessKey ID, and AccessKey Secret of the instance where the target table is located.
config --endpoint https://myinstance.cn-hangzhou.ots.aliyuncs.com --instance myinstance --id NTSVL******************** --key 7NR2**************************************** -
Import the data.
-
Execute the
usecommand to use the target table. This example usestarget_table.use --wc -t target_table -
Import data from the local JSON file to the target table. For more information, see Import data.
import -i /tmp/sourceData.json
-