indexeddb 多标签页并发写入无内置冲突处理,需用 web locks api 串行化关键写流程,覆盖 open、transaction、put 全过程并 await tx.done;读改写、配置强一致等场景必须加锁;升级事务风险高,应避免多标签并发 upgrade;shared worker 可作为强串行写入口提升一致性。

IndexedDB 本身不处理多标签页并发写入冲突——每个标签页的 indexedDB.open() 创建的是完全独立的连接,事务互不感知,同一 key 的多次 put() 会静默覆盖,不是“谁先谁赢”,而是“谁最后谁定”。真正要防的不是锁竞争,而是数据错乱和状态丢失。核心解法是用 Web Locks API 显式串行化关键写流程,并配合合理的事务收口逻辑。
必须把整段写操作包进 Web Locks 回调
只锁 put() 调用没用,锁必须覆盖从打开数据库到事务落地的全过程:
- 调用
navigator.locks.request('user-profile-write', async () => { ... }) - 在回调里完成
indexedDB.open()、<code>transaction('profiles', 'readwrite')、store.put() -
务必 await tx.done(IndexedDB v3+)或手动包装
tx.oncomplete为 Promise —— 这是事务真正完成的唯一信号 - 锁在回调执行完(含 await 结束)后自动释放,无需手动调用
lock.release()
读-改-写场景必须加锁,只读一般不用
多数只读操作(如刷新列表)用 readonly 事务即可,接受最终一致。但以下情况必须加锁,且建议复用写锁名:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- 计数器自增:读旧值 → +1 → 写回,否则两个标签页可能同时读到 5,都写回 6
- 配置更新后立刻读新值:不加锁可能读到缓存或未落盘的旧数据
- 读操作依赖前一个写操作的严格完成,比如“保存草稿后立即获取最新版本号”
升级事务(onupgradeneeded)是最大风险点
版本升级无法被 Web Locks 拦截,浏览器行为不一致(Chrome 静默拒绝,Firefox 可能并发进入),极易导致 schema 错乱:
- 所有标签页共用同一数据库名和版本号,避免无意触发多轮 upgrade
-
onupgradeneeded回调里只做 schema 变更(createObjectStore、createIndex),绝不读写数据 - 升级完成后,用 Web Locks 控制首次业务写入,确保结构就绪再进逻辑
更稳健的方案:Shared Worker 作唯一写入口
Web Locks 是轻量协调,Shared Worker 是强串行代理,适合高一致性要求场景:
- 所有页面通过
port.postMessage({ type: 'write', store: 'settings', key: 'theme', value: 'dark' })发起请求 - Worker 内 FIFO 排队,每次只执行一个事务,避免竞态
- 初始化时从 IndexedDB 加载快照作为内存起点,每次写成功后广播同步消息给各页面
- 可叠加 Web Locks 做双重保障(如锁住 Worker 的写入口),但非必需
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










