第16章 离线并发模式

在长事务的冲突检测、提前锁定与恢复成本之间做出可验证的选择。

学习目标

  • 能区分 Optimistic Offline Lock、Pessimistic Offline Lock、Coarse-Grained Lock 与 Implicit Lock 的责任和代价
  • 能用 TypeScript 以版本检查、冲突结果和重试策略保护订单编辑提交
  • 能根据冲突概率、锁粒度、等待时间、回滚代价和恢复路径选择或拒绝离线并发方案

为什么第16章离线并发模式值得单独学习

第16章离线并发模式处理的是跨越用户思考时间的业务事务:客服读取订单,修改地址或金额,稍后才提交。期间另一个客服可能已经保存了新版本。数据库单次写入成功,不代表整个业务事务没有并发冲突;真正要决定的是谁发现冲突、谁拥有裁决权,以及用户怎样恢复自己的工作。

适合冲突较少、编辑时间较长且可以让用户重试或合并的场景。本页依据 2024 年中文版公开目录限定第16章范围,并根据 Martin Fowler 的作者图书页和模式目录独立重写;目录只决定要讲什么,案例、实验、判断题和答案均为本课程原创。

能降低冲突,却把等待、死锁、锁过期和持有者离线的成本放到了系统中。

先建立直觉:检测、锁定与代价

适合聚合内字段必须保持一致的场景,但会降低本来可以并行的编辑吞吐。

减少重复代码,却可能隐藏锁粒度、隔离级别和冲突异常,必须用运行证据补回可见性。

是 Optimistic Offline Lock 的核心,而不是一个可以被最后写入者胜出规则消除的异常。

对第16章离线并发模式,优先比较的替代路线是:冲突少且编辑时间长时使用版本检查;冲突高且操作短、等待可控时使用悲观锁;聚合必须整体一致时考虑粗粒度锁;采用隐式锁时必须补充锁协议与观测。若方案只能展示“最后一次写入成功”,不能解释冲突检测和恢复路径,就不能据此选择离线并发模式。

目录单元到教学证据

第16章离线并发模式

第16章离线并发模式要求把并发策略变成六组可复核证据:冻结版次、应用切片与目录坐标;从问题和语境比较候选模式;手算远程或映射成本;验证正常样本与恰好边界;只注入一个故障并定位首差;让独立复核者重放结果并接入发布门禁。本文把这些证据映射到两个客服并行修改订单的读取、编辑、提交和恢复流程。

专属代码案例:两个客服并行修改订单

把两个客服并行修改订单切成“读取快照 → 离线编辑 → 版本检查 → 冲突裁决 → 重试或合并”五个观察点。设计草案必须写出谁拥有订单版本、谁决定字段合并、失败怎样传播,以及冲突概率、锁粒度、等待、回滚和恢复中哪个指标最先提示当前方案不再适用。

设计记录采用五个可换行字段:单元键为 poeaa24-chapter-16-offline-concurrency-patterns;模式族为 concurrency;裁决是“以版本检查保护长事务,以明确冲突结果和重试路径保护用户工作”;观测项包括冲突概率、锁粒度、等待、回滚、恢复;拒绝条件是“用最后写入者覆盖冲突,或把锁的所有权和过期规则隐藏在框架默认值中”。

先预测:客服 A 和客服 B 都读取版本 7,A 先提交,B 再提交时应该发生什么?哪些字段可以自动合并,哪些字段必须让用户重新确认?先写下版本、冲突载荷与恢复按钮,再阅读实现;验证时要能区分成功提交、版本冲突、重复提交和订单已被撤销四种结果。

type OrderSnapshot = {
  orderId: string;
  version: number;
  shippingAddress: string;
  note: string;
};
 
type SaveOrderCommand = {
  orderId: string;
  expectedVersion: number;
  shippingAddress: string;
  note: string;
};
 
type SaveOrderResult =
  | { status: "saved"; snapshot: OrderSnapshot }
  | { status: "conflict"; current: OrderSnapshot; submitted: SaveOrderCommand }
  | { status: "not-found" };
 
interface OrderStore {
  read(orderId: string): OrderSnapshot | null;
  save(command: SaveOrderCommand): SaveOrderResult;
}
 
class OfflineOrderEditor {
  constructor(private readonly store: OrderStore) {}
 
  submit(command: SaveOrderCommand): SaveOrderResult {
    const current = this.store.read(command.orderId);
    if (!current) return { status: "not-found" };
    return this.store.save({ ...command, expectedVersion: current.version });
  }
}

示例强调了结果协议而不是某个 ORM API:生产实现应让客户端带回真正读取到的 expectedVersion,由存储层以原子条件更新完成版本检查;若版本已经变化,应返回当前快照和用户提交内容,让应用层决定字段合并或要求重做。不能在控制器里先读再无条件写,否则检查和提交之间仍然存在竞态。

离线并发锁策略

四种策略的关键差异在于锁何时取得、谁看见冲突以及失败后要付出什么恢复成本。图中把读取、编辑、提交和代价放在同一条时间轴上,便于复核策略是否真的匹配业务事务时长。

离线并发:四种锁策略对比读取编辑提交Optimistic Lock提交时检查版本号读 → 编辑 → 提交时校验 version冲突时重试Pessimistic Lock读取时立即加锁读 + LOCK → 编辑 → 提交 + UNLOCK持有期间阻塞他人Coarse-Grained Lock一次锁住整个聚合锁聚合根 → 编辑子对象 → 释放锁范围大、并发低Implicit Lock框架自动加锁框架拦截 → 自动 LOCK/UNLOCK隐藏复杂度但难调试冲突少 → 乐观锁;冲突多 → 悲观锁;聚合边界清晰 → 粗粒度锁
离线并发模式族包含四种锁策略。乐观锁适合冲突少的场景(提交时校验版本号), 悲观锁适合冲突多的场景(读取时加锁),粗粒度锁锁住整个聚合,隐式锁由框架自动管理。

并发边界决策图

先估计冲突概率和编辑时长,再看失败损失与恢复成本。没有高损失或强一致约束时不要用长时间持有的悲观锁;如果选择隐式锁,就必须把实际锁范围、超时和冲突异常写进测试与监控。

并发边界:冲突概率 × 损失 × 恢复成本低冲突 + 长编辑Optimistic Offline Lock版本检查 + 合并让用户可恢复高冲突 + 短操作Pessimistic Offline Lock控制租约与等待防止死锁拖延拒绝覆盖式并发最后写入者胜出无限期持锁隐藏锁协议评审证据:冲突率 × 编辑时长 × 锁粒度 × 恢复成本先测冲突和损失,再选择检测或锁定
离线并发不是最后写入者的竞赛:低冲突优先检测,高冲突短操作才考虑锁定,所有隐式决定都要可观测。

选择与拒绝矩阵

评审问题选择第16章离线并发模式的证据应拒绝或改用其他方案的信号
冲突冲突概率、编辑时长与损失有基线只凭“数据库支持事务”猜测冲突很少
锁定锁范围、等待、过期和死锁处理可测试远程用户持有无期限锁,其他人只能阻塞
检测版本比较与原子提交在同一存储边界控制器先读再无条件写,检查和提交分离
恢复冲突载荷、合并规则和重试路径明确用最后写入者覆盖用户工作,无法解释谁被丢弃

常见误区

可验证练习

练习

本组练习覆盖第16章离线并发模式,并要求把版本检查、锁粒度、等待和恢复映射到订单编辑切片。

问题 1:选择策略。 两个客服平均编辑订单 8 分钟,冲突率低但修改内容需要人工确认,应该优先采用什么?

问题 2:找出竞态。 控制器先读取订单版本,再调用 save 写入新字段,但 save 没有携带期望版本,为什么仍然不安全?

问题 3:恢复失败。 订单备注可以自动合并,但收货地址不能自动合并;版本冲突时如何设计恢复?

名词解释

名词解释

本章出现的专业名词,用大白话再讲一遍。

Optimistic Offline Lock

读取时保存版本,提交时比较版本并在变化时拒绝、合并或重试的策略。

Pessimistic Offline Lock

在业务事务开始时取得锁并持续到提交或回滚,以阻止并发修改的策略。

Coarse-Grained Lock

一次锁住整个聚合或业务边界,以较低并发换取简单一致性的策略。

Implicit Lock

由框架、代理或持久化机制自动管理锁和冲突检查的策略。

Conflict Detection

发现提交所依据的旧版本已经变化,并触发拒绝、合并或重试的过程。

本章小结

掌握第16章离线并发模式的标志不是记住四种锁策略,而是能在两个客服并行修改订单中解释“读取快照 → 离线编辑 → 版本检查 → 冲突裁决 → 重试或合并”的责任链。学习者应利用冲突概率、锁粒度、等待、回滚和恢复作出可证伪的选择,并在最后写入者覆盖、无限期悲观锁和未经验证的隐式锁时明确拒绝当前实现。

前后导航

来源与改写范围

资料与写作方式声明

本章以Martin Fowler《企业应用架构模式》与公开模式目录权威目录界定学习范围,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

讨论

评论区加载中…