遍历和批量操作相关问题

1. 响应包的分包问题

1.1 什么样的响应会涉及分包?

TcaplusDB返回的响应包返回多个记录时,具体的,相关的请求有:

  • BatchGet,批量查询
  • BatchUpdate、BatchDelete,批量修改或删除,设置返回旧记录的flag时,会返回批量的旧记录
  • GetByPartKey(PB API中对应的是IndexGetRequest),通过本地所有查询,返回相同索引key下的所有记录
  • UpdateByPartKey、DeleteByPartKey,批量修改或删除相同索引key下的所有记录,设置返回旧记录的flag时,会返回批量的旧记录
  • ListGetAll,List表查询List Key下所有的记录
  • Traverse,全表遍历查询
  • 全局索引查询,当表配置了全局索引,可通过SQL查询返回批量的记录

1.2 触发分包条件是什么?

当响应包超过256KB会触发分包。但保证分包不会切分单个记录(不管这个记录多大),例如,响应包的3个记录分别是10KB、251KB、1MB,会分为2个包返回,即(10KB、251KB)和(1MB)。

1.3 分包返回情况下,如何判断最后一个包?

  • C++ TDR API,可调用TcaplusServiceResponse::HaveMoreResPkgs()判断
  • C++ PB API,回调中返回错误码API_ERR_NO_MORE_RECORD

1.4 设置是否分包?

对于 PB API,默认是分包的。

对于 TDR API,可通过TcaplusServiceRequest::SetMultiResponseFlag()接口设置是否分包,当设置flag=0(默认值也是0),表示不允许一个请求包自动响应多个应答包, 这种情况下,一趟响应若涉及多个记录(超过256KB),只返回第一个包(部分记录)。未设置分包,可能会出现返回响应包数据记录为空或者比实际少的情况。

注意,TcaplusServiceRequest::SetMultiResponseFlag()仅对部分命令字生效,具体的有:

  • 3.46.0版本(包括3.46.0)以前:
    • TCAPLUS_API_LIST_GETALL_REQ
    • TCAPLUS_API_LIST_DELETE_BATCH_REQ
    • TCAPLUS_API_LIST_GET_BATCH_REQ
  • 3.55.0版本(包括3.55.0)之后:
    • TCAPLUS_API_LIST_GETALL_REQ
    • TCAPLUS_API_LIST_DELETE_BATCH_REQ
    • TCAPLUS_API_LIST_GET_BATCH_REQ
    • TCAPLUS_API_LIST_ADDAFTER_BATCH_REQ
    • TCAPLUS_API_LIST_REPLACE_BATCH_REQ
    • TCAPLUS_API_BATCH_GET_REQ
    • TCAPLUS_API_BATCH_DELETE_REQ
    • TCAPLUS_API_BATCH_REPLACE_REQ
    • TCAPLUS_API_BATCH_UPDATE_REQ
    • TCAPLUS_API_BATCH_INSERT_REQ

1.5 自定义分包?

对于请求XxxByPartKey、ListGetAll,可以在请求中设置Limit、Offset属性来控制返回部分的数据,通过递增Offset来获取后续的记录。相关接口:

  • C++ TDR API,TcaplusServiceRequest::SetResultLimit()设置分批,TcaplusServiceResponse::GetRecordNextOffset()获取下一批offset
  • C++ PB API,在IndexGetRequest中设置分批,IndexGetResponse.m_nRemainNum获取剩余记录数

对于全表遍历,类似,设置Limit、Range属性。相关接口:

  • C++ TDR API,TcaplusServiceTraverser::SetResNumPerReq()TcaplusServiceTraverser::SetRange()
  • C++ PB API,当前无

但超过256KB会触发分包这个规则还是不变,如一次请求设置Limit=3、Offset=0,响应包的3个记录分别是10KB、251KB、1MB,还是会分为2个包返回,即(10KB、251KB)和(1MB)。

1.6 返回的批量记录是否有顺序保证

没有。

1.7 遍历原理及常见问题梳理

Tcaplus支持全表遍历操作,包括generic表和list表,会根据sharding逻辑在各个该表分布的tcapsvr上发起遍历任务,因此,整个遍历请求性能相对较低,建议设置从tcapsvr slave上遍历数据(从tcapsvr slave上遍历数据,不会影响tcapsvr master对外提供服务),即接口:SetOnlyReadFromSlave(bool flag)。

如果每次要拉取的数据不多,可以优先考虑本地索引能否满足需求。

遍历器一旦开始以后,Tcaplus的tcapsvr会启动内部迭代器分时间片去遍历读取树上的节点数据,先找key后找value,然后将遍历到的数据打包到buffer里面,返回给service api,service api内部的遍历逻辑会根据预期seq与收到的seq是否一致,来判断是否丢包、收到重复的包等,一旦确认收到的包符合预期,则主线程调用RecvResponse时可以拿到完整包,然后调用FetchRecord可以解析到一条业务记录,遍历可以随时中断和终止,可以有条件的恢复。整体而言,遍历在service api内部采用的是ping-pong自动触发机制,在收到符合预期的包以后会自动决定是否要继续发起下一个遍历请求(比如后端存储svr明确返回了BUSY的错误,则遍历会中断),一直到明确遍历完成或业务停止遍历。

实践教程

每个表同时只能有一个遍历器在运行,单个gamesvr同时最多支持8个遍历器,建议遍历器要少一点,否则对正常的读写访问时延有影响。

Tcaplus_client的遍历是串行的,只能支持小数据量,大数据量要用分析性文本导出功能,对冷备文件进行并行处理。

常见问题

  • 问:Tcaplus遍历和其余的读写操作可以同时进行吗?
  • 答:可以同时进行,遍历和更新操作一起进行时,则存在如果是更新在前,遍历在后,则遍历的数据不是遍历开始的数据(数据快照),如果遍历在前,更新在后,则遍历不到最新修改的数据。

  • 问:怎么判断遍历是否已经结束?

  • 答:请按照state判断遍历是否结束,即接口GetState处于ST_IDLE状态。

  • 问:遍历时的cursor游标在何种情况下失效,导致遍历接口返回错误?

  • 答:主备切换,搬迁类场景都会导致遍历失败,正常的数据读写不会导致错误发生。

  • 问:遍历是否可停止?是否可中断

  • 答:TDR API:在任何状态都可以调用遍历器的Stop接口停止遍历,在遍历器处于NORMAL或RECOVERABLE状态时执行Resume接口可中断一个遍历,调用Resume成功后可以调用ContinueTraverse继续遍历中断的遍历,恢复执行。

    PB API目前没有这样的接口,不支持。
    
  • 问:遍历时如何节省流量?

  • 答:调用SetFieldNames接口,设置遍历时期望返回的value字段,设置部分字段返回和返回所有字段相比,对gamesvr、Tcaplus的接入层,这两者的带宽和cpu压力会更小, 对Tcaplus存储层的cpu和带宽的压力更小。 具体是和整条记录以及业务访问的字段的长度的比例有关,对存储层的io压力不变。

  • 问:是否支持条件过滤遍历?

  • 答:3.55.0及以上的版本,支持条件过滤遍历。

  • 问:SetResNumPerReq(1)时是否可能分包?

  • 答:Tcaplus后端是按照256KB进行打包的,一旦超过就会分包,调用SetResNumPerReq(1)只是代表每个请求包对应1个响应包,1个响应包中实际上可能有多条记录。项目组只需要考虑不断的收包并根据GetStat判断遍历是否结束即可,不用关心分包细节,ServiceApi投递给业务的都是一个完整的包。

  • 问:Tcaplus支持全Key遍历吗?

  • 答:暂时还没有支持,有计划支持。

  • 问:怎么去组织函数完成遍历?

  • 遍历的例子(example)

    generic表:C++_tdr1.0_asyncmode_generic_simpletable/SingleOperation/travers/main.cpp

    list表:C++_tdr1.0_asyncmode_list_simpletable/SingleOperation/listtravers/main.cpp

    条件过滤:C++_tdr1.0_asyncmode_generic_simpletable/SingleOperation/condition_operation/condition_traverse.cpp

  • 问:遍历时,新插入的数据能否遍历到?

  • 答:新插入在遍历前可以遍历到数据的,遍历的时候,并不会生成快照,而是按照文件偏移进行遍历的,如果更新或者删除或者插入的位置是已经遍历过的位置,那么不会读取到,否则是可以遍历到的

1.8 setCondition不支持 TcaplusApiListAddAfterReq 和 TcaplusApiListAddAfterBatchReq 这样的插入命令吗?

不支持,insert类不支持。insert前记录不存在,条件过滤没意义

1.9 Batch操作时,即使每10个请求中有一个失败,也不会返回错误。有什么方法可以检测到错误吗?

C++ api中Batch操作 result为0时,才可以调用FetchRecord来获取每条记录的执行结果,FetchRecord的返回值就是每条记录的执行结果;如果result非0,那么表示本次batch操作是失败的,不能调用FetchRecord函数。一次性记录请求数量限制为1024个,超过1024条数据,需要业务自己分成多个batch请求发送给后台。

2. TCAPLUS_API_GET_BY_PARTKEY_REQ在分页拉取表数据过程中,如果又有新的数据插入,会不会出现以前的老数据被跳过的情况?比如每次拉取500,拉取第一页的时候有数据写入,下次那个本来501的数据会不会跑到前面去了,导致后面分页都拉不到旧的501数据?

这个是有可能的。前面新写入数据,是可能导致本来500的数据变成501, 这里可能会记录会被回复多次,删除的话,可能导致记录被跳过。新写入的只会导致后面的记录后移,不会跑到前面去;partkey的索引记录是可能存多条,按照HASH段可能分成多个记录,每次新写入记录,会追加到对应的分段记录的尾部,这样下一个HASH段的记录就相当于后移了

3. 请问batch操作可以同时操作多个数据表吗?

不可以的,同一张表的多个key

results matching ""

    No results matching ""