当 q=8、n=3 时,CouchDB 如何计算某文档落在哪个分片?
解读
国内面试官问这道题,并不是想听你背文档,而是考察三件事:
- 是否知道 CouchDB 3.x 默认使用 “q+n 组合” 做一致性哈希,而不是早期版本里的 0-1023 虚拟节点;
- 能否把 文档 _id → 哈希 → 取模 → 映射到 q 个分片 → 再映射到 n 个副本节点 这条链路讲清楚;
- 能否在 90 秒内把计算过程用 中文+公式 说出来,让面试官相信你在生产环境真的调过集群。
知识点
- q:集群创建时固定的分片数,默认 8,不可在线修改;每个分片是一个 .couch 文件。
- n:副本因子,表示同一条数据要存几份;n≤集群节点数。
- 一致性哈希函数:CouchDB 使用 MurmurHash3 对文档 _id 做 32 位无符号整数哈希。
- 分片序号:shard = hash(_id) mod q,结果落在 [0, q-1]。
- 副本放置:系统把分片 s 的 n 份副本放在节点列表 s, s+1, …, s+n-1 mod N(N 为真实节点数),保证 机架感知 与负载均衡。
- 国内公有云常见 3 节点小集群,因此 n=3 时每个分片“刚好”三副本,没有多余节点做异地容灾,面试时要主动提到 “如果后续扩容到 5 节点,副本会自动漂移” 以示深度。
答案
计算步骤一句话:先哈希,再取模,再映射节点。
- 取文档 _id,例如 "user_330108198803123456"。
- 计算 32 位无符号 MurmurHash3 值,假设得到 0x7f2058a2。
- 对 q=8 取模:shard = 0x7f2058a2 mod 8 = 2。
- 副本放置:若集群节点按字典序编号为 nodeA、nodeB、nodeC,则
- 主分片落在 nodeC((2+0) mod 3),
- 第一副本落在 nodeA((2+1) mod 3),
- 第二副本落在 nodeB((2+2) mod 3)。
- 结论:该文档属于 分片 2,三副本分别位于 nodeC、nodeA、nodeB;读写时只要 quorum=2 即可返回成功。
拓展思考
- 如果半年后业务暴涨,需要把 q 从 8 升到 16,国内常规做法是 离线重建集群(备份→新建 16 分片集群→同步→切换),因为 CouchDB 目前 不支持在线重分片;面试时可补充“用 couchdb-replicator 做全量+增量双写,切换窗口控制在 3 分钟以内”。
- 当 n=3、节点数从 3 扩容到 5 时,系统会自动把部分副本漂移到新节点,但 分片数 q 不变,因此热点 _id 的哈希分布不会恶化;这一点和 MongoDB 的 chunk 分裂思路不同,面试官常拿来对比。
- 国内金融客户常要求 “同城双活”,此时可把 n 设为 4,机架感知策略写成 “[zone1, zone1, zone2, zone2]”,保证 RPO=0、RTO<30s;如果你能主动提到 “在阿里云 ECS 上把 zone 信息写进 vm.args”,会大大加分。