IndexedDB 数据库升级怎样处理版本变更
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 字段。是否需要补齐字段是另一个数据迁移决策,本例不做隐式补写,也不删除记录。

升级事务内不要等待外部网络
结构操作依赖活动的升级事务。我不会把 onupgradeneeded 改成等待 Fetch 的 async 回调,再假定恢复执行时事务仍然可用。外部网络等待不是 IndexedDB 事务内的待处理请求,可能让事务进入提交或失活状态。
本例把所有结构声明放在同一同步回调里。若需要准备远端资料,应在打开升级请求前完成准备,再决定如何加入受控的迁移逻辑;不能用无限等待保持升级事务。
事务中止会撤销其中的结构与数据变化。示例捕获同步结构错误后显式 abort,并保留最初错误用于报告;这是为了避免“错误被 catch 了,所以升级还能悄悄成功”。回滚也不是备份方案,不保证修复程序逻辑错误或浏览器存储被清理等其他问题。
回调结束不等于数据库升级成功
onupgradeneeded 回调运行完毕,只代表这段脚本完成了自己的声明;打开请求仍要等待升级结果。因此示例只在 request.onsuccess 中 resolve 连接,错误则由 request.onerror 返回,不在迁移回调末尾宣布成功。
这里可以区分 打开请求、upgradeneeded、升级事务、success、error 与 可用连接:请求拥有事件入口,升级回调与事务关联,打开成功才把连接交给调用者。这是一组状态与对象关系,不是截图中的实际结果。

已有连接也可能让升级等待,示例用 blocked 提示等待,不循环新建打开请求。成功获得的连接收到 versionchange 时会关闭;真实应用还需要让持有旧连接的页面停止访问并提示更新。这里仅说明协作入口,不展开多标签页协议。
以版本矩阵判断升级是否覆盖
如果我要安排发布前的本地测试,会按下表准备独立数据状态。表中是预期覆盖范围,不是已经完成的实验记录。
| 开始状态 | 需要执行的结构变化 | 判断重点 |
|---|---|---|
| 库不存在,oldVersion为0 | entries、byKind、preferences | 新用户结构完整 |
| 版本1 | byKind、preferences | 已有entries记录不被清空 |
| 版本2 | preferences | 不重复创建已有索引 |
| 已是版本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/。
Web Worker 怎样转移 ArrayBuffer 避免复制
- 上一篇
- Web Worker 怎样转移 ArrayBuffer 避免复制
- 下一篇
- Cache API 怎样用完整请求区分缓存条目
-
- 文章 · 前端 | 49分钟前 |
- ARIA live 怎样播报异步状态变化
- 261浏览 收藏
-
- 文章 · 前端 | 50分钟前 | html · 可访问性 焦点管理 HTML inert 模态框背景禁用
- HTML inert 怎样暂时禁用背景区域交互
- 153浏览 收藏
-
- 文章 · 前端 | 59分钟前 | css · 表单 · 表单布局 CSS field-sizing 文本框自适应 textarea 自动高度
- CSS field-sizing 怎样让文本框高度随内容变化
- 328浏览 收藏
-
- 文章 · 前端 | 1小时前 | css ·
- CSS 容器查询怎样按组件宽度切换布局
- 330浏览 收藏
-
- 文章 · 前端 | 1小时前 |
- Service Worker 更新怎样控制接管时机
- 486浏览 收藏
-
- 文章 · 前端 | 1小时前 | 前端 · javascript · request Cache API Cache.match Vary
- Cache API 怎样用完整请求区分缓存条目
- 432浏览 收藏
-
- 文章 · 前端 | 1小时前 |
- Web Worker 怎样转移 ArrayBuffer 避免复制
- 408浏览 收藏
-
- 文章 · 前端 | 1小时前 | 前端 · javascript · AbortController ReadableStream Streams API
- ReadableStream 取消后怎样释放请求
- 203浏览 收藏
-
- 文章 · 前端 | 1小时前 | javascript · 前端工程 · ReadableStream 流式响应 TransformStream pipeThrough Streams API
- ReadableStream 怎样用 TransformStream 转换数据
- 151浏览 收藏
-
- 文章 · 前端 | 1小时前 |
- Fetch 怎样用 AbortSignal.timeout 设置请求超时
- 166浏览 收藏
-
- 文章 · 前端 | 1小时前 |
- Fetch keepalive 适合发送哪些小请求
- 397浏览 收藏
-
- 文章 · 前端 | 15小时前 |
- 前端 source map 发布怎样保护源码边界
- 194浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 478次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 535次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 554次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 506次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 327次使用
-
- JavaScript函数定义及示例详解
- 2025-05-11 502浏览
-
- 智能体安全引领产业升级——国内AI安全产品市场深度分析
- 2026-08-21 501浏览
-
- CSS变量简化按钮悬停效果技巧
- 2026-05-31 501浏览
-
- JavaScript符号类型详解与应用
- 2026-05-31 501浏览
-
- HTML剪贴板复制粘贴怎么用
- 2026-05-26 501浏览
