当前位置:首页 > 文章列表 > 文章 > 前端 > IndexedDB 数据库升级怎样处理版本变更

IndexedDB 数据库升级怎样处理版本变更

来源:17golang原创 2026-10-15 06:21:28 0浏览 收藏

IndexedDB 升级时,使用 indexedDB.open(数据库名, 更高版本号),并在返回请求的 onupgradeneeded 中修改对象仓库和索引。按 event.oldVersion 累计执行尚未完成的迁移,而不是只创建最新的一项结构;等打开请求成功后,再把连接交给正常业务使用。

官方地址:https://developer.mozilla.org/en-US/docs/Web/API/IDBOpenDBRequest/upgradeneeded_event

用户版本分散时,初始化代码不够用

如果我要给离线笔记功能增加一个索引,会先考虑三类读者环境:首次访问的人、仍停在早期结构的人,以及已经到最新结构的人。这里是假设的产品演进场景,不是作者亲历的上线事故,也没有用户规模或性能统计。

只把初始化代码写成“首次建库时创建全部结构”,不能完整表达后续版本变化;只写“版本3新增偏好仓库”,又可能漏掉从版本1直接跳过来的索引。对我来说,清晰的版本契约比一段自动猜测当前结构的修复代码更容易维护。

这里的压力来自客户端版本分散,而不是服务器吞吐量。本文不把 IndexedDB 升级包装成高并发数据库方案,只讨论 onupgradeneeded 内的结构演进。

按 oldVersion 累计声明结构变化

下面是原创的独立演示库:版本1定义 entries 仓库,版本2添加 byKind 索引,版本3添加 preferences 仓库。它假设历史版本严格符合这些约定。示例未实际运行,不使用或清理真实业务数据库。

function openSchemaNotes() {
  return new Promise((resolve, reject) => {
    // 独立演示库;不要替换成真实业务数据库名后直接运行。
    const request = indexedDB.open("schema-notes-demo", 3);
    let migrationError = null;

    request.onupgradeneeded = (event) => {
      const db = request.result;
      const upgradeTx = request.transaction;
      try {
        // 版本1约定:记录以id为键;新库从oldVersion=0开始。
        if (event.oldVersion  {
      // 这里只提示;等待旧连接释放,不删除数据库或无限重试。
      console.warn("升级在等待其他连接关闭");
    };
    request.onerror = () => {
      reject(migrationError || request.error);
    };
    request.onsuccess = () => {
      const db = request.result;
      // 示范连接释放;真实应用还应让旧页面停止使用这个连接。
      db.onversionchange = () => {
        db.close();
        console.warn("数据库连接已关闭,请更新页面后重新使用");
      };
      resolve(db);
    };
  });
}

// 打开成功后才查看结构;演示结束释放连接,不删除数据库。
openSchemaNotes().then((db) => {
  console.log("数据库版本", db.version);
  console.log("仓库名称", Array.from(db.objectStoreNames));
  db.close();
}).catch((error) => {
  console.error("数据库未能打开", error);
});

新库的 oldVersion 为 0,因此三个条件都满足;旧库为1时执行索引与偏好仓库两项,旧库为2时只添加偏好仓库。这里要用相互独立的 if,而不是只命中一个分支的 else if。

例子包含 oldVersion、升级事务、entries、byKind 与 preferences 五个核心对象。升级事务提供 entries 的仓库句柄,byKind 属于 entries,preferences 是另一个独立仓库。

byKind 是非唯一索引;这不等于自动给旧记录填入 kind 字段。是否需要补齐字段是另一个数据迁移决策,本例不做隐式补写,也不删除记录。

版本判断、升级事务与对象仓库索引的静态关系
图1:结构契约说明图。版本条件分组关联升级事务,存储结构分组显示 entries、byKind 与 preferences 的归属;不是迁移时间轴或数据库截图。

升级事务内不要等待外部网络

结构操作依赖活动的升级事务。我不会把 onupgradeneeded 改成等待 Fetch 的 async 回调,再假定恢复执行时事务仍然可用。外部网络等待不是 IndexedDB 事务内的待处理请求,可能让事务进入提交或失活状态。

本例把所有结构声明放在同一同步回调里。若需要准备远端资料,应在打开升级请求前完成准备,再决定如何加入受控的迁移逻辑;不能用无限等待保持升级事务。

事务中止会撤销其中的结构与数据变化。示例捕获同步结构错误后显式 abort,并保留最初错误用于报告;这是为了避免“错误被 catch 了,所以升级还能悄悄成功”。回滚也不是备份方案,不保证修复程序逻辑错误或浏览器存储被清理等其他问题。

回调结束不等于数据库升级成功

onupgradeneeded 回调运行完毕,只代表这段脚本完成了自己的声明;打开请求仍要等待升级结果。因此示例只在 request.onsuccess 中 resolve 连接,错误则由 request.onerror 返回,不在迁移回调末尾宣布成功。

这里可以区分 打开请求、upgradeneeded、升级事务、success、error 与 可用连接:请求拥有事件入口,升级回调与事务关联,打开成功才把连接交给调用者。这是一组状态与对象关系,不是截图中的实际结果。

IndexedDB打开请求、升级事务与最终成功错误事件的关系
图2:打开结果结构图。请求对象分组关联升级处理与结果事件,可用连接分组强调成功后交付;连线不表示执行先后或实测结果。

已有连接也可能让升级等待,示例用 blocked 提示等待,不循环新建打开请求。成功获得的连接收到 versionchange 时会关闭;真实应用还需要让持有旧连接的页面停止访问并提示更新。这里仅说明协作入口,不展开多标签页协议。

以版本矩阵判断升级是否覆盖

如果我要安排发布前的本地测试,会按下表准备独立数据状态。表中是预期覆盖范围,不是已经完成的实验记录。

开始状态需要执行的结构变化判断重点
库不存在,oldVersion为0entries、byKind、preferences新用户结构完整
版本1byKind、preferences已有entries记录不被清空
版本2preferences不重复创建已有索引
已是版本3不进行更高版本升级普通打开正常完成
升级代码出错不交付成功连接保留错误原因与旧数据状态

收益是路径清晰,代价是团队必须维护版本与结构的对应关系。我不会在未知来源的库上盲目沿用这些分支:如果历史结构已经偏离约定,需要单独制定恢复策略,而不是靠 contains 检查把问题掩盖过去。

版本号相同也会再次触发升级吗?

请求当前版本不是新升级。不要把普通打开成功当成迁移回调一定再次执行;结构变更需要新的版本契约。

为什么不能把 createObjectStore 放在普通业务事务里?

它是版本变更事务中的结构操作。业务读写事务不应承担建仓库的职责;把结构迁移集中在升级入口更容易明确边界。

升级失败后应该删库重建吗?

本例不会这样做。删除可能丢掉用户本地资料,应先保留错误并查明迁移问题,再按业务恢复方案处理,不能把删库当通用修复。

参考文档:https://developer.mozilla.org/en-US/docs/Web/API/IDBFactory/open;https://developer.mozilla.org/en-US/docs/Web/API/IDBVersionChangeEvent/oldVersion;https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/createObjectStore;事务规则:https://w3c.github.io/IndexedDB/。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Web Worker 怎样转移 ArrayBuffer 避免复制Web Worker 怎样转移 ArrayBuffer 避免复制
上一篇
Web Worker 怎样转移 ArrayBuffer 避免复制
Cache API 怎样用完整请求区分缓存条目
下一篇
Cache API 怎样用完整请求区分缓存条目
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    478次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    535次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    554次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    506次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    327次使用