一致性、分片与副本

本节目标

你将从单机 point 扩展到分布式 collection:理解 shard 与 replica、写确认与读一致性、并发 upsert/delete 的冲突、分片键与 fan-out、扩缩容/重平衡,并用可观察测试定义业务需要的语义。

Shard 与 replica 不是一回事

Shard

一个 collection 被拆成若干不相交数据子集。每个 point 通常只属于一个逻辑 shard。分片用于:

  • 超过单机容量;
  • 分摊查询/写入;
  • tenant/partition 路由;
  • 并行构建与维护。

Replica

同一 shard 的额外副本。副本用于:

  • 节点故障时继续服务;
  • 分担读取;
  • 升级/维护;
  • 降低单份数据丢失风险。

增加 shard 不自动增加容灾;增加 replica 不自动增加总唯一容量。Replication 也不是 backup:所有副本都可能复制误删除、逻辑损坏或恶意写入。

一次查询可能发生什么

  1. 接入节点接收 query 与 filter;
  2. 根据 collection topology 找到相关 shards;
  3. 每个 shard 在一个或多个 replica 上执行局部 top-k;
  4. 协调节点合并局部候选;
  5. 应用 filter/排序语义;
  6. 返回全局 top-k。

如果查询必须 fan-out 所有 shards,尾延迟受最慢 shard 影响。Custom shard key 可以缩小路由,但错误键会漏数据或形成热点。

Global top-k 不能简单取每 shard top-k 以外的任意少量而不验证;过滤和分数分布会影响合并需要的 oversampling。

为什么会看到旧结果

分布式系统中:

  • 写入先到某 replica;
  • 其他 replica 延迟应用;
  • 查询读到尚未更新的副本;
  • 并发两次 upsert 在不同 replica 顺序不同;
  • 删除与旧 upsert 交错;
  • 新索引/segment 尚未 searchable;
  • shard 正在转移或恢复;
  • cache 仍保留旧候选。

“写 API 返回成功”必须映射到明确确认级别,不能自然推导“所有后续查询都立刻只见新版”。

先写业务语义

不要先选 quorum/strong/eventual名词。先回答:

  • 写成功需要几个副本或哪个 leader 确认?
  • 同一用户写后立即搜,必须 read-your-writes 吗?
  • 新 revision 最迟多久可搜索?
  • 两个客户端并发写同一 point,按 source revision、leader order 还是 last-write-wins?
  • Delete 与旧 upsert 乱序时谁赢?
  • 节点/网络故障时优先可用还是拒绝不确定读写?
  • 查询是否必须读到 majority/all 一致结果?
  • Collection schema/alias 修改与 point 写入是否有相同保证?
  • 索引构建未完成时是 exact fallback、旧索引还是拒绝?

用具体时间、版本和错误行为写 SLO,例如:

Source revision r8 提交成功后 30 秒内,授权查询不得再返回 r7;超时则撤下文档并告警。

这比“最终一致”更可验收。

写确认、读一致性与 ordering

产品可能提供:

  • write acknowledgement count/factor;
  • leader/strong ordering;
  • read one/quorum/majority/all;
  • wait until operation applied;
  • optimistic row/version 条件;
  • transaction(单表、单 shard 或更大范围)。

代价通常是更强确认需要更多活跃节点、更多网络往返,并在分区时降低可用性。具体语义和失败时“是否部分应用”必须读产品文档并故障测试。

截至 2026-07-22,Qdrant 当前文档明确区分 collection topology/structure 的 consensus 与 point operations,并提供 write consistency、read consistency、write ordering 选项;Points 文档还给出 payload version 条件更新。其默认与范围是该版本产品事实,不是所有向量库模型,条件更新也不会替应用理解业务 source identity。pgvector 则继承 PostgreSQL transaction/replication 体系,语义完全不同。

并发更新用版本解决业务冲突

数据库 ordering 只能决定执行顺序,不知道哪个 source revision 更正确。若 source_revision 是 opaque identity,就不能按字典序或数字后缀比较;但同一 point 跨生命周期仍必须由 canonical source 保证 identity 不复用。应用需要由 canonical source/工作流确定合法 successor,并记录:

  • source_revision;
  • tombstone 的 deleted_source_revision;
  • expected-current source revision;
  • delete event ID / fence;
  • published pointer。

规则示例:

  • Writer A 读取当前 r7,用 expected=r7 发布候选 r8;成功后,仍提交 expected=r7 的 writer 全部 CAS 失败;
  • Delete 只有在 expected-current 匹配时才能写 tombstone,并持久化被删 source 与 delete event;
  • Tombstone 默认拒绝后续 upsert;显式 resurrection 必须匹配旧 source/delete fence,并使用不同的新 source identity;
  • 同 revision 但 vector、payload 或 hash 不同进入冲突队列;
  • ACL revision 可独立且不得回退;
  • query 只读 published revision。

这使 replica 恢复、重放和 shard transfer 仍能检查语义新旧,但只保存“当前 source + 当前 tombstone”不能解决 ABA。若生命周期出现 r1 → delete → r2 → delete → r1,很旧的 delete(r1, d1) 可能重新匹配。生产系统需要不可复用 generation、单调 row/store version 或永久 history fence;append-only lifecycle/audit log 还要在 resurrection 移除当前 tombstone 后继续保留历史事件。

读己之写(Read-your-writes)

用户上传文档后立即查询,可能需要看到它。实现方式包括:

  • 等待写入达到所需确认和 searchable 状态;
  • Query 携带 minimum revision/fence;
  • Session 暂时路由到已确认 replica;
  • 在索引可见前合并小型 recent-write buffer;
  • UI 明确“处理中”,完成后才开放搜索。

不要用固定 sleep 5 秒代替状态和超时。记录 operation/event ID,从写确认追踪到 search canary。

同一进程中的多个 repository/store 实例也可能持有不同快照;“单进程”不等于“单实例”。读路径若无法证明本地 snapshot revision 仍是当前值,就应重新加载一致快照,或像本库玩具项目一样抛出冲突并 fail-closed。尤其在 delete 或 ACL 收紧后,不得由 stale instance 返回旧 point;summary/no-op 也不能把过期缓存误报为当前成功状态。

分片键权衡

随机/哈希 point ID

优点:均匀分布。缺点:tenant 查询可能 fan-out 全 shards。

Tenant ID

优点:tenant 查询可定向,隔离直观。缺点:大租户成为热点或超过单 shard。

Tenant group / composite key

可将大租户拆 buckets、长尾租户分组;路由和重平衡更复杂。

选择依据:

  • tenant size/增长长尾;
  • query 是否跨 tenant;
  • filter 组合;
  • 热点与并发;
  • 单 shard 容量/索引构建;
  • reshard 能力和停机代价;
  • data residency。

给当前数据均匀的键不一定适合两年后。建立每 shard point/bytes/QPS/P99/写入/索引大小监控和重分片阈值。

扩容与 shard transfer

扩容不是“加节点就自动变快”。需要确认:

  • 新节点是否自动接收 shards;
  • rebalance 手工还是自动;
  • transfer 复制 records 还是 snapshot/index;
  • 目标是否重新建 ANN/量化;
  • transfer 期间写如何排队/重放;
  • 切换 ordering;
  • 峰值磁盘/网络;
  • 失败回滚;
  • downscale 前是否已移走所有唯一 shard。

Qdrant 当前文档列出多种 shard transfer 方法及不同 index/ordering/资源属性;这些细节会变化,应按实际版本运行演练。

副本健康与降级

监控:

  • active/partial/dead replica;
  • replication lag;
  • write acknowledgement failures;
  • shard under-replicated;
  • leader/consensus changes;
  • query partial failure;
  • rebuild/optimization backlog;
  • disk high watermark;
  • top-k 返回数异常。

失败时定义:

  • 是否允许 partial result;
  • 是否降级 exact/关键词/缓存;
  • 是否拒绝写;
  • 是否撤下可能旧权限的数据;
  • 如何通知调用方而不是伪装完整成功。

权限和删除场景通常不应以可用性为由返回不确定旧数据。

故障测试

场景验证
一个 replica 停止读写可用性、确认级别、告警
网络分区哪一侧接受写、恢复后冲突
两个 writer 都基于 r7 更新首个 CAS 成功,另一个 expected=r7 的写明确冲突
delete fence 与迟到 upsertTombstone 默认拒绝复活;只有匹配 fence 的显式 resurrection 可进入下一版本
ACL/delete 后 stale store instance 查询读前验证 snapshot revision;变化时 reload 或 fail-closed,不返回旧权限/旧 point
source identity 跨生命周期复用Generation/history ledger 识别 ABA,旧事件不得因身份再次相等而成功
shard transfer 中写入无漏项/重复/旧 ACL
新索引构建searchable lag 与 fallback
磁盘满写失败清楚,不损坏旧状态
协调节点超时partial results 不伪装完整
恢复旧 snapshot删除日志和新 revision 重放

使用测试数据和隔离环境,不在生产随意断节点。

常见错误与排查

  • 加 replica 当备份:补独立快照、恢复和误删保护。
  • 默认设置当业务保证:把写/读/ordering 映射到场景测试。
  • 并发靠 last-write-wins:source revision/tombstone 才知道业务新旧。
  • 把 opaque identity 当作可复用值:canonical source 保证不复用,并增加 generation/history fence 防 ABA。
  • 单进程就忽略 stale read:多实例仍会缓存旧快照;search、summary 与 no-op 都要校验 revision。
  • 按 tenant 分片后大户热点:拆 bucket 或专属 shard/collection。
  • 加节点没有容量变化:shards 未转移/重平衡。
  • 部分 shard 超时仍返回 200:定义 partial response 和上游降级。
  • 睡眠后验证可见性:改 operation ID + revision canary。
  • 权限更新期间优先可用:对不确定旧 ACL fail-closed。

练习

  1. 为“上传后 30 秒可搜,只能见最新 revision”写端到端验收。
  2. 画 4 shards × 2 replicas 的查询 fan-out 与合并。
  3. 不比较 revision 字符串大小,为“两个 writer 基于 r7 更新 + 一个绑定当前值的 delete event”设计所有乱序处理规则,再加入 identity 复用的 ABA 反例。
  4. 比较 tenant shard key 与随机 key 在长尾租户下的热点/fan-out。
  5. 写一次 shard transfer 演练的容量、写入、query 和回滚观察项。
  6. 为一个 replica 失联定义读写降级与安全边界。

掌握检查

  • 我区分 shard 的容量/并行与 replica 的可用性。
  • Replication 不代替 backup。
  • 业务语义用 revision/时间/SLO 写清,不只用一致性名词。
  • Database ordering 与 expected-current source CAS 共同防止乱序覆盖;opaque revision 不做字符串大小比较。
  • Source identity 跨生命周期不可复用;generation/history ledger 防止只看当前值导致 ABA。
  • Read-your-writes 用状态/fence,不用固定 sleep。
  • Stale instance 在 search/summary/no-op 前校验 snapshot revision,权限与删除不确定时 fail-closed。
  • 分片键基于 query、增长、热点、residency 与 reshard 能力。
  • 扩容包含 shard transfer/rebuild/rebalance 验证。
  • 故障时 partial/旧权限结果不会伪装成功。

小结与下一步

副本提高可用性,却会同步复制逻辑错误;真正的灾难恢复需要独立快照、保留与演练。下一步:[[Knowledge/AI Agent Engineer/docs-CN/向量数据库/06-备份恢复与迁移|备份、恢复与迁移]]。

参考资料

来源获取日期:2026-07-22。返回 [[Knowledge/AI Agent Engineer/docs-CN/向量数据库/00-目录|向量数据库目录]]。