IndexedDB 增量更新:实现精准的字段级“补丁”

在 SQL 中,我们可以执行 UPDATE table SET field = 'val' WHERE id = 1。但在 IndexedDB 的世界里,原生 API 只提供了 put()put 的本质是 "全量覆盖" :如果你只传一个字段,整个对象剩下的部分都会丢失。

处理这种"增量更新"时,必须保证原子性(Atomicity) ,尤其是在处理长篇 AI Prompt 时,防止数据在"读取-修改-写入"的过程中被并发操作篡改。


1. 核心原理:Get-Modify-Put 模式

由于 IndexedDB 不支持部分更新,我们必须手动实现一个 patch 方法。关键点在于:获取数据、合并字段、重新存入这三个步骤必须在**同一个事务(Transaction)**中完成。

2. 代码实现:给 PromptDB 增加 patch 方法

我们在之前封装的 PromptDB 基础上,新增一个智能增量更新函数:

JavaScript

javascript 复制代码
/**
 * 增量更新指定 ID 的数据
 * @param {string} id - 主键
 * @param {Object} changes - 需要修改的字段,例如 { title: '新标题' }
 */
async patch(id, changes) {
  await this.init();
  
  return new Promise((resolve, reject) => {
    // 1. 开启读写事务
    const transaction = this.db.transaction(this.storeName, 'readwrite');
    const store = transaction.objectStore(this.storeName);

    // 2. 先读取原始数据
    const getRequest = store.get(id);

    getRequest.onsuccess = () => {
      const data = getRequest.result;
      if (!data) {
        reject(new Error(`ID 为 ${id} 的记录不存在`));
        return;
      }

      // 3. 合并差异 (使用 Object.assign 或 展开运算符)
      const updatedData = { ...data, ...changes, updatedAt: Date.now() };

      // 4. 写回数据库
      const putRequest = store.put(updatedData);
      
      putRequest.onsuccess = () => resolve(updatedData);
      putRequest.onerror = () => reject(new Error('增量更新失败'));
    };

    transaction.onerror = () => reject(new Error('事务异常'));
  });
}

3. 为什么必须在同一个"事务"里?

你肯定关心并发控制

  • 场景 :AI 正在流式更新 content 字段,而用户同时在点击"收藏"按钮(修改 isFavorite 字段)。
  • 风险 :如果 getput 分属不同事务,可能会发生"写覆盖":用户读取了旧数据,修改了收藏状态,此时 AI 更新了内容,用户最后写回时,把 AI 刚更新的内容又覆盖回了旧版。
  • 解决 :IndexedDB 的事务锁机制能确保在 onsuccess 回调完成前,其他写操作必须排队,从而保证了增量操作的原子性

4. 8 个具体的业务实战场景

场景 patch 传参示例 意义
字数统计 { wordCount: 1500 } 仅更新统计信息,不触碰数万字的 Prompt 正文
收藏状态 { isFavorite: true } 极速切换,不影响复杂的上下文关联
流式更新 { content: currentText } 在 AI 打字过程中,频繁同步内容,避免丢失进度
标签修正 { tags: [...oldTags, '金融'] } 增量添加标签索引
错误标记 { lastError: 'API Timeout' } 记录异常状态,保留原始请求参数
读取计数 { views: data.views + 1 } 简单的原子累加
版本回退 { version: 2, content: history[2] } 部分字段回滚到历史版本
UI 状态记录 { isExpanded: false } 记录卡片折叠状态,不触碰业务数据

5. 性能优化:大数据量下的增量更新

如果你处理的是超过 100KB 的大型 Prompt 对象,频繁的 patch 会产生大量的垃圾回收(GC)压力。

  1. 防抖处理 (Debounce) :对于"用户改了一个词"这种高频操作,建议在内存中缓冲 500ms,再调用一次 patch
  2. 字段拆分 :如果某个字段(如 chat_history)会无限增长,建议将其拆分为另一个 Object Store ,通过 ID 关联,而不是塞在一个超大的对象里不断 patch
  3. 使用 Proxy :在前端框架中,可以用 Proxy 监听对象变化,自动收集 changes 补丁包,实现自动化增量备份。

相关推荐
mCell3 小时前
AI 时代 SVG 画图的潜力
前端·agent·svg
我命由我123458 小时前
CesiumJS 笔记 - 获取容器中心点、Cartesian3 clone 方法、修改 Cartesian3 对象的高度
前端·javascript·css·前端框架·html·html5·js
Hopebearer_9 小时前
页面突然只剩 DOM?一次静态资源版本错配排查
前端·部署
东风破_10 小时前
ESLint 是什么?为什么你的项目需要它?
前端·后端·代码规范
圣殿骑士-Khtangc11 小时前
Go字符串高效拼接性能对比与底层原理分析
服务器·前端·golang
BigTopOne12 小时前
【ijkplayer】 硬解码流程
前端
kyriewen12 小时前
DeepSeek Harness开源第一天我就上手了——和Claude Code的差距比想象中大
前端·ai编程·deepseek
捡田螺的小男孩13 小时前
什么是 Skill?手把手带你写一个简单有用的 Skill!
前端·后端·程序员
IT_陈寒13 小时前
Redis集群这个坑,差点让我通宵
前端·人工智能·后端
90后的晨仔13 小时前
uni-app 路由跳转与页面导航 —— 终极详解(对标 iOS / Android / 鸿蒙三端原生)
前端