TcaplusDB实现乐观锁

TcaplusDB为了保证多个客户端对同一条数据访问的一致性,在内部为每条维护了一个版本号(version),每次数据变更时,版本号会自增,读取数据时版本号无变化。

用户在提交更新请求时,可以指定期望的数据版本号,只有数据的当前版本号与用户期望的版本号一致时,才会执行更新操作,否则会更新失败,并将错误信息(错误码:-7949(SVR_ERR_FAIL_INVALID_VERSION))返回给用户。用户如果收到版本号过期的错误提示时,应当重新获取数据最新的版本号,并将获取到的版本号作为新的期望数据版本号,重新发起更新请求,直到成功,或者达到其它限制条件(如重试次数达到上限、重试时间达到上限等)为止。

注:数据版本号突破了int32上限后,会溢出反转,乐观锁失效。溢出后变为0,0表示不启用乐观锁,再次更新变为1,后续是正常的乐观锁机制。

1. 使用场景

  1. 存在多个客户端对同一条数据进行修改,且要求保证数据访问的一致性,即在客户端读取数据后,到执行修改的期间,要确保不会有其它客户端修改数据。

  2. 适用于写冲突概率较低的场景。如果写冲突概率较高,可能会因为不断地进行重试,反而导致整体性能下降,此时业务可能需要考虑采用其它更优的解决方法(如悲观锁)。

2. 使用方法

以TDR表 C++ SDK为例,实现乐观锁所涉及的两个主要接口函数分别是:GetVersion和SetVersion,函数说明如下:

GetVersion

/**
    @brief  获取记录版本号
    @retval 记录版本号
    @note 对于Generic操作表示获取Record的版本;对于List操作表示获取Record所在List的版本。
    */
    int32_t GetVersion() const;

SetVersion

 /**
    @brief  设置记录版本号
    @param [IN] iVersion     数据记录的版本号:  <=0 表示不关注版本号不关心版本号。具体含义如下。
                当class TcaplusServiceRequest的int SetCheckDataVersionPolicy(enum tagCHECKDATAVERSIONTYPE type)函数传入的参数type的值为CHECKDATAVERSION_AUTOINCREASE时: 表示检测记录版本号。如果class TcaplusServiceRecord的void SetVersion(IN int32_t iVersion)函数传入的参数iVersion的值<=0,则仍然表示不关心版本号不关注版本号;如果class TcaplusServiceRecord的void SetVersion(IN int32_t iVersion)函数传入的参数iVersion的值>0,那么只有当该版本号与服务器端的版本号相同时,Replace, Update, Increase, ListAddAfter, ListDelete, ListReplace, ListDeleteBatch操作才会成功同时在服务器端该版本号会自增1。
                当class TcaplusServiceRequest的int SetCheckDataVersionPolicy(enum tagCHECKDATAVERSIONTYPE type)函数传入的参数type的值为NOCHECKDATAVERSION_OVERWRITE时: 表示不检测记录版本号。如果class TcaplusServiceRecord的void SetVersion(IN int32_t iVersion)函数传入的参数iVersion的值<=0,则会把版本号1写入服务端的数据记录版本号(服务器端成功写入的数据记录的版本号最少为1);如果class TcaplusServiceRecord的void SetVersion(IN int32_t iVersion)函数传入的参数iVersion的值>0,那么会把该版本号写入服务端的数据记录版本号。
                当class TcaplusServiceRequest的int SetCheckDataVersionPolicy(enum tagCHECKDATAVERSIONTYPE type)函数传入的参数type的值为NOCHECKDATAVERSION_AUTOINCREASE时: 表示不检测记录版本号,将服务器端的数据记录版本号自增1,若服务器端新写入数据记录则新写入的数据记录的版本号为1。
    @retval void
    */
    //注意,对于Generic操作表示设置Record的版本,对于List操作表示设置Record所在List单元的版本。
    void SetVersion(IN int32_t iVersion);

从单个客户端角度看,实现基于乐观锁的数据更新流程主要有以下几个步骤:

  1. 查询将要修改的数据,并通过记录对象(Record)的GetVersion函数,获取数据当前的版本号,此时不需要通过SetVersion设置期望的数据版本号;

  2. 构造数据更新请求,并通过请求中的记录对象(Record)的SetVersion函数,设置期望的数据版本号,并发送请求;

  3. 检查更新请求的响应,如果返回-7949(SVR_ERR_FAIL_INVALID_VERSION)错误码,则表示在第1步和第2步之间,有其它客户端更新了数据,此时需要从第1步开始重试整个流程,如果返回的修改成功,则直接结束流程。

PB表乐观锁的数据更新步骤:

  1. 查询将要修改的数据,并通过记录对象的GetMessageOption函数,获取数据当前的版本号,即GetMessageOption(*msg, NS_TCAPLUS_PROTOBUF_API::MESSAGE_OPTION_DATA_VERSION, &record.version);

  2. 开启版本号校验,SetMessageOption函数开启版本号校验 MESSAGE_OPTION_VERSION_CHECK “1”

  3. 构造数据更新请求,并通过请求中的记录对象的SetMessageOption函数 MESSAGE_OPTION_DATA_VERSION,设置期望的数据版本号,即步骤1获取的版本号,并发送请求;

  4. 检查更新请求的响应,如果返回-7949(SVR_ERR_FAIL_INVALID_VERSION)错误码,则表示在第1步和第2步之间,有其它客户端更新了数据,此时需要从第1步开始重试整个流程,如果返回的修改成功,则直接结束流程。

注:

  1. 如果业务在更新数据时不需要做版本验证,可以调用SetVersion,设置-1或0。
  2. List表是一个大key的版本号,即更新key下的记录version值+1

results matching ""

    No results matching ""