局部事务

更新时间:
复制为 MD 格式

为数据表开启局部事务后,使用局部事务功能,您可以创建数据范围在一个分区键值内的局部事务并对局部事务中的数据进行读写操作。通过使用局部事务您可以实现单行或多行读写的原子操作。

为数据表开启局部事务功能后,你可以在指定分区键值的范围内创建一个局部事务,并在该事务内执行读写操作。事务内的所有操作要么全部成功,要么全部失败。隔离级别为读已提交(Read Committed)。

前提条件

工作原理

局部事务的作用范围限定在单个分区键值内。事务内的所有读写请求共享同一个事务 ID,表格存储通过该ID保证原子性和隔离性。

  1. 调用 StartLocalTransaction,传入分区键值,创建局部事务并获取事务 ID。

  2. 在事务内执行读写操作,每个请求都需要携带事务 ID。

    支持的操作:GetRow、PutRow、DeleteRow、UpdateRow、BatchWriteRow 和 GetRange。

  3. 调用 CommitTransaction 提交事务使修改生效,或调用 AbortTransaction 丢弃事务放弃所有修改。

注意事项

  • 主键自增列功能和局部事务功能不能同时使用。

  • 局部事务通过悲观锁(Pessimistic Lock)实现并发控制。

  • 每个局部事务从创建开始生命周期最长为60秒。

    如果超过60秒未提交局部事务或丢弃局部事务,则表格存储服务端会认为此局部事务超时,并将局部事务丢弃。

  • 如果创建局部事务时超时,则请求可能在表格存储服务端已执行成功,此时请等待该局部事务超时后重新创建。

  • 未提交的局部事务可能会失效,如果出现此情况,则需要重试该局部事务内的操作。

  • 如果未对局部事务范围内的数据进行写操作,则提交局部事务或丢弃局部事务的操作是等同的。

  • 在局部事务中读写数据有如下限制:

    • 不能使用局部事务ID访问局部事务范围(即创建时使用的分区键值)以外的数据。

    • 同一个局部事务中所有写请求的分区键值必须与创建局部事务时的分区键值相同,读请求则无此限制。

    • 一个局部事务同时只能用于一个请求中,在使用局部事务期间,其他使用此局部事务ID的操作均会失败。

    • 每个局部事务中两次读写操作的最大间隔为60秒。

      如果超过60秒未操作局部事务,则表格存储服务端会认为此局部事务超时,并将局部事务丢弃。

    • 每个局部事务中写入的数据量最大为4 MB,按正常的写请求数据量计算规则累加。

    • 如果在局部事务中写入了未指定版本号的Cell,则该Cell的版本号会在写入数据时(而非提交局部事务时)由表格存储服务端自动生成,生成规则与正常写入一个未指定版本号的Cell相同。

    • 如果BatchWriteRow请求中带有局部事务ID,则此请求中所有行只能操作该局部事务ID对应的表。

    • 在使用局部事务期间,对应分区键值的数据会被加上写锁,只有持有局部事务ID在局部事务范围内的写请求才会成功。其他非事务请求或持有其他局部事务ID在局部事务范围内的写请求均会失败。在局部事务提交、丢弃或超时后,对应的锁也会被释放。

    • 带有局部事务ID的读写请求失败不会影响局部事务本身的存活情况,您可以指定重试规则进行重试或者主动丢弃当前局部事务。

参数

参数

说明

TableName

数据表名称。

PrimaryKey

数据表主键。

  • 创建局部事务时,只需指定分区键值。

  • 在事务内执行读写操作时,需要指定完整主键(所有主键列)。

TransactionId

局部事务 ID,用于唯一标识一个局部事务。

事务内的每个读写请求都需要携带该 ID。

示例

使用局部事务写入一行数据

以下示例为指定分区键创建一个局部事务,在事务内写入一行数据,然后提交事务。

private static void transactionPutRow(SyncClient client) {
    String tableName = "<TABLE_NAME>";

    // 1. 为指定分区键值创建局部事务,获取事务 ID。
    PrimaryKeyBuilder pkBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    pkBuilder.addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("pkvalue"));
    PrimaryKey partitionKey = pkBuilder.build();

    StartLocalTransactionRequest startRequest =
        new StartLocalTransactionRequest(tableName, partitionKey);
    String txnId = client.startLocalTransaction(startRequest).getTransactionID();

    // 2. 在事务内写入一行数据,需指定完整主键并携带事务 ID。
    PrimaryKeyBuilder rowKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    rowKeyBuilder.addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("pkvalue"));
    rowKeyBuilder.addPrimaryKeyColumn("pk2", PrimaryKeyValue.fromLong(10001));
    PrimaryKey rowKey = rowKeyBuilder.build();

    RowPutChange rowPutChange = new RowPutChange(tableName, rowKey);
    rowPutChange.addColumn(new Column("col1", ColumnValue.fromString("colvalue")));
    rowPutChange.addColumn(new Column("col2", ColumnValue.fromLong(10)));

    PutRowRequest putRequest = new PutRowRequest(rowPutChange);
    putRequest.setTransactionId(txnId);
    client.putRow(putRequest);

    // 3. 提交事务,使所有写入生效。如需放弃修改,调用 abortTransaction()。
    CommitTransactionRequest commitRequest = new CommitTransactionRequest(txnId);
    client.commitTransaction(commitRequest);
    // AbortTransactionRequest abortRequest = new AbortTransactionRequest(txnId);
    // client.abortTransaction(abortRequest);
}

使用局部事务读取一行数据

以下示例为指定分区键创建一个局部事务,在事务内读取一行数据。对于只读事务,提交和丢弃的效果相同,均会释放事务。

private static void transactionGetRow(SyncClient client) {
    String tableName = "exampletabled";

    // 1. 为指定分区键值创建局部事务,获取事务 ID。
    PrimaryKeyBuilder pkBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    pkBuilder.addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("111"));
    PrimaryKey partitionKey = pkBuilder.build();

    StartLocalTransactionRequest startRequest =
        new StartLocalTransactionRequest(tableName, partitionKey);
    String txnId = client.startLocalTransaction(startRequest).getTransactionID();

    // 2. 在事务内读取一行数据,需指定完整主键并携带事务 ID。
    PrimaryKeyBuilder rowKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
    rowKeyBuilder.addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("111"));
    rowKeyBuilder.addPrimaryKeyColumn("pk2", PrimaryKeyValue.fromLong(10001));
    PrimaryKey rowKey = rowKeyBuilder.build();

    SingleRowQueryCriteria criteria = new SingleRowQueryCriteria(tableName, rowKey);
    criteria.setMaxVersions(1);  // 读取最新版本。

    GetRowRequest getRequest = new GetRowRequest(criteria);
    getRequest.setTransactionId(txnId);
    GetRowResponse getRowResponse = client.getRow(getRequest);

    // 3. 提交或丢弃事务。对于只读事务,两者效果相同,均会释放事务。
    CommitTransactionRequest commitRequest = new CommitTransactionRequest(txnId);
    client.commitTransaction(commitRequest);
    // AbortTransactionRequest abortRequest = new AbortTransactionRequest(txnId);
    // client.abortTransaction(abortRequest);

    // 打印读取结果。
    Row row = getRowResponse.getRow();
    System.out.println("读取完毕,结果为:");
    System.out.println(row);
}

相关文档

如需在局部事务内执行批量写入或范围读取操作,创建局部事务后,参考写入数据读取数据中的相应示例,并在每个请求中携带事务ID即可。