Web Locks API 不能解决 IndexedDB 并发事务冲突,但可协调多标签页写入顺序;须将整个数据库操作流程包裹在 navigator.locks.request() 回调中并 await tx.done,否则锁提前释放引发竞态。

Web Locks API 不能直接解决 IndexedDB 的并发事务冲突,但它能有效协调多标签页对同一数据的写入顺序。关键在于把“打开数据库→启动事务→读取→修改→写入→等待事务完成”整个流程包裹在 navigator.locks.request() 回调中,并确保 tx.done 被 await,否则锁会提前释放,导致竞态和数据覆盖。
必须串行化整段写逻辑,不能只锁住 put()
很多人误以为只要加了锁、再调用 store.put() 就安全了,但这是错的:
-
put()是异步发起、不返回 Promise,它只把操作加入队列,事务仍在后台提交 - tx.done,锁立刻释放,其他标签页可能已拿到锁、读到旧值、覆盖写入
- readwrite 事务、执行读写、最后 await tx.done
读-改-写场景必须加锁,纯读通常不用
是否对读操作加锁,取决于业务一致性要求:
- 普通列表刷新、配置读取等“最终一致”场景,用
readonly事务即可,无需锁 - 但涉及 读-改-写(如计数器 +1、草稿版本校验、购物车增删)时,必须用同一把排他锁(
mode: 'exclusive')包住全部步骤 - 若多个读操作之间有强依赖(如 A 读完 v1 才允许 B 读 v2),也需加锁,但要注意避免与写锁形成死锁
锁名设计要兼顾粒度与隔离性
锁名不是随便起的字符串,它决定了哪些操作会相互阻塞:
- 全局锁(如
'db-write')最简单,但会让所有写操作串行,影响性能 - 推荐按业务实体命名,例如
'user-profile-write-123'或'cart-update-456',让不同用户的操作互不干扰 - 锁名必须固定、全局一致、URL-safe;不能带时间戳或随机数,否则无法互斥
- 同源下所有标签页、iframe、Service Worker 共享同一锁域,跨域完全隔离
兼容性与降级方案不可少
Safari 直到 16.4+ 才支持 Web Locks,旧版或不支持环境必须有 fallback:
- 服务端幂等是最稳妥的兜底:前端生成唯一
requestId,后端拒绝重复 ID 或校验版本号(如_rev字段) - 不要用
localStorage模拟锁——它是同步 API,锁插不进执行流,只能做轻量提示 - 检测支持性要用
"locks" in navigator,不能只靠 try/catch;不支持时应跳过锁逻辑,但不能跳过幂等或校验

















