TcaplusDB TDR表 C++ SDK 常见问题
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 返回的批量记录是否有顺序保证
没有。
2 遍历原理及常见问题梳理
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的遍历是串行的,只能支持小数据量,大数据量要用分析性文本导出功能,对冷备文件进行并行处理。
3 常见问题
问:Tcaplus遍历和其余的读写操作可以同时进行吗?
- 答:可以同时进行,遍历和更新操作一起进行时,则存在如果是更新在前,遍历在后,则遍历的数据不是遍历开始的数据(数据快照),如果遍历在前,更新在后,则遍历不到最新修改的数据。
问:怎么判断遍历是否已经结束?
- 答:请按照state判断遍历是否结束,即接口GetState处于ST_IDLE状态。
问:遍历时的cursor游标在何种情况下失效,导致遍历接口返回错误?
- 答:主备切换,搬迁类场景都会导致遍历失败,正常的数据读写不会导致错误发生。
问:遍历是否可停止?是否可中断
- 答:在任何状态都可以调用遍历器的Stop接口停止遍历,在遍历器处于NORMAL或RECOVERABLE状态时执行Resume接口可中断一个遍历,调用Resume成功后可以调用ContinueTraverse继续遍历中断的遍历,恢复执行。
问:遍历时如何节省流量?
- 答:调用SetFieldNames接口(其中pb api 3.55.0SP17支持,老版本不支持遍历部分字段),设置遍历时期望返回的value字段。如果只返回key字段,SetFieldNames接口参数field_name[]传空数组+field _count为0。
问:是否支持条件过滤遍历?
- 答: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
问:遍历时,新插入的数据能否遍历到?
- 答:新插入在遍历前可以遍历到数据的,遍历的时候,并不会生成快照,而是按照文件偏移进行遍历的,如果更新或者删除或者插入的位置是已经遍历过的位置,那么不会读取到,否则是可以遍历到的
问:如何查看C++ SDK版本号?
- 方法一:在api启动日志里有打印,可以搜下“release”关键词
- 方法二:在下载的Tcaplus包里查看version文件(路径release\x86_64\version),文件中也有版本号。提供版本号时,需要带上日期,即带上版本号 build at 后面的部分一起,如3.46.0.199750.x86_64_release build at 20220524
- 方法三:通过 strings|grep version查看版本号 strings libtcaplusserviceapi.a | grep x86_64_release
问:API打日志导致core问题 使用api的进程退出时,在日志句柄销毁前先调用下Fini函数,防止日志句柄销毁了,API打日志导致core
问:如何升级C++ SDK?有哪些注意事项?
SDK的接口是兼容的,理论上无需修改代码,升级需要替换相应库的include和lib目录,注意:C++ SDK和Tbase的版本绑定,升级需要下载对应版本的Tbase依赖库