Synchronize data between tables

更新时间:
复制 MD 格式

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

Note

If the source and target tables are in different regions, you must create a VPC peering connection to establish cross-region network connectivity.

Create a VPC peering connection to establish cross-region network connectivity

The following example describes a scenario in which the DataWorks workspace and the source table instance are in the China (Hangzhou) region, and the target table is in the China (Shanghai) region.

  1. Bind a VPC to the Tablestore instance.

    1. Log on to the Tablestore console. In the top navigation bar, select the region where the target table is located.

    2. Click the instance alias to go to the Instance Management page.

    3. On the Network Management tab, click Bind VPC, select a VPC and vSwitch, enter a name for the VPC, and then click OK.

    4. Wait for the VPC to be bound. The page automatically refreshes to display the bound VPC ID and VPC Address in the VPC list.

      Note

      When you add a Tablestore data source in the DataWorks console, you must use this VPC address.

  2. Obtain the VPC information of the resource group for the DataWorks workspace.

    1. Log on to the DataWorks console. In the top navigation bar, select the region where your workspace is located. In the left-side navigation pane, click Workspaces to go to the Workspace list page.

    2. Click the workspace name to go to the Workspace Details page. In the left-side navigation pane, click Resource Group to view the list of resource groups that are bound to the workspace.

    3. In the row of the target resource group, click Network Settings. In the Resource Scheduling & Data Integration section, view the VPC ID of the bound VPC.

  3. Create a VPC peering connection and configure routes.

    1. Log on to the VPC console. In the left-side navigation pane, click VPC Peering Connection and then click Create VPC Peering Connection.

    2. On the Create VPC Peering Connection page, enter a name for the peering connection, select the requester VPC instance, accepter account type, accepter region, and accepter VPC instance, and then click OK.

    3. On the VPC Peering Connection page, find the VPC peering connection that you created and click Configure route in both the Requester VPC and Accepter columns.

      The destination CIDR block must be the CIDR block of the peer VPC. When you configure a route entry for the requester, enter the CIDR block of the accepter. When you configure a route entry for the accepter, enter the CIDR block of the requester.

Step 2: Add a Tablestore data source

Add a Tablestore data source for both the source table instance and the target table instance.

  1. Log on to the DataWorks console. Switch to the target region. In the left-side navigation pane, choose Data integration > Data integration. In the drop-down list, select the desired workspace and click Go to Data Integration.

  2. In the left-side navigation pane, click Data Source.

  3. On the Data Sources page, click Add Data Source.

  4. In the Add Data Source dialog box, search for and select Tablestore as the data source type.

  5. 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

  6. 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.

    1. In the Connection Configuration section, click Test Network Connectivity in the Connection Status column for the resource group.

    2. 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

  1. Go to the Data Development page.

    1. Log on to the DataWorks console.

    2. In the top navigation bar, select the resource group and region.

    3. In the left-side navigation pane, choose .

    4. Select the desired workspace and click Go To Data Studio.

  2. On the Data Development page of the Data Studio console, move the pointer over the image icon next to Workspace Directory and choose Batch Synchronization.

  3. 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_min represents negative infinity.

      When the primary key consists of an int column id and a string column name, 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_max represents infinity.

      When the primary key consists of an int column id and a string column name, 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 int primary key column id and one string primary key column name, 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 int primary key column id and a string primary key column name, and the attribute column contains an int field age, 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 int column id and a string column name, and the attribute column contains an int field age, 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 an int column id and a string column name, and the attribute columns include an int field age. When you configure the settings, replace the datasource and table name 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.

  1. 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
  2. View the data in the target table.

    1. Go to the Tablestore console. In the top navigation bar, select the resource group and region.

    2. Click the instance alias. On the Tables page, click the target table.

    3. 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

Step 2: Export data from the source table

  1. 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****************************************
  2. Export the data.

    1. Run the use command to use the source table. This topic uses source_table as an example.

      use --wc -t source_table
    2. 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

  1. 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****************************************
  2. Import the data.

    1. Execute the use command to use the target table. This example uses target_table.

      use --wc -t target_table
    2. Import data from the local JSON file to the target table. For more information, see Import data.

      import -i /tmp/sourceData.json 

FAQ