← 中文资源

简体中文版本

乐观锁版本校验

用版本列实现乐观锁,在并发写入冲突时返回带类型的冲突结果。

阅读英文原文

功能说明

该示例展示乐观并发的标准写法:读取记录并记下版本,修改后写回时把版本作为条件,只有存储中的版本仍等于读到的版本才允许写入。写入影响零行即表示期间他人改过,函数不据此抛错,而是返回带当前值的结果,交由调用方重读后重试或呈现冲突。

关键代码

存储接口把“保存并比对版本”抽成返回布尔的操作,真实实现可对应 SQL 的 UPDATE … WHERE id = ? AND version = ? 并按影响行数判断。更新总是把版本加一,保证每次成功写入都推进版本。冲突分支重新加载最新记录放入结果,避免调用方再次往返;记录消失是另一类错误,单独抛出。返回类型用可辨识联合区分成功与冲突。

使用注意

调用方在冲突后必须重读并重新施加变更,绝不能拿旧值盲重试,否则会覆盖他人改动或反复冲突。版本应使用整数并严格自增,避免用时间戳,因为同刻度内的两次写入可能无法区分。乐观锁适合读多写少;写密集场景下频繁冲突的重试会放大负载,此时悲观锁或串行化隔离可能更合适。

代码示例

export interface Record { id: string; value: string; version: number; }
export type UpdateResult = { ok: true; record: Record } | { ok: false; conflict: true; current: Record };

export async function updateWithVersion(
  store: { load(id: string): Promise<Record | undefined>; save(record: Record, expectedVersion: number): Promise<boolean> },
  id: string,
  mutate: (value: string) => string,
): Promise<UpdateResult> {
  const current = await store.load(id);
  if (!current) throw new Error('record not found: ' + id);
  const next: Record = { id, value: mutate(current.value), version: current.version + 1 };
  const saved = await store.save(next, current.version);
  if (!saved) {
    const fresh = await store.load(id);
    if (!fresh) throw new Error('record vanished during update: ' + id);
    return { ok: false, conflict: true, current: fresh };
  }
  return { ok: true, record: next };
}