CAP/BASE、一致性模型与 Paxos/Raft

共 38 题
📑 题目列表 38 题
#
★★★

1. 线性一致性(Linearizability)与可串行化(Serializability)的本质区别是什么?为什么严格可串行化(Strict Serializability)被视为二者的交集?

线性一致性(Linearizability)与可串行化(Serializability)的本质区别是什么?为什么严格可串行化(Strict Serializability)被视为二者的交集?

  • 线性一致性的操作语义
  • 可串行化的事务语义
  • 严格可串行化的交集性质

线性一致性(Linearizability)是单对象(单操作)的一致性模型,要求每个操作在调用与返回之间有一个线性化点,所有操作按该点全局排序,读操作能看到该点之前所有已提交的写,强调“实时顺序”与读到的旧值限制。可串行化(Serializability)是事务(多操作)的隔离语义,要求并发事务的执行结果等价于某个串行顺序,但不保证该顺序与真实时间一致,只要求事务内多操作原子、有序。严格可串行化(Strict Serializability)同时满足二者:事务结果等价于串行执行,且串行顺序与真实时间顺序一致,因此被视为二者的交集——既保证事务串行等价,又保证操作按实时顺序可线性化。

区分二者的关键是“单操作 vs 事务”与“是否尊重实时时间”。线性一致性约束单操作排序,可串行化约束事务等价,严格可串行化把两者结合,是分布式事务与数据库一致性的重要交汇点。

#
★★★

2. NWR quorum 模型(Dynamo 风格)如何通过 R+W>N 保证读到最新值?Sloppy Quorum 与 Hinted Handoff 又会引入怎样的一致性退化?

NWR quorum 模型(Dynamo 风格)如何通过 R+W>N 保证读到最新值?Sloppy Quorum 与 Hinted Handoff 会引入怎样的一致性退化?

  • NWR 模型与 R+W>N
  • 写入与读取的仲裁
  • Sloppy Quorum 与 Hinted Handoff 的退化

NWR 模型(N 个副本,W 个写副本,R 个读副本)中,当 R+W>N 时,任意读写集合必然有交集,读操作能读到写入的副本,从而保证读到最新值。写入要 W 个副本确认成功,读取要 R 个副本返回并取最新版本。Sloppy Quorum 在部分副本不可达时,允许写入落到临时节点(不在原副本集合),以换可用性,但可能放宽 R+W>N 的约束,导致读到旧值。Hinted Handoff 把临时节点应写的数据暂存并稍后回传原节点,但回传前的窗口内数据可能不可见或覆盖旧值,造成一致性退化(最终一致性期间读旧值)。

NWR 是“读仲裁”保证一致性的基础,Sloppy Quorum 与 Hinted Handoff 是可用性优先时的妥协,理解其放大了“读到旧值”的风险窗口。

#
★★★

3. CAP 定理精确约束的是什么(发生网络分区时一致性与可用性只能二选一)?为什么它常被误读为“三选二”,且分区未发生时并不适用?

CAP 定理精确约束的是什么?为什么常被误读为“三选二”,且分区未发生时并不适用?

  • CAP 定理的精确表述
  • “三选二”的误读
  • 分区未发生的场景

CAP 定理精确约束的是:当发生网络分区(Partition)时,系统只能在一致性(C)与可用性(A)之间二选一。分区时,若需保证一致性(所有副本一致),因无法与对端通信,只能拒绝部分请求(牺牲可用性);若需保证可用性(所有请求都有响应),则无法保证所有副本一致(牺牲一致性)。常见误读是“三选二”(C/A/P 任选两个),因为 CAP 表面上列了三个属性,但 P 是发生与否的条件而非可选项,分区不可避免时只能在 C 与 A 间取舍。分区未发生时,系统可同时满足 C 与 A,故 CAP 不适用。

CAP 的精确理解是“分区时 C 与 A 的取舍”,P 是条件而非选项。回答应纠正“三选二”误读,并说明分区未发生时的正常状态。

#
★★★

4. 因果一致性(Causal Consistency)如何借助向量时钟(Vector Clock)判定并发与偏序?它与线性一致性在协调开销上的本质差异是什么?

因果一致性如何借助向量时钟判定并发与偏序?它与线性一致性在协调开销上的本质差异是什么?

  • 因果一致性的语义
  • 向量时钟的偏序判定
  • 与线性一致性的协调开销差异

因果一致性(Causal Consistency)要求:有因果关系的操作按因果顺序被所有节点以相同顺序看到,无因果关系的并发操作可乱序。向量时钟(Vector Clock)为每个节点维护一个计数器向量,操作发生时更新本节点计数器,向量用“≤”偏序判定因果关系:若一个向量各分量 ≤ 另一向量,则前者因果先于后者;若两向量互不可比,则二者并发。其与线性一致性的本质差异在于协调开销:线性一致性需要一个全局实时排序点,节点间需同步/仲裁,协调开销高;因果一致性只维护因果偏序,允许无因果关系的并发操作自由排序,不需要全局协调,延迟更低、可用性更好。

因果一致性用向量时钟判定偏序,是其低协调开销的关键。回答核心是“因果需排序、并发可乱序”,对比线性一致性需全局排序的代价。

#
★★★

5. PACELC 中的 PA/EL 与 PC/EC 分别描述什么场景?相比 CAP,它补充了系统正常运行(无分区)时延迟与一致性的何种权衡?

PACELC 中的 PA/EL 与 PC/EC 分别描述什么场景?相比 CAP,它补充了无分区时延迟与一致性的权衡?

  • PACELC 的完整表述
  • PA/EL 与 PC/EC 场景
  • 无分区时的延迟与一致性权衡

PACELC 扩展了 CAP:在分区(P)时,选择可用性(A)还是一致性(C),即 PA 或 PC;在正常运行(E,Else)时,选择延迟(L)还是一致性(C),即 EL 或 EC。PA/EL 组合(如 Dynamo 风格)在分区时优先可用、正常时优先低延迟,牺牲强一致;PC/EC 组合(如 Spanner 风格)在分区时优先一致、正常时优先强一致,牺牲延迟。相比 CAP 只讨论分区时的取舍,PACELC 补充了系统无分区正常运行时的权衡:即使没有分区,强一致(同步复制、全局协调)也会增加延迟,因此在无分区时也需在“低延迟”与“强一致”间取舍。

PACELC 的价值是把 CAP 未覆盖的“正常运行期”也纳入取舍,理解“无分区也要在延迟与一致性间权衡”,是分布式系统设计更完整的视角。

#
★★★

6. Raft 的线性一致读优化,ReadIndex 与 Lease Read 的实现与适用条件

Raft 的线性一致读优化:ReadIndex 与 Lease Read 如何实现,适用条件是什么?

  • ReadIndex 的实现
  • Lease Read 的实现
  • 两者的适用条件与差异

Raft 的线性一致读需要保证读到的数据不旧于最新已提交。ReadIndex 优化:Leader 在处理读请求时,先与多数派确认自身的提交信息(ReadIndex),确认自己仍是 Leader 且没有更新的已提交日志,然后应用本地状态机读取该 ReadIndex 对应的数据,避免每次读都走一次完整日志同步。Lease Read 优化:Leader 通过选举超时/心跳租约,在租约期内相信自己是 Leader,无需与多数派交互即可直接本地读,前提是时钟同步且有明确租约边界,否则可能读到旧 Leader 的数据。适用条件:ReadIndex 通用性强、无需时钟同步;Lease Read 延迟更低但依赖时钟同步与租约安全,不适配时钟漂移大的环境。

ReadIndex 与 Lease Read 都是在“保证线性一致”前提下减少读延迟的优化,区别在于是否依赖时钟租约。回答核心是“ReadIndex 靠多数派确认、Lease Read 靠租约信任”。

#
★★

7. 会话一致性保证 Read-Your-Writes、Monotonic Reads、Monotonic Writes、Writes-Follow-Reads 分别如何用会话令牌或读时间戳实现?

会话一致性保证 Read-Your-Writes、Monotonic Reads、Monotonic Writes、Writes-Follow-Reads 分别如何用会话令牌或读时间戳实现?

  • 各会话一致性的语义
  • 会话令牌机制
  • 读时间戳实现

会话一致性是放宽到单会话的一致性保证。Read-Your-Writes(读自己写):用户写入后,后续读能看到自己的写,实现方式是记录写入的时间戳/版本,读时确保读到≥该时间戳的数据。Monotonic Reads(单调读):会话内多次读不读到旧值,按会话记录已读时间戳,读时跳过更旧数据。Monotonic Writes(单调写):会话内写入按提交顺序可见,用会话令牌记录写入顺序,保证后续写不覆盖已提交的写。Writes-Follow-Reads(写跟随读):写基于读到的数据,先读再写时保证读到的是最新,用读时间戳作为写的基础,避免写覆盖并发读后更新的数据。

会话一致性通过“会话令牌/时间戳”把一致性限定在单会话内,降低协调成本。回答核心是“记录位置、按时间戳过滤”,区分四类语义。

#
★★

8. 最终一致性依赖哪些收敛机制(LWW 最后写入获胜、CRDT 无冲突复制数据类型)?CRDT 需要满足什么代数性质(join-半格、单调合并)?

最终一致性依赖哪些收敛机制?CRDT 需要满足什么代数性质?

  • LWW 最终写入获胜
  • CRDT 无冲突复制
  • join-半格与单调合并性质

最终一致性的收敛机制用于让各副本最终一致:LWW(Last-Writer-Wins,最后写入获胜)为每次写入附带时间戳,副本冲突时保留时间戳最新的值,简单但可能丢失并发更新;CRDT(无冲突复制数据类型)通过设计合并操作,使不同副本的并发更新能无冲突地合并为一致结果,无需回滚。CRDT 需满足代数性质:作为 join-半格(join-semilattice),所有状态合并是幂等、可交换、结合的(对任意两个状态合并结果相同),即合并操作单调(单调合并),保证副本无论以何种顺序合并都收敛到同一状态。

收敛机制解决“最终一致如何收”。LWW 简单但丢更新,CRDT 用可交换单调合并保证无冲突收敛,理解 join-半格与单调性是其数学基础。

#
★★

9. 为什么 Cassandra 类系统即使配置 R+W>N 仍可能读到旧值(节点宕机恢复、读修复未完成)?Read Repair 与反熵(Anti-Entropy)如何协作收敛?

为什么 Cassandra 类系统即使配置 R+W>N 仍可能读到旧值?Read Repair 与反熵如何协作收敛?

  • R+W>N 读旧值的场景
  • Read Repair 机制
  • 反熵(Anti-Entropy)与收敛

即使 R+W>N,Cassandra 类系统仍可能读到旧值,因为:节点宕机恢复后,其副本可能落后,读修复(Read Repair)尚未完成时,若旧副本被读到且未触发修复,会返回旧值;写未及时传播到所有副本、或节点重启后数据依赖其他机制收敛。Read Repair 在读路径发现副本不一致时,把最新值同步给过期副本,修复读路径上的不一致。反熵(Anti-Entropy)是后台定期比对副本数据(Merkle 树)并同步差异,修复未被读到的静默不一致。二者协作:Read Repair 修复读到的热点数据,Anti-Entropy 修复全部副本,最终收敛到一致。

R+W>N 是概率保证而非绝对,读旧值源于副本修复滞后。Read Repair 与 Anti-Entropy 分别从读路径与后台路径收敛,理解其协作是处理 Cassandra 一致性的关键。

#
★★

10. Raft 的日志匹配性质(Log Matching Property)包含哪两条不变式?Follower 如何通过 AppendEntries 的 prevLogIndex/prevLogTerm 一致性检查发现并修复日志冲突?

Raft 的日志匹配性质包含哪两条不变式?Follower 如何通过 prevLogIndex/prevLogTerm 检查发现并修复日志冲突?

  • 日志匹配性质的两条不变式
  • prevLogIndex/prevLogTerm 检查
  • 日志冲突的修复

Raft 的日志匹配性质(Log Matching Property)包含两条不变式:一是“不同节点日志中相同 index 的条目若 term 相同,则条目内容相同”;二是“若一条日志在各节点的某 index 处相同,则此前所有 index 的日志也相同(由一致的 prevLogIndex/prevLogTerm 检查保证)”。Follower 收到 AppendEntries 时,检查 prevLogIndex 与 prevLogTerm 是否与本地日志一致,若不一致则拒绝该请求,Leader 收到拒绝后回退 nextIndex 重试,直到找到双方一致的最长前缀,再追加冲突日志,从而修复冲突。

日志匹配性质是 Raft 日志一致性的基础,prevLogIndex/prevLogTerm 检查是冲突检测与修复的关键。核心是“先前缀匹配、再追加补齐”。

#
★★

11. 为什么 Raft Leader 不能仅靠计数直接提交前任 term 的日志(Figure 8 已提交日志被覆盖问题),而必须通过提交本 term 日志间接提交旧日志?

为什么 Raft Leader 不能仅靠计数直接提交前任 term 的日志,而必须通过提交本 term 日志间接提交旧日志?

  • Figure 8 已提交日志被覆盖问题
  • 计数提交的缺陷
  • 间接提交机制

Raft 的“Figure 8”问题指:Leader 若仅凭复制到多数派就提交前任 term 的日志,可能在新 Leader 选举后这些日志被覆盖,因为新 Leader 可能没有这些日志且多数派允许覆盖。原因是一个 term 的日志即使当前复制到多数派,也不能保证被未来 Leader 保留(新 Leader 可能缺少且可覆盖)。正确做法是:Leader 只能直接提交本 term 的日志,通过提交本 term 日志隐含提交了其之前的所有日志(因为日志连续且本 term 日志复制到多数派时,前任 term 日志也已在多数派),从而安全地间接提交旧日志。

Figure 8 揭示“复制到多数派≠已提交”的安全陷阱。核心是“只能自信提交本 term 日志,借其传播间接提交旧日志”。

#
★★

12. Raft 选举中 RequestVote 的授予规则(日志至少与自己一样新、每个 term 只投一票)如何保证当选者一定包含所有已提交日志?随机化选举超时如何避免选票分裂(Split Vote)?

Raft 选举中 RequestVote 的授予规则如何保证当选者包含所有已提交日志?随机化选举超时如何避免选票分裂?

  • RequestVote 授予规则
  • 保证已提交日志的机制
  • 随机化超时避免选票分裂

Raft 的 RequestVote 授予规则:节点只投票给日志至少与自己一样新(term 更大,或 term 相同但 index 更长)的候选者,且每个 term 只投一票。该规则保证当选者一定包含所有已提交日志:因为已提交日志已在多数派节点上,当选者通过多数派当选,其日志必不落后于多数派中的任一节点,而多数派节点都包含已提交日志,故当选者必包含。随机化选举超时使各节点的超时时间不同,避免同时发起选举导致选票分散(Split Vote)无法选出 Leader,增加快速收敛的概率。

选举规则用“日志新旧”约束保证当选者不丢已提交日志,随机化超时解决选票分裂。核心是“多数派+日志新旧+随机超时”。

#
★★

13. Raft 集群成员变更的 Joint Consensus(两阶段联合配置)与单节点变更(single-server change)各自如何避免出现两个不相交多数派?

Raft 集群成员变更的 Joint Consensus 与单节点变更如何避免出现两个不相交多数派?

  • 成员变更的挑战
  • Joint Consensus 两阶段
  • 单节点变更机制

成员变更若直接切换到新配置,可能因新旧配置的多数派不相交而产生两个各自提交的 Leader(脑裂)。Joint Consensus(联合共识)两阶段:先切换到新旧配置的联合配置(C_old,new),要求新旧两个多数派都提交才生效,再切到新配置 C_new,避免任一步出现两个不相交多数派。单节点变更(single-server change)利用一次只增删一个节点的性质,通过“新旧配置多数派必相交”的论证(相邻配置的多数派有交集),逐步调整成员,避免脑裂,实现更简单的变更。

成员变更的核心是“避免新旧配置多数派不相交导致的脑裂”。Joint Consensus 用两阶段、单节点变更用相邻交集性,回答应对比二者机制。

#
★★

14. Basic Paxos 的 Prepare/Promise 与 Accept/Accepted 两阶段各自承担什么职责?为什么提案编号(proposal number)必须全局单调递增且 Leader 需尊重已承诺的最大值?

Basic Paxos 的 Prepare/Promise 与 Accept/Accepted 两阶段各自承担什么职责?为什么提案编号必须全局单调递增?

  • Prepare/Promise 职责
  • Accept/Accepted 职责
  • 提案编号单调递增的意义

Basic Paxos 第一阶段 Prepare/Promise:Proposer 发送带提案编号 n 的 Prepare 请求,Acceptor 承诺不再接受编号小于 n 的提案,并返回已接受的最大编号提案(若有),作用是确定多数派并收集已接受值,为后续取值做准备。第二阶段 Accept/Accepted:Proposer 依据收到的承诺值(若有已接受值则沿用,否则用自己值)发送 Accept 请求,Acceptor 接受后返回 Accepted,多数派接受后提案达成。提案编号必须全局单调递增,保证同一编号只对应一个提案,避免不同提案用相同编号冲突;Leader 需尊重已承诺的最大值,即当多数派已承诺某编号时,新提案编号必须大于它,且须沿用已接受的最大值,以达成一致,防止编号回退导致乱序与分歧。

Paxos 两阶段用“编号+承诺”解决协商一致。Prepare 定位承诺、Accept 取值,编号单调递增与尊重承诺值保证安全性与收敛性。

#
★★

15. Multi-Paxos 如何通过稳定 Leader 省略多数提案的 Prepare 阶段?它与 Raft 强 Leader、连续日志模型的本质差异在哪里?

Multi-Paxos 如何通过稳定 Leader 省略 Prepare 阶段?它与 Raft 的本质差异在哪里?

  • Multi-Paxos 的 Leader 机制
  • 省略 Prepare 的条件
  • 与 Raft 的差异

Multi-Paxos 通过稳定 Leader 在多数节点上完成一次 Prepare(确立 Leader 的编号与承诺)后,后续连续提案复用该 Leader 权威,省略每次的 Prepare 阶段,直接执行 Accept,降低每提案的通信开销。它与 Raft 的本质差异:Raft 采用强 Leader、连续日志(log replication)模型,Leader 通过 AppendEntries 复制有序日志,日志连续、追加顺序推进,提交与选举规则更严格;Multi-Paxos 允许对多个不同的日志槽(slot)并行协商,日志编号可离散、可乱序,含义更灵活但实现复杂、难以保证日志连续,多数实现仍需通过 Leader 约定顺序。

Multi-Paxos 用稳定 Leader 省 Prepare,Raft 用强 Leader 连续日志简化。回答核心是“复用 Leader 权威 vs 连续日志顺序”,体现 Raft 的工程化取舍。

#
★★

16. Raft 日志压缩(Snapshot + InstallSnapshot RPC)如何防止日志无限增长?落后太多的 Follower 如何通过快照追赶,期间 Leader 需保留什么?

Raft 日志压缩(Snapshot + InstallSnapshot RPC)如何防止日志无限增长?落后 Follower 如何追赶?

  • 快照压缩机制
  • InstallSnapshot RPC
  • Follower 追赶与 Leader 保留

Raft 日志无限增长会耗尽存储并拖慢重放,通过快照(Snapshot)压缩:当状态机状态超过阈值时,把某一点的状态机状态与元数据打包成快照,丢弃该点之前的日志。落后太多的 Follower 由于缺失大量日志,无法通过增量 AppendEntries 追赶,Leader 通过 InstallSnapshot RPC 直接把快照发给 Follower,Follower 应用快照后跳过多余日志。期间 Leader 需保留:当前快照的索引与最后一个已包含日志条目,以及从快照之后的最新日志,以便新 Follower 追到快照点后继续增量同步;同时应保留与快照兼容的日志,避免快照后立即追加导致断层。

快照解决日志无限增长,InstallSnapshot 解决严重落后 Follower 的追赶。核心是“快照点+保留后续日志”,理解 Leader 的保留策略。

#
★★

17. Learner/Non-voting 成员在 Raft 中不参与投票计数,它在新节点加入预热、只读副本与跨地域容灾场景中有哪些典型用法?

Learner/Non-voting 成员不参与投票计数,它有哪典型用法?

  • Learner 的角色
  • 新节点加入预热
  • 只读副本与跨地域容灾

Learner(Non-voting 成员)是 Raft 中不参与投票计数、只接收复制日志的成员。典型用法:一是新节点加入预热——新节点先以 Learner 身份同步日志追平数据,避免立即参与投票影响可用性,追平后再转为正式成员;二是只读副本——Learner 提供读扩展,承担只读流量而不影响写入决策,降低 Leader 读负载;三是跨地域容灾——在异地部署 Learner 接收数据,用于灾难备份与异地就绪,虽不参与投票仍可快速晋升,实现低成本异地容灾。

Learner 的关键是“不投票、只复制”,用身份换取加入安全与读扩展。回答核心是预热、只读、容灾三类典型用法。

#
★★

18. ZooKeeper 的 ZAB 协议与 Raft 在 epoch(zxid 高位)、Leader 选举时的日志同步(DIFF/TRUNC/SNAP)与事务提交上有何异同?

ZooKeeper 的 ZAB 协议与 Raft 在 epoch、日志同步与事务提交上有何异同?

  • epoch 与 zxid 高位
  • DIFF/TRUNC/SNAP 日志同步
  • 事务提交对比

ZAB 与 Raft 都用 epoch/term 机制标识 Leader 任期:ZAB 的 zxid 高位是 epoch,低位是序号,Raft 的 term 类似。两者在 Leader 选举后都需同步日志:ZAB 用 DIFF(增量同步差异)、TRUNC(截断多余日志)、SNAP(快照同步)三种方式,与已提交记录对齐,Raft 同样通过日志匹配前移截断冲突日志、用快照追赶落后 Follower。事务提交上,两者都要求 Leader 将事务复制到多数派并提交,但 ZAB 强调以 Leader 的 FIFO 顺序提交事务、保证事务顺序,Raft 同样按日志顺序提交,二者在“多数派提交+顺序保证”上一致,主要差异在实现细节(如 ZAB 的恢复协议、Raft 的日志匹配/选举规则)。

ZAB 与 Raft 都基于“epoch+多数派提交+顺序同步”,实现细节不同。回答核心是 epoch/zxid 对应、DIFF/TRUNC/SNAP 与 Raft 日志匹配的对应关系。

#
★★

19. Raft 的 Commit Index 与 Applied Index 有什么区别?为什么状态机必须严格按日志顺序 apply,乱序 apply 会破坏什么?

Raft 的 Commit Index 与 Applied Index 有什么区别?为什么状态机必须严格按日志顺序 apply?

  • Commit Index 与 Applied Index 的区别
  • 状态机按序 apply 的原因
  • 乱序破坏的性质

Raft 中 Commit Index 是已提交日志的最大序号(已复制到多数派、可安全应用),Applied Index 是状态机已应用的最大日志序号。二者之差代表已提交但尚未应用的日志。状态机必须严格按日志顺序 apply,因为:Raft 日志是确定性的状态机命令序列,必须按顺序执行才能得到一致结果;乱序 apply 会破坏状态机的确定性(如先应用后一条命令会基于错误状态)、破坏日志匹配性质与可重复性,导致不同副本状态不一,丧失一致性保证。

Commit Index 是“可应用”,Applied Index 是“已应用”。按序 apply 是状态机确定性一致性的基础,乱序会破坏副本一致性。

#
★★

20. 为什么说 2PC 是阻塞协议(参与者投 Yes 后等待协调者裁决期间持锁阻塞)?Presumed Abort / Presumed Commit 如何通过默认推断减少日志与恢复开销?

为什么说 2PC 是阻塞协议?Presumed Abort / Presumed Commit 如何减少日志与恢复开销?

  • 2PC 的阻塞性
  • 参与者持锁阻塞
  • Presumed Abort/Commit 优化

2PC 是阻塞协议:参与者投票 Yes 后,必须等待协调者的最终裁决,期间事务持有的锁不能释放,若协调者故障,参与者只能阻塞等待,业务被挂起。阻塞源于参与者无法自行决定事务结果。Presumed Abort:协调者默认按 Abort 推断,若恢复时未找到 Commit 记录则判定为 Abort,可省略部分日志与恢复步骤;Presumed Commit:默认按 Commit 推断,减少 Commit 日志的同步开销。二者通过“默认推断”减少写日志与恢复时的查询,降低协调者崩溃恢复的实现与开销,但 Presumed Commit 需谨慎以防误判。

2PC 的阻塞是参与者在裁决前持锁等待,Presumed Abort/Commit 用默认推断简化日志与恢复。回答核心是“阻塞源于等待裁决、默认推断降低开销”。

#
★★

21. Google Percolator 的数据模型包含哪几列(Data、Lock、Write)?事务提交如何通过 Prewrite(上锁并检测冲突)+ Commit primary key 两轮完成,secondary 如何异步提交?

Google Percolator 的数据模型包含哪几列?事务提交如何通过 Prewrite + Commit primary key 完成?

  • Percolator 的列模型
  • Prewrite 上锁与冲突检测
  • Commit primary 与 secondary 异步提交

Percolator 的数据模型在 Bigtable 每行包含三列:Data(实际数据值)、Lock(事务锁)、Write(提交的版本记录,指向 Data)。事务提交两轮:第一轮 Prewrite,事务为每个写入的 key 检查冲突(若有锁或已更新的 Write 则失败),在 Data 列写入值并加锁;第二轮 Commit,先提交 primary key——在 Write 列写入提交记录并清除 primary 锁,表示事务已提交;随后 secondary 异步提交,后台逐个清除 secondary 锁并写入提交记录,无需客户端同步等待。通过 primary key 的提交决定事务成败,secondary 异步收敛。

Percolator 用 Data/Lock/Write 三列 + primary key 裁决实现分布式事务,Prewrite 检测冲突、Commit primary 定成败、secondary 异步提交。

#
★★

22. Percolator 如何处理客户端崩溃后的残留锁(锁带 TTL,遇到过期锁写 Rollback 记录)?Prewrite 阶段如何通过检查 Write 列检测写写冲突?

Percolator 如何处理客户端崩溃后的残留锁?Prewrite 阶段如何检测写写冲突?

  • 残留锁的 TTL 处理
  • Rollback 记录
  • Prewrite 的 Write 列冲突检测

Percolator 的锁带 TTL,客户端崩溃后锁会残留。遇到过期锁时,其他事务会认为持有者已崩溃,清除该锁并写入 Rollback 记录(在 Write 列标记该事务回滚),使后续事务能继续处理该 key。Prewrite 阶段检测写写冲突:对每个 key,检查其 Write 列是否有更新版本,若检测到该 key 已被其它事务提交或存在更晚的提交,则发生写写冲突,Prewrite 失败,事务回滚重试。通过检查 Write 列的最新提交记录,避免两个事务同时写同一 key 造成覆盖。

残留锁靠 TTL + Rollback 清理,写写冲突靠 Prewrite 检查 Write 列。回答核心是“过期锁清理与提交版本检测”。

#
★★

23. Google Spanner 的 TrueTime API 返回一个时间不确定区间 [earliest, latest],Commit Wait 如何据此保证外部一致性(External Consistency)?为什么依赖原子钟/GPS 时钟?

Google Spanner 的 TrueTime API 如何用 Commit Wait 保证外部一致性?为何依赖原子钟/GPS?

  • TrueTime 的时间区间
  • Commit Wait 机制
  • 外部一致性的保证

Google Spanner 的 TrueTime API 返回真实时间的不确定区间 [earliest, latest],表示真实时间落在此区间内。为保证外部一致性(External Consistency,即事务提交顺序与真实时间顺序一致),Spanner 在事务提交前执行 Commit Wait:等待直到真实时间超过 latest(即提交时间戳 assigned 之后,等待不确定区间结束),确保分配的时间戳晚于所有已提交事务,从而保证事务提交顺序与真实时间一致。依赖原子钟/GPS 时钟是因为它们提供高精度、低不确定性的时间同步,使不确定区间 ε 很小,从而减小 Commit Wait 的等待延迟,提升事务吞吐。

Commit Wait 通过等待真实时间越过 latest 来保证时间戳顺序与真实时间一致,是外部一致性的关键。依赖原子钟/GPS 是为了缩小不确定区间、降低等待成本。

#
★★

24. Percolator 的快照读如何在 read_ts 上避免读到未提交数据(遇到锁则触发清理并回退重试)?只读事务为何可以省去加锁与提交阶段?

Percolator 的快照读如何在 read_ts 上避免读到未提交数据?只读事务为何可省去加锁与提交阶段?

  • 快照读与 read_ts
  • 遇到锁的处理
  • 只读事务的优化

Percolator 的快照读在指定的时间戳 read_ts 上读取,读到的是 read_ts 或更早已提交的数据。读取时若遇到该 key 上的锁(表明有未提交事务),则不能读取该值,需触发清理:若锁过期则清除并写 Rollback,回退重试;若锁未过期则等待持有者提交后重试,从而避免读到未提交数据。只读事务可省去加锁与提交阶段,因为只读不需要修改数据、不产生写冲突,只需在单一时间戳上读取一致快照,无需 Prewrite 加锁、无需 Commit primary,省去参与提交协调,显著降低延迟与开销。

快照读用 read_ts 读已提交数据,遇锁清理重试避免脏读;只读事务无写操作故无需加锁与提交。回答核心是“读快照、避未提交、只读免协调”。

#
★★

25. 确定性事务(Calvin 模型)通过定序器(Sequencer)预先排序事务来避免 2PC,它对依赖型事务(dependent transaction)有什么限制?

确定性事务(Calvin 模型)如何通过定序器避免 2PC?对依赖型事务有什么限制?

  • Calvin 的定序器机制
  • 确定性执行避免 2PC
  • 依赖型事务的限制

Calvin 模型通过定序器(Sequencer)预先对事务进行全局排序,把事务的依赖关系在提交前确定,各节点按该确定顺序执行事务,从而避免 2PC 的协调与阻塞。因为事务顺序预先确定,执行结果确定,无需在提交时协商。其限制在于依赖型事务(dependent transaction):事务的写入集合或依赖需要运行时读取结果才能确定(如 SQL 中依赖子查询结果的写集),Calvin 无法预先确定其依赖关系与顺序,导致这类事务无法直接受益于预排序,需特殊处理(如把事务拆分为可预定的阶段,或要求业务提供读集/写集),不能完全像 2PC 那样灵活处理任意运行时依赖。

Calvin 用预排序换取无 2PC 的确定性,但对依赖型事务(运行时写集未知)受限。回答核心是“确定顺序 vs 运行时依赖的矛盾”。

#
★★

26. 2PC 结合 WAL(MySQL XA、PostgreSQL PREPARE TRANSACTION)如何在参与者崩溃后恢复 in-doubt 事务?悬挂的 prepared 事务长期不决会带来哪些危害?

2PC 结合 WAL 如何在参与者崩溃后恢复 in-doubt 事务?悬挂的 prepared 事务有何危害?

  • WAL 与 in-doubt 恢复
  • 参与者崩溃恢复
  • 悬挂 prepared 事务的危害

2PC 结合 WAL(如 MySQL XA、PostgreSQL PREPARE TRANSACTION)在参与者崩溃后,通过 WAL 中的 prepared 记录恢复 in-doubt 事务:参与者重启后,从 WAL 恢复处于 prepared 状态但未决的事务,协调者据此决策(提交或回滚),参与者按协调者裁决完成。悬挂的 prepared 事务(长期未决)会带来危害:事务持有的锁与资源长期不释放,阻塞其他事务,导致连接与锁耗尽、性能下降;且积累的 prepared 事务会占用日志与存储,极端情况下拖垮系统。因此需监控并清理长期未决的 prepared 事务。

WAL 让参与者崩溃后能恢复并协同决策,悬挂 prepared 事务造成锁与资源长期占用。回答核心是“WAL 恢复 + 悬挂危害”。

#
★★

27. 相比传统 2PC,Percolator 式去中心化两阶段提交(无独立协调者、由 primary key 驱动裁决)在延迟与协调者可用性上有哪些改进与代价?

相比传统 2PC,Percolator 式去中心化两阶段提交在延迟与协调者可用性上有哪些改进与代价?

  • 去中心化提交的机制
  • 延迟与可用性改进
  • 代价与权衡

Percolator 式去中心化两阶段提交没有独立协调者,由 primary key 驱动裁决:事务的 primary key 所在节点充当裁决者,次级 key 异步提交。改进:无独立协调者单点,协调者可用性不再成为瓶颈,避免协调者故障导致整事务阻塞;primary key 裁决可并行推进,减少中心化协调的通信往返,降低延迟。代价:事务提交依赖 primary key 的进度,primary key 故障影响裁决;需处理 secondary 异步提交的窗口内数据可见性;客户端需参与清理与重试逻辑,且锁的 TTL 与清理机制增加复杂度,业务侧需承担更多协调责任。

去中心化提交用 primary key 替代协调者,换可用性与延迟,代价是裁决集中于 primary 与异步一致性窗口。回答核心是“协调者去中心化、primary 裁决、异步窗口”。

#
★★

28. 2PC 中协调者与参与者的 Prepare/Commit/Abort 日志记录分别有哪些?为什么协调者必须先持久化 Commit 决定再通知参与者?

2PC 中协调者与参与者的 Prepare/Commit/Abort 日志记录有哪些?为什么协调者必须先持久化 Commit 决定再通知参与者?

  • 协调者的日志记录
  • 参与者的日志记录
  • 先持久化 Commit 的原因

2PC 中协调者需记录:Prepare 请求日志、Commit/Abort 决定日志(写日志再到发通知);参与者需记录:本地 prepared 日志(收到 Prepare 后)、以及按协调者决定的 Commit/Abort 日志。协调者必须先持久化 Commit 决定再通知参与者,是因为:若协调者先通知参与者再写日志,协调者崩溃后无法恢复自己的 Commit 决定,无法向参与者传达统一裁决,会导致参与者处于不确定状态(部分已提交、部分未提交),破坏原子性。先持久化 Commit 决定,即使协调者崩溃,重启后也能从日志恢复决定并统一通知,保证所有参与者一致提交或回滚。

协调者先持久化 Commit 决定是保证崩溃后能统一裁决的原子性前提。回答核心是“先落盘再通知,崩溃可恢复裁决”。

#
★★

29. 对比 TiDB(TiKV/RocksDB)、CockroachDB(Pebble)、OceanBase(自研 LSM-Tree)的存储引擎,Compaction 策略、写放大与空间放大有何差异?

对比 TiDB、CockroachDB、OceanBase 的存储引擎,Compaction 策略、写放大与空间放大有何差异?

  • 各引擎的存储技术
  • Compaction 策略差异
  • 写放大与空间放大

TiDB 的 TiKV 基于 RocksDB(LSM-Tree),采用 LevelDB 风格的分层 Compaction(level 分层、Size 触发),写放大较高但实现成熟;CockroachDB 的 Pebble 是自研 LSM-Tree,优化了 Compaction 调度与内存,写放大低于 RocksDB,且针对其 Raft 场景做了优化;OceanBase 自研 LSM-Tree,采用内存 MemTable + 转储 + 合并(major merge)机制,通过定期的转储与合并控制写放大,并利用基线数据与增量数据分离降低空间放大。总体差异:RocksDB 分层 compaction 写放大较高、空间放大可控;Pebble 优化写放大与内存;OceanBase 通过转储/合并架构在写放大与空间放大间平衡,通常写放大更低、空间利用率高。

三引擎都用 LSM 但 Compaction 策略不同,写放大与空间放大是核心权衡。回答应对比分层/转储/合并机制对读写放大的影响。

#
★★

30. Quorum 读与线性一致读的代价(读放大 vs 延迟)

Quorum 读与线性一致读的代价有哪些(读放大 vs 延迟)?

  • Quorum 读的读放大
  • 线性一致读的延迟
  • 两者代价对比

Quorum 读(如 R+W>N 的 quorum 读)需要读取 R 个副本并比较版本,带来读放大:读请求被放大到多个副本,增加网络与 IO 开销,且需处理副本间版本比对。线性一致读(如 Raft 的 ReadIndex/Lease Read)保证读到最新且实时一致,但需要与多数派确认 Leader 身份或提交信息,增加协调延迟与通信开销;Lease Read 可降低延迟但依赖时钟。二者代价权衡:Quorum 读读放大高但无需 Leader 协调,适合无需强一致顺序的场景;线性一致读延迟高但保证强一致,适合需要实时一致读取的场景。

Quorum 读的代价是读放大,线性一致读的代价是协调延迟。回答核心是“读放大 vs 延迟”的权衡。

#

31. 三者分别如何提供 SQL 兼容性(TiDB 兼容 MySQL 协议、CRDB 兼容 PostgreSQL 协议、OceanBase MySQL/Oracle 双模式)?协议兼容与语法/语义兼容的典型坑有哪些?

TiDB、CockroachDB、OceanBase 分别如何提供 SQL 兼容性?协议兼容与语法/语义兼容的典型坑有哪些?

  • 三者的协议兼容
  • 语法兼容
  • 语义兼容的典型坑

TiDB 兼容 MySQL 协议,CockroachDB 兼容 PostgreSQL 协议,OceanBase 提供 MySQL 与 Oracle 双模式。协议兼容指兼容客户端连接协议(如握手、认证、二进制协议),使现有驱动可直接连接;语法兼容指支持相应 SQL 语法(函数、类型、索引语法);语义兼容指行为一致(如隔离级别、事务语义、函数结果、错误码)。典型坑:协议兼容但语义不兼容(如某些函数返回值、隐式转换、NULL 处理、隔离级别差异);语法兼容但隐藏行为不同(如自增、锁行为、日期格式);错误码与性能特性不一致,导致应用迁移时行为异常。

SQL 兼容分协议、语法、语义三层,坑多在“协议通但语义/行为不同”。回答核心是分层兼容与迁移时的行为差异。

#

32. 三者事务模型有何不同(TiDB 基于 Percolator 的乐观/悲观事务、CRDB 可串行化隔离、OceanBase Paxos+两阶段提交)?默认隔离级别与分布式死锁处理有何差异?

TiDB、CockroachDB、OceanBase 的事务模型有何不同?默认隔离级别与分布式死锁处理有何差异?

  • 三者的分布式事务模型
  • 默认隔离级别
  • 分布式死锁处理

TiDB 基于 Percolator 模型的乐观/悲观事务,默认悲观事务,支持可重复读(Snapshot Isolation)与线性一致;CockroachDB 实现可串行化隔离(Serializable),通过串行化快照隔离(SSI)检测冲突,保证串行化;OceanBase 采用 Paxos + 两阶段提交,支持多版本读,默认读已提交/可重复读,兼容 MySQL/Oracle 事务语义。分布式死锁处理:TiDB 用死锁检测器检测并回滚;CockroachDB 通过事务优先级与冲突检测(重试低优先级事务)规避死锁;OceanBase 通过锁等待图与超时检测处理死锁。隔离级别上,CRDB 默认可串行化最强,TiDB/OB 默认可重复读/读已提交。

三者的分布式事务都基于两阶段/Percolator 但隔离级别与死锁处理不同。回答核心是“事务模型、隔离级别、死锁处理”的对比。

#

33. 三者分别如何实现 HTAP(TiDB TiFlash 列存副本、CRDB 无专用列存、OceanBase 行列混合与并行执行)?列存副本与行存的数据新鲜度如何保证?

TiDB、CockroachDB、OceanBase 分别如何实现 HTAP?列存副本与行存的数据新鲜度如何保证?

  • 三者的 HTAP 实现
  • 列存副本同步
  • 数据新鲜度保证

TiDB 通过 TiFlash 列存副本(Raft Learner 异步同步)实现 HTAP,分析查询走列存;CockroachDB 无专用列存引擎,HTAP 依赖行存的列式算法与向量化,未提供独立列存;OceanBase 采用行列混合存储与并行执行,在同一存储上支持行存与列存访问。列存副本与行存的数据新鲜度:TiDB 依赖 Raft 日志复制到 TiFlash,列存基本实时但可能有轻微滞后;OceanBase 行列混合在同源存储上并行维护,新鲜度较高;CockroachDB 因无独立列存,天然无列存滞后问题。总体来说,新鲜度取决于列存同步机制,TiDB 异步、OB 同源、CRDB 无独立列存。

三者的 HTAP 路径不同:TiDB 独立列存副本、CRDB 无列存、OB 行列混合。回答核心是“列存实现 + 新鲜度同步机制”。

#

34. 三者的调度组件(PD、Allocator/Replication、RootService)如何实现分片(Region/Range/Tablet)迁移、副本均衡与热点打散?

TiDB、CockroachDB、OceanBase 的调度组件如何实现分片迁移、副本均衡与热点打散?

  • 各调度组件的职责
  • 分片迁移与副本均衡
  • 热点打散

TiDB 的 PD(Placement Driver)负责分片(Region)的迁移、副本均衡与调度,依据 Region 大小与负载移动 Raft 副本、分裂热点 Region;CockroachDB 的 Allocator/Replication(通过 Gossip 各节点信息)实现 Range 的复制均衡与再平衡,按存储与负载调整副本位置;OceanBase 的 RootService 负责 Tablet 的迁移、副本均衡与负载调度,依据分区数据量与访问负载调整。三者都通过“监控负载与存储 → 决策迁移 → 平滑迁移”实现分片均衡,并针对热点(高访问 Region/Range/Tablet)做分裂与分散,把热点分散到多节点。

三者的调度组件都围绕“分片迁移、副本均衡、热点打散”实现,只是命名与实现不同(PD/Allocator/RootService)。回答核心是调度职责与热点处理。

#

35. 节点或可用区故障时,三者在 RPO(Raft/Paxos 多数派自动选主)与切换时间上有何差异?跨地域部署的延迟代价如何?

节点或可用区故障时,三者在 RPO 与切换时间上有何差异?跨地域部署的延迟代价如何?

  • 三者的 RPO 与自动选主
  • 切换时间差异
  • 跨地域延迟代价

三者在节点/可用区故障时都通过多数派共识(Raft/Paxos)自动选主,RPO 通常为 0(多数派已同步,无数据丢失),切换时间取决于故障检测与选主速度,通常为秒级。差异:TiDB 与 CockroachDB 用 Raft,OceanBase 用 Paxos,多数派同步保证 RPO=0;切换时间受故障检测超时、选举超时影响,三者在毫秒到秒级。跨地域部署的延迟代价:多数派同步需要跨地域往返,写入延迟随地域间距离(RTT)增加,延迟显著上升;为降低延迟,可配置多数派在地域内或使用更近的副本,但会牺牲跨地域容灾能力。

三者都靠多数派共识实现 RPO=0 与自动选主,跨地域部署使写延迟随 RTT 上升。回答核心是“共识选主 + 跨地域延迟代价”。

#

36. 三者在数据迁移(TiDB Lightning/DM、CRDB IMPORT/MOVABLE、OceanBase OMS)与 CDC 生态工具上的成熟度差异?

TiDB、CockroachDB、OceanBase 在数据迁移与 CDC 生态工具上的成熟度差异?

  • 三者的迁移工具
  • CDC 生态
  • 成熟度对比

TiDB 提供 TiDB Lightning(批量导入)、DM(Data Migration,从 MySQL 迁移)与完善的 CDC 工具(TiCDC),迁移与同步生态成熟,能平滑从 MySQL 迁移;CockroachDB 提供 IMPORT、MOVABLE(在线迁移)与 CDC 能力,支持从主流数据库迁移,但生态相对较小;OceanBase 提供 OMS(OceanBase Migration Service),支持从 MySQL/Oracle 等迁移,并具备配套 CDC 能力。成熟度上,TiDB 因 MySQL 生态对齐与 MySQL 迁移场景成熟,工具链更完善;OceanBase 面向 Oracle/MySQL 迁移提供 OMS,成熟度较高;CockroachDB 迁移工具功能完整但生态规模较小。

三者的迁移与 CDC 工具各具特色,TiDB 的 MySQL 生态、OB 的 OMS、CRDB 的 IMPORT/MOVABLE 是代表。回答核心是“工具与生态成熟度对比”。

#

37. 为什么共识组通常部署奇数个节点?3 节点与 4 节点集群在可容忍故障数与可用性上为何没有差别却多花成本?

为什么共识组通常部署奇数个节点?3 节点与 4 节点集群在可容忍故障数与可用性上为何没有差别却多花成本?

  • 奇数节点与多数派
  • 3 与 4 节点的容错
  • 成本与可用性关系

共识组通常部署奇数个节点,因为共识需要多数派(超过一半)才能提交与选主,奇数节点能最大化容忍故障数:N 节点容忍 (N-1)/2 个故障(向下取整)。3 节点与 4 节点集群的可容忍故障数相同(都只能容忍 1 个故障,因为都需要多数派:3 节点需 2 个,4 节点需 3 个),故可用性无差别,但 4 节点多花一份节点成本,收益却不变。因此实际部署多选奇数(3、5、7)以在容错与成本间取得最优,避免偶数节点浪费。

共识需多数派,奇数节点在给定容错下成本最优。3 与 4 都只能容 1 故障,故 4 节点纯属浪费。回答核心是“多数派与容错成本”。

#

38. 混合逻辑时钟(HLC)在分布式数据库中的应用

混合逻辑时钟(HLC)在分布式数据库中有哪些应用?

  • HLC 的原理
  • HLC 相对物理/逻辑时钟的优势
  • 分布式数据库中的应用

混合逻辑时钟(HLC,Hybrid Logical Clock)结合物理时钟与逻辑时钟:以物理时间为基准,通过逻辑计数器处理同时刻的并发事件,既保留物理时钟的近似实时性,又保证逻辑时钟的因果排序能力。在分布式数据库中的应用:用于事务时间戳与快照隔离,为事务分配单调递增、可比较的时间戳,保证因果一致的读与写;用于跨节点排序与一致性,避免仅用物理时钟的时钟偏差导致的乱序,也避免纯逻辑时钟缺乏实时感的问题;用于 MVCC 版本管理、快照读与跨地域排序,以较低协调开销实现因果一致的排序。

HLC 融合物理时钟的实时性与逻辑时钟的因果性,是分布式事务时间戳与因果排序的实用方案。回答核心是“物理+逻辑结合、因果排序”。