遍历说明
遍历(Traverse / ListTraverse)用于全量扫描一张表的所有记录,常用于数据导出、迁移、对账、批量改造等场景。本文说明遍历的原理、限制、注意事项,以及"遍历开始后能否中途停止 / 续传"这一关键问题。
1. 遍历原理
遍历是 SDK 侧驱动的一个状态机 + 游标扫描过程,不是一次请求一次性返回全表。
1.1 整体流程
GetTraverser(zoneId, table) // 取得遍历器,进入 ST_READY
│
Start() // 进入 ST_NORMAL,先拉取表的 shard 列表
│
┌────▼─────────────────────────────────────┐
│ 按 shard 逐个扫描: │
│ 对当前 shard 发遍历请求,带上 offset 游标 │
│ tcapsvr 返回一批记录 + 新 offset + 完成标志 │
│ 当前 shard 扫完 → offset 归 0,切下一个 shard│
└────┬─────────────────────────────────────┘
│ 所有 shard 扫完
ST_IDLE → Stop() // 结束并释放遍历器
- 基于 shard + offset 的游标:表被划分为多个 shard(路由范围
[0, 10000))。SDK 先发GetShardListReq拿到 shard 列表,再对每个 shard 携带offset反复请求,tcapsvr 每次返回一批记录并回带下一个 offset 与是否完成标志,直到该 shard 扫完,再切到下一个 shard。 - SDK 收包驱动:发出请求后,业务必须持续驱动收包,SDK 内部会在收到一批后自动续发下一批请求。
- 丢包检测与重发:SDK 通过应答 sequence检测丢包,游标只在按序正确收到一个包后才推进。一旦发现缺包(收到的序号大于期望值),该乱序包会被丢弃(不交付业务),并从上次已确认的 offset 重发后续请求(go-back-N),SDK自动处理丢包场景,对业务透明
- 主备切换检测:遍历请求会带回处理它的 svrId,SDK 通过
CheckIfSwitchMS校验,若中途发生主备切换会被感知(可能导致当前 shard 需要从头重扫)。
1.2 遍历不是快照
遍历读取的是 tcapsvr 当前数据按 shard+offset 推进的游标结果,不是某一时刻的一致性快照。遍历持续期间表数据的增、删、改可能导致:
- 遍历开始后新写入的记录,可能被遍历到,也可能漏掉;
- 遍历期间被删除的记录,可能已经读到、也可能读不到;
- 极端情况下(如 offset 重排、主备切换重扫)存在少量重复或遗漏。
因此遍历适合"近似全量"的导出 / 对账,不应作为强一致的事务快照使用。
如需某一时刻的一致性快照(精确全量导出、离线分析、按时间点重建数据等),不要用在线遍历,推荐改用基于备份的方案:
2. 遍历限制
| 限制项 | 说明 |
|---|---|
| 单请求应答数 | SetResNumPerReq(num) 控制一个遍历请求让服务端连续回多少个单位的应答,SDK 收齐后才发下一个请求(流水线批量大小):· Generic 表:num 为一个请求对应的应答包数目(不是记录数),即服务端连续回 num 个数据包后 SDK 再续发; · List 表:num 为一个请求返回的 list key 数目(不是包数,一个 key 可能因元素多再分多个包)。 默认 1,最大 100,传 0 返回失败。调大可减少请求往返、提升吞吐,但sdk更容易丢包重试,默认推荐为1 |
| 总记录数 | SetLimit(n):最多遍历 n 条,-1 表示不限制(全表) |
| 返回字段 | SetFieldNames 指定要返回的 key/value 字段(TDR 表用字段名 / PB 表用字段路径),字段数最多 256 个(TCAPLUS_MAX_VALUE_FIELD_NUM,3.46.0 之前为 128);不指定则返回整条记录 |
| 条件过滤 | SetCondition 支持服务端过滤,语法见 条件过滤和更新语法说明 |
| 并发遍历数 | 单个 client 实例最多同时进行 8 个遍历,超出会取不到遍历器 |
| 读 slave | SetOnlyReadFromSlave(true) slave遍历,减小对master的影响,详见 读分流说明 §5.5 |
3. 遍历注意事项
- 必须持续驱动收包:遍历不会自己跑完。要在循环里不断
RecvResponse直到状态变为完成。 - 遍历对 tcapsvr 有压力:全表扫描是后台重操作,建议结合读 slave(见读分流说明)降低对 master 的冲击,并控制
SetResNumPerReq与遍历节奏。 - 做好超时与异常兜底:大表遍历耗时长,要处理瞬时错误(进入
ST_RECOVERABLE后按流控间隔Resume)、不可恢复错误(ST_UNRECOVERABLE只能Stop重试遍历)。 Stop后对象不可再用:调用Stop()之后该遍历器被回收并重置,不得再对其做任何操作,否则行为未定义。- 一致性:遍历非快照(见 §1.2),对账类场景要容忍少量重复 / 遗漏,或在业务侧做去重。
4. 遍历开始后无法中途"暂停"
遍历一旦 Start(),要么驱动它自然跑到结束(ST_IDLE),要么 Stop() 直接终止,没有"暂停后原地继续"的能力。
4.1 为什么不能暂停
- 遍历进度(当前 offset、当前 shard、shard 列表、遍历器内部 id)全部保存在遍历器对象的内存里。
- 一旦
Stop(),遍历器被Reset(offset 归 0、shard 列表清空、状态回到空闲)并归还给内部池,进度全部丢失。 Resume()看起来像"继续",但它只用于同一进程、同一遍历对象在遇到瞬时中断(限流 busy、单批超时、丢包)后的错误恢复,依赖内存里尚存的进度。它不能在Stop()之后、更不能在进程退出 / 重启后恢复。
所以:"遍历到一半,先停掉,过段时间再从断点继续"——靠 Resume() 是做不到的。
5. 与其它文档的关系
- 遍历读 slave 的开关与回退:读分流说明 §5.5
- 遍历响应分包与丢包识别:响应分包和丢包处理说明
- 条件过滤语法:条件过滤和更新语法说明