Distributed Data Systems LabLynn Lee / Engineering notebook

真实实操

05 · 跨范围、跨节点的事务

目标与前置

完成 04,理解 BEGIN / COMMIT 与“读后修改”。验证真实范围放置,执行跨范围提交,并观察实际的可串行化冲突。

分布式必须靠观察确认

连接同一个单机数据库的三个 SQL 客户端,不构成分布式数据库实验。这里运行三个独立 CockroachDB 进程,加入同一个集群;每个进程使用独占持久化目录和回环 SQL / HTTP 端口。实验等待账户范围拥有三个副本,主动迁移租约,再读回放置状态。不同 Leaseholder 证明不同服务节点;它们依然共用一台物理宿主机。

SQL 网关可以协调多个范围的操作。主键 1、1001、2001 跨越分界。转账在同一事务内从 id=1 扣 7、给 id=1001 加 7,总额必须保持 300。KV Trace 与实际执行过的聚合计划帮助检查请求路径;它们不是网络抓包,也不能证明所有崩溃场景。

不更新同一行,也可能冲突

业务规则要求总余额至少为 220。A、B 都读到 300;A 从 id=1 扣 80,B 从 id=1001 扣 80。单独执行各自都剩 220;若都基于过期总额提交,就只剩 140。写集合不相交,但决策依赖重叠读集合,这就是 Write Skew。

实验使用 SERIALIZABLE 事务,先把总额返回给各客户端,再更新并发提交。一方提交成功,另一方必须收到 SQLSTATE 40001、要求重试;没有开启人为注入重试错误。提交后总额为 220。完整业务重试读取新总额,拒绝第二笔提款。只重放最后一条 UPDATE 会忽略原始决策条件。

数据与可执行代码

python scripts/bootstrap.py database
python lab.py database

experiments/distributed_transactions.py 保存精确调度、连接、断言、KV Trace 与指标采集。证据页保留双方结果、余额、放置、计划与计数器快照。胜方可能变化,错误文本也可能变化,但不变量相同。微小耗时差不是吞吐比较。

实际结果与解释

检查三个在线节点 ID,每范围三个副本,以及不同 Leaseholder。验证转账后余额 [93,107,100]、总额仍为 300,随后有一条 40001,最终总额 220。失败方可能是 A 或 B,所以各账户最终余额可能随运行变化。计数器变化也包含准备与检查查询,只能作为背景,不能精确归因到一笔事务。

热点拓展:推理,不是容量实测

递增键可能把写入集中到边缘范围。预切分只有在键分布触达多个范围时才有帮助,不能把一个热键自动分散。更宽的索引也改变单次逻辑写的代价。要研究热点,应改变确定性的键分布并采集范围级速率与锁争用。本版选择实测冲突调度,没有声称完成热点性能基准。

动手任务

交换双方更新的主键,保留所有不变量检查。把总额读取改为只读取自己的账户,解释业务规则为何失去保护。真实应用不能省略 40001 处理:用有上限的策略重试完整事务,并重新判断业务条件。

自测

一笔成功转账能证明节点崩溃时也原子吗? 不能,本实验没有注入节点崩溃。为什么副本之外还要查 Leaseholder? 数据副本与服务职责不同。重试错误等于保证失效吗? 在这里,拒绝提交正是阻止非法可串行化历史的机制。

大学课程导读与原创练习

运行转账与过期总额 Write Skew,再基于新总额重试完整业务决策。

python scripts/bootstrap.py database
python lab.py database

KV Put 到不同服务节点;转账总额 300,一条真实 40001,最终 220,第二次提款被拒绝。

以上是原创 Lab 练习,不是大学课程作业答案。公开阅读不等于注册、学分或评分资格;可检索资源库列出先修与核验日期。

原始来源