想象你同时在上海、北京、广州开了三家连锁咖啡馆。每一位顾客都有一张全球通会员卡,无论在哪家分店消费,余额和积分必须实时同步——这就是 CockroachDB 想解决的难题。CockroachDB 是一家“打不死的小强”式分布式数据库:每一个分店(数据中心)都能接单(读写),店长们通过一套严格的对讲机纪律(Raft 共识)同步账本,任何一笔账目必须在绝大多数分店确认后才算成功。同时,当两位顾客争抢最后一杯限定咖啡时,需要一套公平裁决规则(Serializable 隔离)决定谁拿到货,杜绝“互相等待”的死结。这篇指南将带你走进 CockroachDB 的多活世界,从地理复制、Raft 共识、可串行化隔离、事务处理到跨区域延迟优化,最后看看如何在真实世界部署这种强一致全球集群。

岗位关联

掌握 CockroachDB 的多活架构与 Serializable 隔离,能让你设计出全球部署、无单点、强一致的分布式数据库系统,这是分布式数据库工程师的核心竞争力。

目录
  1. 可视化演示
  2. 多活架构
  3. 地理复制
  4. Raft共识
  5. 可串行化隔离
  6. 事务处理
  7. 延迟优化
  8. 部署实践
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示CockroachDB的Raft共识副本一致性、Serializable隔离级别事务排序及跨区域Leaseholder就近延迟优化,交互切换理解分布式强一致性。

本节配有交互动效(进入视口后自动加载,约 3KB)。

Raft共识与副本一致性 3副本分散到不同区域 多数派确认后提交日志 2/3 3副本需2个确认达成Quorum

多活架构

假设你的咖啡馆扩张到了 30 个城市。传统做法是“总部定菜谱,分店只能照做”:所有会员卡数据只存在总部机房,分店每天要把账本传回去。一旦总部停电,全世界的分店都无法验证会员余额。这就是典型的“主从单活”架构——只有一个写入口,主库挂了,业务就停摆。而多活(Multi-Active)的思路是:每家分店都是一家独立运作的“总部”,都能收银、充值、盘点库存。

CockroachDB 正是这种“处处皆总部”的设计。集群中每个节点都完全对等,任意节点都能接收读写请求,不存在“主库优先”的单点瓶颈。相较于主从架构中故障切换需要人工或脚本提升新主、产生秒级不可用窗口,多活架构中其他节点天然在线,请求自动绕开故障节点,切换过程对客户端几乎无感。

多活的收益可以用一个概率公式直观展示。假设单个区域的数据中心可用性为 a,部署了 n 个多活区域,则整体可用性为:

A=1−(1−a)nA = 1 - (1-a)^{n}

1. 区域可用性 a:a 表示单个数据中心在单位时间内正常运行的概率,比如 0.99(99%)。

a=正常时间正常时间+故障时间a = \dfrac{\text{正常时间}}{\text{正常时间} + \text{故障时间}}

2. 单区域不可用:某个区域发生故障的概率为 1 - a。

1−a1-a

3. 全部区域同时故障:假设区域故障互相独立,n 个区域全挂的概率是 (1 - a) 的 n 次方。

(1−a)n(1-a)^n

4. 至少一个区域存活:用 1 减去“全挂”的概率,就是多活集群的整体可用性。

A=1−(1−a)nA = 1 - (1-a)^n

回看咖啡馆:每家分店都独立运作,30 家分店同时关门的概率趋近于零。多活架构没有“总部”这一致命单点,这正是它能在全球范围持续营业的根本原因。

地理复制

你决定把会员数据印成五本《全球会员总账》,分别锁在上海、北京、广州、成都、深圳五个城市的分店保险柜里。顾客在任何一家分店充值,店员必须当场打电话向其他分店确认——通了至少三家,这笔充值才生效。这就是地理复制:数据副本分散在不同地理区域,相互备份并协同服务。

CockroachDB 中,一个 range(数据分片)默认保存多个副本(replica),并可通过配置把副本分布到不同的 region(区域)和 availability zone(可用区)。每个区域是独立的故障域,电力、网络、制冷都独立。跨区域分布让“一个机房着火烧掉账本”变成小概率事件:只要多数派副本存活,数据就完好无损。

副本数 R 决定了系统能容忍的故障数。由于写入需要多数派副本确认,容错上限为:

fmax⁡=⌊R−12⌋f_{\max} = \left\lfloor \dfrac{R-1}{2} \right\rfloor

1. 多数派写:R 个副本中超过一半(≥ ⌊R/2⌋+1)确认,写入才算成功。

需要确认数=⌊R2⌋+1\text{需要确认数} = \left\lfloor \dfrac{R}{2} \right\rfloor + 1

2. 可容忍故障数:只要故障副本数不超过“R 减去多数派阈值”,多数派就仍然存在。

R−(⌊R2⌋+1)=⌊R−12⌋R - \left(\left\lfloor \dfrac{R}{2} \right\rfloor + 1\right) = \left\lfloor \dfrac{R-1}{2} \right\rfloor

3. 五副本实例:R=5 时,需要 3 个副本确认,最多可容忍 2 个副本同时故障。

fmax⁡=⌊5−12⌋=2f_{\max} = \left\lfloor \dfrac{5-1}{2} \right\rfloor = 2

五本账本放在五个城市,烧掉任意两家分店,剩下三本账仍然多数一致。地理复制为“多活”提供了数据层面的地基——副本存得越分散,局部灾难对全局的影响就越小。

Raft共识

店长们定下一条规矩:任何跨店账目变更,必须由当周轮值主席(Leader)发起,至少三位店长在对讲机里表示“收到且同意”,才能写进各自账本。主席每隔几秒要喊一次“我还活着”(心跳),大家发现主席失联,就重新投票选新主席。这套店长议事规则,正是分布式系统里鼎鼎大名的 Raft 共识算法。

Raft 把复杂的共识问题拆成三个子问题:领导者选举(Leader Election)、日志复制(Log Replication)、安全性(Safety)。所有写请求先到达 Leader,Leader 把操作追加进自己的日志,然后广播给各副本;各副本追加日志后回复“已记录”;Leader 收到多数派确认后,才把这条日志标记为已提交(committed),并通知客户端成功。读请求由 Leader 或拥有最新租约的副本处理,保证读到的数据不落后。

多数派阈值是 Raft 的灵魂。假设集群有 n 个节点,需要超过半数的节点确认才能提交:

Q=⌊n2⌋+1Q = \left\lfloor \dfrac{n}{2} \right\rfloor + 1

1. 节点总数 n:参与 Raft 表决的副本节点数量。

nn

2. 多数派阈值 Q:大于 n/2 的最小整数,即至少需要 Q 个节点确认。

Q=⌊n2⌋+1Q = \left\lfloor \dfrac{n}{2} \right\rfloor + 1

3. 交集必然性:任意两组 Q 个节点,它们的交集非空,因为 2Q > n。

2Q=2⌊n2⌋+2>n2Q = 2\left\lfloor \dfrac{n}{2} \right\rfloor + 2 > n

对讲机规矩看起来很笨——每笔账都要喊一圈。但正是这条“少数服从多数”的规矩,让 30 家分店在主席失联、网络抖动时依然能选出新主席,继续营业。Raft 是多活集群的“神经系统”。

可串行化隔离

周末限定咖啡只剩最后一杯。顾客甲正扫码付款,顾客乙也同时发起购买。两人都可能在屏幕前认为自己“锁定了这杯咖啡”。为了避免超卖和误解,你规定:如果两位顾客的操作没形成“循环等待”,就按先完成付款的人得手;一旦发现“你等我取消、我等你取消”的僵局,强制中止后到的那一位,请 TA 稍后再试。这个裁决哲学,就是 Serializable(可串行化)隔离。

数据库隔离级别从低到高有:读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)、可串行化(Serializable)。最高级别的可串行化意味着:多个并发事务执行后的结果,等价于它们按某个顺序一个一个执行的结果。CockroachDB 默认实现了可串行化快照隔离(SSI,Serializable Snapshot Isolation)。SSI 不是悲观地给每一行加锁排队,而是让事务乐观地并发执行,在提交时检查“读写冲突图”是否存在环。

冲突图(Conflict Graph)的节点是各事务,边代表数据访问冲突(如 T1 写了 x,T2 又读了 x)。可串行化的充要条件是冲突图无环:

可串行化  ⟺  冲突图无有向环\text{可串行化} \iff \text{冲突图无有向环}

1. 冲突边:若事务 T1 写入数据 x,事务 T2 之后读写 x,则画一条从 T1 指向 T2 的有向边。

T1→T2(if T1 writes x before T2 accesses x)T_1 \to T_2 \quad (\text{if } T_1 \text{ writes } x \text{ before } T_2 \text{ accesses } x)

2. 环检测:在冲突图上搜索有向环。若无环,则这些事务可以按拓扑序串行执行。

∃ cycle  ⟺  不可串行化\exists\, \text{cycle} \iff \text{不可串行化}

3. 中止策略:发现环后,中止创建时间戳较晚(后到)的事务,让先到者正常提交。

abort(Tlater)\text{abort}(T_{\text{later}})

回到咖啡店:裁决员不需要让所有顾客排队买咖啡(那样太慢),只需要在结账瞬间检查是否有“互相卡住”的循环依赖。多数时候大家各买各的,相安无事;真有冲突时,礼貌地请后来者稍等再试。SSI 就这样在并发度与一致性之间取得了优雅的平衡。

事务处理

顾客点了一杯拿铁、一块芝士蛋糕,还办了张新会员卡。这三件事分属三家分店的库存和会员系统:咖啡豆在上海仓库、蛋糕在北京中央厨房、会员卡在广州总部。你的要求是“要么全部成功,要么全部失败”——绝不能出现“卡办了、蛋糕没出库”的尴尬。这就是事务的原子性(Atomicity)在分布式场景下的挑战。

CockroachDB 的事务可以跨任意多个 key、跨多个 range、跨多个区域。它通过“事务记录 + Raft 日志”实现原子提交:事务先在一个协调节点上写入事务记录(transaction record),各参与分片通过 Raft 完成本地写入;最终提交时,协调节点把事务记录标记为 COMMITTED,各分片异步完成清理。CockroachDB 还引入了“事务流水线”(write pipeline)和“并行提交”(parallel commits),把一次跨区域提交从多次串行网络往返压缩为并行发送,大幅降低延迟。

一次跨区域事务的总体延迟可以用简单模型表示:

L=2×RTTquorum+LprocessL = 2 \times \text{RTT}_{\text{quorum}} + L_{\text{process}}

1. 多数派往返 RTT:从协调节点发出请求到获得多数派副本确认,需要一次网络往返。

RTTquorum=max⁡多数派(往返时延)\text{RTT}_{\text{quorum}} = \max_{\text{多数派}} (\text{往返时延})

2. 两次往返:准备阶段一次往返,最终提交确认再一次往返。

Lnet=2×RTTquorumL_{\text{net}} = 2 \times \text{RTT}_{\text{quorum}}

3. 加入处理时间:加上本地 CPU、磁盘写入等处理开销,得到总延迟。

L=2×RTTquorum+LprocessL = 2 \times \text{RTT}_{\text{quorum}} + L_{\text{process}}

广岛的三家分店通过流水线机制并行协调,不必等一家完成再去下一家;一旦其中任何一家无法完成,整笔订单回滚,顾客的钱原路退回。强一致的全局事务,让顾客永远不用担心“钱付了,咖啡没到手”。

延迟优化

常客张女士住在广州,她的会员卡数据副本恰好有一份落在广州分店。她结账时,广州店员直接查本地账本完成扣款,不需要拨长途电话问上海总部——这就叫数据局部性(data locality):数据副本放在离用户近的地方,读写优先走本地。全球多活不等于所有请求都要绕地球一圈。

CockroachDB 的延迟优化三板斧:第一,区域感知路由(region-aware routing),客户端把请求发给最近区域的节点;第二,租约(lease)跟随数据,每个 range 的读写租约尽量分配给与客户端同区域的副本,让写请求的多数派确认也能在区域内完成;第三,Follower 读(follower reads),在保证可串行化的前提下,读请求可直接发往本地 follower 副本,用时间戳验证数据新鲜度,不必绕道 Leader。

整体期望延迟可以这样估算:

E[L]=plocal⋅Llocal+(1−plocal)⋅LremoteE[L] = p_{\text{local}} \cdot L_{\text{local}} + (1-p_{\text{local}}) \cdot L_{\text{remote}}

1. 本地命中率 p_local:请求在用户所在区域即可完成读写的概率。

plocal=本地完成请求数总请求数p_{\text{local}} = \dfrac{\text{本地完成请求数}}{\text{总请求数}}

2. 本地延迟 L_local:区域内机房往返延迟,通常 1–10ms。

LlocalL_{\text{local}}

3. 远程延迟 L_remote:跨区域(如上海到广州)往返延迟,通常 30–100ms。

LremoteL_{\text{remote}}

4. 加权期望:两种延迟按命中率加权平均。

E[L]=plocal⋅Llocal+(1−plocal)⋅LremoteE[L] = p_{\text{local}} \cdot L_{\text{local}} + (1-p_{\text{local}}) \cdot L_{\text{remote}}

张女士的每一次点单都在广州本地秒级完成,而她的数据仍会通过 Raft 悄悄同步到其他城市——下次她去上海出差,上海店也能查到她的余额。把数据放到顾客身边,把同步放到后台,这就是多活数据库既强一致又好用的秘密。

部署实践

开店手册最后几页定下了全球布局:五个城市各存一份账本(三区域五副本),任何三份账本在线就能营业;每个季度演练一次“关闭深圳分店”,检验其他分店能否无缝接管;同时安排品控员每日抽查各地账本是否一致。这套方法论,映射到 CockroachDB 就是生产环境的标准部署实践。

典型的高可用部署是“三区域五副本”:数据按 range 切分,每个 range 的 5 个副本分布到 3 个区域(region),其中 2 个区域各放 2 个副本,第 3 个区域放 1 个副本。这样即使整个区域故障(最多损失 2 个副本),剩余 3 个副本仍构成多数派,集群继续提供读写服务:

Q=⌊52⌋+1=3Q = \left\lfloor \dfrac{5}{2} \right\rfloor + 1 = 3

1. 五副本分布:5 个副本按 2-2-1 布局到三个区域。

R=5,区域=3,分布=2:2:1R = 5,\quad \text{区域}=3,\quad \text{分布}=2:2:1

2. 多数派阈值:5 副本需要 3 副本确认。

Q=⌊5/2⌋+1=3Q = \lfloor 5/2 \rfloor + 1 = 3

3. 容灾判定:单区域最多损失 2 副本,剩余 3 ≥ 3,故可用。

存活=5−2=3=Q⇒可用\text{存活}=5-2=3 = Q \Rightarrow \text{可用}

部署之外,还要做区域容灾演练(定期 kill 一个区域进程验证自动恢复)和一致性核查(对比各副本数据)。把开店手册变成可执行的监控、告警和演练脚本,多活集群才能真正做到“计划内从容,计划外不慌”。

AI 应用场景

全球电商订单系统

阶段:多活数据库架构设计

解决的问题:跨国电商需要全球各地用户都能随时下单和支付。传统主从架构只有主库可写,主库部署在美国,亚洲用户写操作延迟高,且主库故障会带来分钟级不可用。CockroachDB 多活架构让每个区域都能独立接受订单写入,Raft 确保全球数据强一致,用户在任何区域看到的库存都是最新的。

具体案例:一家办公用品 SaaS 公司在北美、欧洲、亚太各部署一个 CockroachDB 区域,每个区域的购物车、订单表副本均采用 3 区 5 副本布局。伦敦用户下单时请求落在伦敦副本组,多数派确认在本地完成,写延迟从跨大西洋的 180ms 降到 15ms。

跨境支付与钱包余额管理

阶段:强一致事务与并发控制

解决的问题:支付场景绝不允许“扣款成功但入账失败”或“余额被两笔订单同时透支”。传统方案用分布式锁牺牲并发度。CockroachDB 的 Serializable 隔离(SSI)允许事务乐观并发执行,提交时通过冲突环检测自动中止冲突事务,既保证余额操作严格可串行化,又保留了高吞吐。

具体案例:一家跨境汇款平台把账户流水表放在 CockroachDB 上,两个并发转账事务同时操作同一账户时,SSI 检测到读写冲突环,自动中止后到的事务并让客户端重试。系统上线后,账务差错率降为零,峰值 TPS 比旧系统提升 3 倍。

跨国游戏玩家数据就近读写

阶段:延迟优化与数据局部性设计

解决的问题:游戏玩家遍布全球,装备、金币数据如果每次读写都跨洋访问,体验会非常糟糕。CockroachDB 的 locality 与区域感知路由把玩家数据副本放在对应区域,亚洲玩家访问亚洲副本,欧洲玩家访问欧洲副本,同时靠 Raft 维持全局一致。

具体案例:某手游厂商按玩家注册区域设置 locality 约束,亚洲区 5 副本中的 3 个落在东京/新加坡机房,亚洲玩家读写延迟稳定在 20ms 内;当东京机房故障时,Raft 自动切换,玩家无感知,装备数据零丢失。

Python 演示

python59 行
"""简化版 SSI 冲突图环检测模拟器"""from collections import defaultdict class Txn:    def __init__(self, tid, read_keys, write_keys):        self.tid = tid        self.read_keys = set(read_keys)        self.write_keys = set(write_keys) class ConflictDetector:    def __init__(self, txns):        self.txns = txns        self.graph = defaultdict(list)        self.state = [0] * len(txns)     def add_conflict_edges(self):        for i, ti in enumerate(self.txns):            for j, tj in enumerate(self.txns):                if i == j:                    continue                if ti.write_keys & tj.read_keys:                    self.graph[i].append(j)                if ti.write_keys & tj.write_keys:                    self.graph[i].append(j)     def dfs(self, u):        self.state[u] = 1        for v in self.graph[u]:            if self.state[v] == 1:                return True            if self.state[v] == 0 and self.dfs(v):                return True        self.state[u] = 2        return False     def has_cycle(self):        for i in range(len(self.txns)):            if self.state[i] == 0 and self.dfs(i):                return True        return False def main():    txns = [        Txn(0, ["x"], ["x", "y"]),        Txn(1, ["y"], ["y", "z"]),        Txn(2, ["z"], ["x", "z"]),    ]    detector = ConflictDetector(txns)    detector.add_conflict_edges()    for i in range(len(txns)):        print("T%d 读集: %s 写集: %s" % (            i, txns[i].read_keys, txns[i].write_keys))    if detector.has_cycle():        print("检测到冲突环 -> 必须中止后到事务,保证可串行化")    else:        print("无冲突环 -> 所有事务可提交,结果可串行化") if __name__ == "__main__":    main()
目的:模拟 SSI 可串行化快照隔离中的冲突图构建与环检测,判断一组并发事务是否满足可串行化要求。
  1. class ConflictDetector:定义冲突检测器,负责构建冲突图并执行 DFS 环检测,对应 CockroachDB SSI 中的提交前检查。
  2. def add_conflict_edges(self):遍历所有事务对,根据读写集合交集构建有向边,模拟 SSI 对数据访问冲突的收集。
  3. common = ti.write_keys & tj.read_keys若事务 ti 写入的 key 被 tj 读取,构成一条读写冲突边 ti→tj,这就是冲突图的基本单元。
  4. self.graph[i].append(j)把检测到的冲突边加入邻接表,形成有向图结构。
  5. def dfs(self, u):深度优先搜索遍历冲突图,state[u]=1 表示当前路径上正在访问的节点。
  6. if self.state[v] == 1:如果遇到正在访问的节点,说明发现后向边,即冲突环。
  7. detector.add_conflict_edges()调用建图方法,把三个模拟事务的冲突关系组织成图。
  8. if detector.has_cycle():检测结果决定是否中止事务,模拟 CockroachDB 提交时中止后到事务的决策。

常见误区

误区“多活'嘛,不就是各区域先用着,数据慢慢同步吗?CAP 里选的是 AP,最终一致很正常。”

正解CockroachDB 的多活是 CP 系统,走的是强一致路线。写入必须经过 Raft 多数派确认后才返回成功,任何区域读到的都是已提交的全局最新数据。多活指的是“任意节点都可读写、无单点”,而不是“数据可以暂时不一致”。如果放宽到最终一致,就不需要 Raft 和 Serializable 隔离了。

为何常见:很多互联网产品的“多活”确实是最终一致的(如 DNS、缓存),让人误以为多活天然意味着弱一致性。NewSQL 的卖点恰恰是多活和强一致兼得。

误区“Serializable 隔离?那不就是把所有事务排成一队,一个个执行吗?并发性能肯定很低。”

正解可串行化只要求“结果等价于某个串行顺序”,并不要求物理上串行执行。CockroachDB 的 SSI 让事务全部并发跑,只在提交时检查冲突图:有环才中止后到者,无环就直接提交。在冲突率低的场景(大部分 OLTP)下,SSI 的吞吐远高于加锁串行。

为何常见:从中文“串行”二字望文生义,忽略了“等价于”这个关键修饰。数据库理论中的可串行化是关于结果正确性的定义,而非执行方式的描述。

误区“5 个副本要全部确认才算写成功,所以副本越多延迟越高,一个慢节点会拖垮整体。”

正解Raft 只需多数派(5 副本中 3 个)确认即可提交,少数派慢了、挂了都不影响。这也是 3 区 5 副本能容忍单区域故障的原因。正因为只需多数派,Raft 才能在不牺牲可用性的前提下保证强一致。

为何常见:“共识”字面意思容易被理解成“所有人都同意”,实际工程共识算法都是多数派法定人数机制,牺牲一点点确认数换取容错和降延迟。

误区“数据库在全球部署,每次读写都要跨洋通信,那延迟不是至少 200ms?”

正解多活不等于所有请求都跨区域。CockroachDB 通过数据局部性和租约机制,让绝大多数请求在用户所在区域内部完成:写请求的多数派副本尽量都在本地区域,读请求可以直接走本地 follower。只有真正跨区域分布式事务才需要跨洋往返,这类操作占比不高。

为何常见:把“数据存在全球”等同于“请求跑全球”。实际上 locality 设计的目标就是让数据围绕用户分布,把跨区域通信降到最低。

误区“跨节点事务不就是 2PC 加全局锁吗?锁多了容易死锁,死锁检测又慢。”

正解CockroachDB 的分布式事务不使用全局两阶段锁(2PL),而是基于 SSI 的乐观冲突检测:事务自由执行,提交时检查冲突环,有环就中止重试。再加上事务流水线与并行提交优化,既避免了大面积锁等待,又把跨区域提交延迟压缩到两次多数派往返以内。

为何常见:传统分布式数据库教材常以 2PC + 2PL 讲解,导致思维定式。NewSQL 用“乐观并发 + 冲突环检测 + Raft 日志”完成了更强的隔离和更低的阻塞。

面试题

Q1. CockroachDB 的多活架构最核心的特征是什么?

单选题 · easy

  • A.只有主节点可写,备节点只读
  • B.所有节点都能提供读写能力← 正确答案
  • C.写入必须同步到所有副本
  • D.用最终一致换取高可用性

答案:B。多活(Multi-Active)的核心是每个节点都可读写,无单点写入口。A 是主从架构特征;C 是“全量同步”的误解,Raft 只需要多数派;D 是多活与最终一致的错误混淆,CockroachDB 是强一致 CP 系统。

Q2. 五副本 Raft 集群中,提交一条日志至少需要几个副本确认?

单选题 · easy

  • A.1 个副本确认即可
  • B.3 个副本确认即可← 正确答案
  • C.5 个副本全部确认
  • D.由 Leader 单独裁决

答案:B。Raft 采用多数派法定人数,n=5 时 quorum = ⌊5/2⌋+1 = 3,所以至少 3 个副本确认才能提交。1 个太少,5 个全部确认会降低可用性,Leader 单独裁决无法防脑裂。

Q3. Raft 共识中,节点可能处于哪些角色?

多选题 · mid

  • A.领导者节点← 正确答案
  • B.跟随者节点← 正确答案
  • C.候选者节点← 正确答案
  • D.协调者节点

答案:ABC。Raft 定义了 Leader、Follower、Candidate 三种角色:正常情况下 Follower 听从 Leader,Leader 失联后 Follower 转为 Candidate 发起选举。CockroachDB 中没有“协调者”这一 Raft 角色(协调者是事务层的概念),故 D 不选。

Q4. CockroachDB 默认提供的最高隔离级别是?

单选题 · easy

  • A.读未提交隔离级别
  • B.读已提交隔离级别
  • C.可串行化隔离级别← 正确答案
  • D.可重复读隔离级别

答案:C。CockroachDB 默认实现的是可串行化隔离级别(具体为 SSI 可串行化快照隔离),这是 SQL 标准中最高级别的隔离。读未提交、读已提交、可重复读都低于可串行化,不可能是默认最高级别。

Q5. SSI 检测到冲突图存在环时,数据库会如何处理?

单选题 · mid

  • A.中止时间戳较早的事务
  • B.中止时间戳较晚的事务← 正确答案
  • C.两个事务都继续执行
  • D.两个事务都中止回滚

答案:B。SSI 的原则是“先到先得”:发现冲突环时,中止创建时间戳较晚(后到)的事务,让先到者正常提交。后到者通常会收到冲突错误并由客户端自动重试。若同时中止两个事务可以但会降低吞吐,不是默认策略。

Q6. 相比主从单活架构,多活架构的优势包括哪些?

多选题 · hard

  • A.消除单点写入瓶颈← 正确答案
  • B.故障切换过程更平滑← 正确答案
  • C.网络延迟必然降为零
  • D.跨区域扩展读写容量← 正确答案

答案:ABD。多活让所有节点可写,消除了单一写瓶颈(A),故障时请求自动绕开无需人工切换(B),新区域加入即可扩展读写能力(D)。C 明显错误,跨区域网络物理延迟不可能为零。

Q7. 三区域五副本部署下,最多可容忍几个副本节点同时故障而不丢数据?

单选题 · mid

  • A.最多容忍 1 个副本故障
  • B.最多容忍 2 个副本故障← 正确答案
  • C.最多容忍 3 个副本故障
  • D.最多容忍 4 个副本故障

答案:B。五副本的 Raft 多数派阈值是 3,只要存活副本数 ≥ 3 就能继续提供读写。因此最多容忍 2 个副本故障;故障 3 个时剩余 2 个构不成多数派,将失去提交能力。

Q8. 亚洲用户读写数据希望落在亚洲副本而非跨洋访问,主要依赖哪种机制?

单选题 · mid

  • A.在客户端随机选择区域
  • B.数据局部性与租约设计← 正确答案
  • C.加大网络带宽降低延迟
  • D.对数据做更激进压缩

答案:B。CockroachDB 通过数据局部性(locality)把副本分布到用户附近,并通过区域感知路由和租约机制让读写尽量在本地区域完成。随机路由无法保证落在本地,带宽和压缩解决的是传输效率而非地理位置问题。

Q9. CockroachDB 分布式事务具备哪些特性?

多选题 · mid

  • A.跨节点事务原子提交← 正确答案
  • B.冲突检测自动中止重试← 正确答案
  • C.使用全局两阶段锁
  • D.默认可串行化隔离← 正确答案

答案:ABD。CockroachDB 事务跨节点原子提交(A),SSI 检测冲突环后自动中止并支持客户端重试(B),隔离级别默认可串行化(D)。它不使用全局两阶段锁(C),而是乐观冲突检测加 Raft 日志,避免锁等待。

Q10. SSI 冲突图中,T1 写了数据 x、T2 后读取 x,且 T1 时间戳早于 T2,冲突边方向是?

单选题 · extreme

  • A.T1 指向 T2 的有向边← 正确答案
  • B.T2 指向 T1 的有向边
  • C.两者之间形成双向边
  • D.冲突图中不存在边

答案:A。冲突边由“先执行者”指向“后访问者”:T1 先写 x,T2 后读 x,所以边是 T1→T2。如果所有边都按这个方向且无环,事务就等价于按拓扑序串行执行;方向反了会导致检测失效,双向边只有在互相读写交叉时才出现,本题读-写单向冲突只产生一条边。