批量操作接口的相关说明
批量操作指一个请求中携带多条记录或多个 List 元素。批量接口不保证原子性,一次请求可能部分成功、部分失败,业务必须逐条检查子错误码。
1. 接口分类
1.1 Generic 表批量接口
| 接口 | 类型 | 处理语义 | 说明 |
|---|---|---|---|
BatchGet |
读 | 拆成多个 Get |
TDR 表可指定返回部分一级字段;PB 表返回整条记录 |
BatchFieldGet |
读 | 拆成多个 FieldGet |
仅 PB 表;用于返回部分字段,含子级字段 |
BatchInsert |
写 | 拆成多个 Insert |
记录已存在则该条失败 |
BatchUpdate |
写 | 拆成多个 Update |
记录不存在则该条失败 |
BatchReplace |
写 | 拆成多个 Replace |
记录不存在则插入 |
BatchDelete |
写 | 拆成多个 Delete |
删除不存在记录时该条失败 |
Generic 表批量请求主要由接入层拆分、转发到存储层,再聚合多个单记录响应返回客户端。
1.2 List 表批量接口
| 接口 | 类型 | 处理语义 | 说明 |
|---|---|---|---|
ListGetBatch |
读 | 同一大 key 下批量读取元素 | 记录不存在时继续读下一条;其他错误会终止遍历 |
ListAddAfterBatch |
写 | 同一大 key 下批量插入元素 | 某条失败后继续处理下一条 |
ListReplaceBatch |
写 | 同一大 key 下批量更新元素 | 目标元素不存在则该条失败;失败后继续处理下一条 |
ListDeleteBatch |
写 | 同一大 key 下批量删除元素 | 某条失败后继续处理下一条 |
List 表批量请求都针对同一个大 key 下的多个元素,接入层透传给存储层,由存储层在一次请求中循环处理。
2. 通用限制
| 限制项 | 规则 |
|---|---|
| 单请求记录数 | 最多 1024 条记录或元素 |
| 单请求大小 | 不能超过 10 MB |
| 原子性 | 非原子;可能部分成功、部分失败 |
| 单记录打包 | C++ TDR SDK 中,若用 SetKey / SetValue 填充 BatchInsert / BatchUpdate / BatchReplace 的 record,需要再调用 record 的 Pack,用于标记该 record 已是完整记录;若用 SetData 填充则不需要 Pack;BatchGet / BatchDelete 不需要 |
3. 返回码与结果读取
批量响应包含两层错误码,业务判断每个 key 是否成功时,应以分错误码为准:
| 错误码 | 含义 | 获取方式 |
|---|---|---|
| 总错误码 | 本次批量请求的整体流程状态,例如请求解析、路由、接入层与存储层通信是否出现系统级错误 | response 返回值 |
| 分错误码 | 单条记录或元素的执行结果,即某个 key 成功、失败、超时或条件不满足 | 遍历响应中的记录 / 元素时获取 |
总错误码和分错误码的关系:
| 场景 | 关系 | 业务处理 |
|---|---|---|
| 总错误码为 0 | 表示整体流程正常完成;不代表每个 key 都成功 | 继续遍历分错误码,逐条判断 |
| 总错误码非 0 且响应包中有记录 | 表示整体流程发生错误或超时;响应中仍可能携带已明确失败 / 超时的 key | 仍要读取响应中的 key 和分错误码,比如批量Get的某个响应包的记录全是261(记录不存在),总错误码就是261,如果任意一个是0(成功),总错误码则是0,故不论何种场景,用户都需要读取分错误码判断哪个Key成功,哪个Key失败 |
| Swift / proxy 等待存储层超时 | 总错误码为超时错误;proxy 会构造响应并带回超时 key | 按这些 key 的分错误码标记失败 / 超时 |
| 响应包中没有出现的 key | 没有对应分错误码 | 按本次总错误码或本地超时策略处理,不能当作成功 |
proxy 对普通 Batch* 写类请求的回包规则:
| 接口 | key 是否返回 | 分错误码是否返回 | value 是否返回 |
|---|---|---|---|
BatchInsert / BatchUpdate / BatchReplace / BatchDelete |
返回 | 返回 recordResult[i] |
由 ResultFlag 控制 |
ResultFlag = NOVALUE(0) |
仍返回 key | 仍返回分错误码 | 不返回 value |
| Swift / 存储层响应超时 | 返回超时 key | 返回对应超时错误码 | 不返回 value |
因此,批量写接口的正确处理方式是:先遍历响应中每条 key 的分错误码,再决定该 key 成功还是失败。总错误码只用于判断整体流程状态,不能替代分错误码。需要控制 value 回带时,使用
SetResultFlagForSuccess/SetResultFlagForFail,详见 ResultFlag响应数据返回控制。
3.1 C++ TDR SDK 示例
C++ TDR SDK 完整示例(含 RecvResponse 主循环、FetchRecord 读分错误码、HaveMoreResPkgs 续包、ScanTimeout 超时兜底)以及对应的分包丢包处理思路,参见 响应分包和丢包处理说明 §3.2.1 Generic Batch 类。
3.2 C++ PB SDK 示例
C++ PB async 的批量写接口通过 TcaplusPbCallback 回调返回结果,三个回调的含义如下:
| 回调 | 触发条件 | 传入消息含义 | 业务处理 |
|---|---|---|---|
OnRecv(const std::map<Message*, int>& mapMsg) |
SDK 收到并成功解析一个批量响应包 | mapMsg 中每个 Message* 对应一个 key,int 是该 key 的分错误码;map 内既可能有成功 key,也可能有失败 key |
按 map 中的分错误码逐条判断 key 成败 |
OnError(const std::vector<Message*>& msgs, int errorcode) |
SDK/协议层遇到整批错误,或用 API_ERR_NO_MORE_RECORD 通知本次批量响应结束 |
msgs 是 SDK 当前保存的原始请求消息集合(实现中直接传 m_vecMsgs),不是失败消息集合;errorcode 是 SDK 回调层面的整体错误/结束码,不是 proxy 响应里的总错误码,也不是分错误码 |
errorcode == API_ERR_NO_MORE_RECORD 只表示结束;其他错误码表示本次批量请求在 SDK/协议层整体异常,本地仍未确认成功的 key 可按该错误码处理 |
OnTimeout(const std::vector<Message*>& msgs) |
SDK 等待响应超过超时时间 | msgs 是 SDK 当前保存的请求消息集合;如果此前已收到过部分分包,它不一定精确等于“未返回 key 集合” |
将本地仍未确认成功的 key 按超时处理 |
C++ PB SDK 完整示例(含 OnRecv 累积分错误码、OnError 处理 API_ERR_NO_MORE_RECORD 结束哨兵、OnTimeout 兜底、OnFinish 统一收口)以及 m_result 哨兵覆写思路,参见 响应分包和丢包处理说明 §3.2.1 Generic Batch 类。
3.3 Go SDK 示例
Go SDK 提供两套接口:
| 用法 | 入口 | 接口返回值语义 | 分错误读取方式 | 适用场景 |
|---|---|---|---|---|
| 高级用法(推荐) | client.DoBatchUpdate 等 DoBatch* 系列 + option.TDROpt / option.PBOpt |
只要有任一 key 失败,整体 err 就非 nil(封装的底层用法,故意如此设计的) |
opt.BatchResult[i](与 dataSlice[i] 一一对应) |
业务代码,按 key 维度判断成败 |
| 底层用法 | client.NewRequest + client.DoMore |
与老的 C++ TDR 接口一致:返回的 err 只反映 SDK / 协议层错误(如等待超时、参数错误),业务错误需要自己从 resp.GetResult() + 每条 FetchRecord() 读出 |
遍历每个 resp.FetchRecord() 返回的 error |
需要直接操作 Request / 多包遍历 |
高级用法内部就是封装了底层
NewRequest + DoMore,并自动把每条记录的分错误码、version 按下标回填到opt.BatchResult/opt.BatchVersion,再把任一子失败聚合成接口级err。两套接口最终走同一条协议链路,分错误码语义一致;但接口返回值的含义不同:高级用法是"全成功才返回nil",底层用法是"SDK 层无错就返回nil"。
Go SDK 完整示例(高级用法 DoBatchGet + 底层用法 NewRequest / DoMore)以及分错误码、丢包识别、超时兜底的处理思路,参见 响应分包和丢包处理说明 §3.2.1 Generic Batch 类。
4. 分包行为
4.1 分包规则
| 规则 | 说明 |
|---|---|
| 触发条件 | 响应包超过 256 KB 时触发分包 |
| 单记录完整性 | 分包不会切分单条记录;单条记录只会出现在一个响应包中 |
| 示例 | 3 条记录大小分别为 10 KB、251 KB、1 MB,可能分为 (10 KB + 251 KB) 和 (1 MB) 两个响应包 |
4.2 强烈建议:所有批量接口都手动开启分包
⚠️ "不允许分包" 是历史遗留的畸形设计:响应数据超过 256 KB 时,会出现只返回部分记录、
ListGetBatch提前截断等"静默丢数据"现象,但接口仍可能返回成功,业务很难感知。为了好记,请把下面这条作为统一规则:
- 任何批量接口、任何 SDK、任何表类型,调用前都手动设置
SetMultiResponseFlag(1),由 SDK 负责收齐所有分包;- 不要去记忆"哪个 SDK / 哪个命令字默认是否允许分包",记一条规则比记一张表更可靠。
各 SDK 中开启分包的入口:
| SDK | 设置入口 |
|---|---|
| C++ TDR SDK | TcaplusServiceRequest::SetMultiResponseFlag(1) |
| C++ PB SDK | TcaplusPbMessageOption::SetMultiResponseFlag(1)(或通过 SDK 封装的 SetMessageOption) |
| Go SDK 底层用法 | req.SetMultiResponseFlag(1) |
| Go SDK 高级用法 | option.TDROpt{ MultiFlag: 1 } / option.PBOpt{ MultiFlag: 1 } |
4.3 不开启分包时各接口的具体影响
不开启分包并不会让 SDK 报错,但响应数据过大时会发生静默截断,下表列出典型场景:
| 接口类别 | 不开启分包时的实际表现 |
|---|---|
Generic 表写类批量接口(BatchInsert / BatchUpdate / BatchReplace / BatchDelete) |
响应数据过大时只返回前若干条记录,剩余 key 的分错误码无法读到 |
BatchGet |
C++ TDR SDK 该接口本身会自动分包;其它语言 SDK 若未显式开启,可能仍受单包大小限制 |
ListGetBatch |
累计响应超过单包大小后,直接停止读后续元素,业务以为已读完,实际丢数据 |
ListAddAfterBatch / ListReplaceBatch / ListDeleteBatch |
写入仍会全量执行,但 ResultFlag 要求回带的记录数据可能无法全部返回 |
综上,所有批量接口都建议默认打开
SetMultiResponseFlag(1),不需要再按接口/SDK 区分。
5. 返回顺序
| 表类型 | 批量读返回顺序 | 说明 |
|---|---|---|
| Generic 表 | 无序 | 返回顺序不保证与请求 key 顺序一致;同一个请求多次执行,返回顺序也可能不同 |
| List 表 | 按 List 元素顺序 | 尾插场景先插入先返回;头插场景先插入后返回 |
6. 超时处理
接入层等待存储层单记录响应有超时时间,默认约 2 秒。超时后处理如下:
| 步骤 | 行为 |
|---|---|
| 1 | 先返回已经聚合但尚未发送的响应包 |
| 2 | 构造超时响应包,总错误码为超时错误(如 -7953) |
| 3 | 超时响应中携带超时记录的 key,之后本次批量请求结束 |
SDK 侧超时判断:
| SDK | 超时处理 |
|---|---|
| Java SDK / C++ PB SDK | SDK 超时后触发超时回调 |
| C++ TDR SDK | 业务自行实现请求超时判断 |
| Go SDK | 同步接口直接返回超时错误 |
如需调整接入层等待存储层的超时时间,请联系 DBA。
7. 性能与使用建议
| 对比项 | 批量接口 | 单记录接口 |
|---|---|---|
| 存储层开销 | Generic 表批量请求会拆成单记录请求,存储层开销基本相同;List 表批量请求在同一大 key 内循环处理 | 单条请求逐个处理 |
| 平均时延 | 通常更高,需要等待多条记录聚合返回 | 通常更低 |
| 网络流量 | 多条记录场景可减少协议头开销 | 单条记录场景更省 |
| 接入层 CPU | Generic 表需要拆分请求、聚合响应,接入层开销更高 | 接入层处理更简单 |
建议:
| 场景 | 推荐 |
|---|---|
| 一次需要处理多条记录,且业务希望减少请求数 | 使用批量接口 |
| 只操作 1 条记录 | 使用单记录接口 |
| 强依赖每条记录独立低时延 | 优先考虑单记录接口 |
| 任何批量接口 | 统一手动开启 SetMultiResponseFlag(1)(详见 §4.2) |
8. 接口文档与示例
各 SDK 和 API 的具体接口说明,请参考 TcaplusDB SDK & API。
常用 C++ TDR SDK 接口文档:
| 接口 | 文档 |
|---|---|
BatchGet |
Generic 表批量查询多条数据 |
BatchInsert |
Generic 表批量插入多条数据 |
BatchUpdate |
Generic 表批量更新多条数据 |
BatchReplace |
Generic 表批量替换多条数据 |
BatchDelete |
Generic 表批量删除多条数据 |
ListAddAfterBatch |
List 表向列表中批量插入元素 |
ListGetBatch |
List 表从列表中批量查询元素 |
ListReplaceBatch |
List 表向列表中批量更新元素 |
ListDeleteBatch |
List 表从列表中批量删除元素 |