局部事务

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

目前局部事务功能处于邀测中,默认关闭。如果需要使用该功能,请提交工单进行申请或者加入钉钉群36165029092(表格存储技术交流群-3)进行咨询

使用局部事务可以指定某个分区键值内的操作是原子的,对分区键值内的数据进行的操作要么全部成功要么全部失败,并且所提供的隔离级别为读已提交。

前提条件

  • 已初始化Client,详情请参见初始化OTSClient

  • 已创建数据表并写入数据。

使用方法

  1. 使用start_local_transaction在指定的分区键值创建一个局部事务,并获取局部事务ID。

  2. 对局部事务范围内的数据进行读写操作。

    支持对局部事务进行操作的接口为GetRow、PutRow、DeleteRow、UpdateRow、BatchWriteRowGetRange。

  3. 使用commit_transaction提交局部事务或者使用abort_transaction丢弃局部事务。

注意事项

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

参数

参数

说明

table_name

数据表名称。

key

数据表分区键。

创建局部事务时,只需要指定局部事务对应的分区键值。

primary_key

数据表主键。

创建局部事务后,对局部事务范围内的数据进行读写操作时,需要指定完整主键。

transaction_id

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

创建局部事务后,操作局部事务时均需要带上局部事务ID。

示例

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

以下示例用于为表的指定分区键创建一个局部事务后,在局部事务内写入一行数据。如果数据写入成功,则提交事务;如果数据写入失败,则丢弃事务。

def  transaction_put_row(client):
    # 设置数据表名称。
    table_name = '<TABLE_NAME>'
    # 在PK0下创建局部事务。
    key = [('PK0', 1)]
    # start_local_transaction方法的返回值即为transaction_id。
    transaction_id = client.start_local_transaction(table_name, key)

    # 写入数据。
    primary_key = [('PK0', 1), ('PK1', 'transaction')]
    attribute_columns = [('value', 'origion value')]
    row = Row(primary_key, attribute_columns)
    condition = Condition(RowExistenceExpectation.IGNORE)
    try:
        # 调用put_row方法,如果没有指定ReturnType,则return_row为None。
        consumed, return_row = client.put_row(table_name, row, condition, None, transaction_id)
        # 打印此次请求消耗的写CU。
        print('put row succeed, consume %s write cu.' % consumed.write)
        # 如果数据写入成功,则提交事务,使局部事务中的所有数据修改生效。您也可以通过丢弃事务来使数据写入不生效。
        client.commit_transaction(transaction_id)
    # 客户端异常,一般为参数错误或者网络异常。
    except OTSClientError as e:
        print("put row failed, http_status:%d, error_message:%s" % (e.get_http_status(), e.get_error_message()))
        # 如果数据写入失败,则丢弃事务,局部事务中的所有数据修改均不会应用到原有数据。
        client.abort_transaction(transaction_id)
    # 服务端异常,一般为参数错误或者流控错误。
    except OTSServiceError as e:
        print("put row failed, http_status:%d, error_code:%s, error_message:%s, request_id:%s" % (e.get_http_status(), e.get_error_code(), e.get_error_message(), e.get_request_id())) 
        # 如果数据写入失败,则丢弃事务,局部事务中的所有数据修改均不会应用到原有数据。
        client.abort_transaction(transaction_id)

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

以下示例用于为表的指定分区键创建一个局部事务后,在局部事务内读取一行数据。

def transaction_get_row(client):
    # 设置数据表名称。
    table_name ='<TABLE_NAME>'
    # 在PK0下创建局部事务。
    key = [('PK0', 1)]
    # start_local_transaction方法的返回值即为transaction_id。
    transaction_id = client.start_local_transaction(table_name, key)
    
    # 读取数据。
    primary_key = [('PK0', 1), ('PK1', 'transaction')]
    columns_to_get = ['value']
    consumed, return_row, next_token = client.get_row(
        table_name, primary_key, columns_to_get, None, 1, None, None, None, None, transaction_id
    )
    for att in return_row.attribute_columns:
        print ('\tname:%s\tvalue:%s' % (att[0], att[1]))

    # 提交或丢弃局部事务。对于读操作来说,提交局部事务或丢弃局部事务的操作是等同的。
    # 提交局部事务,使局部事务中的所有数据修改生效。
    client.commit_transaction(transaction_id)
    # 丢弃局部事务,局部事务中的所有数据修改均不会应用到原有数据。
    #client.abort_transaction(transaction_id)

相关文档

如果要在局部事务内进行批量写入、范围读取等操作,请在创建局部事务后,使用写入数据或者读取数据文档中的相应操作示例以及在请求中带上局部事务ID实现。