IndexedDB浏览器内建数据库并行更新问题详解

 更新时间:2022年12月29日 10:05:37   作者:莫凡是莫烦  
这篇文章主要为大家介绍了IndexedDB浏览器内建数据库并行更新问题详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

正文

IndexedDB 是一个浏览器内建的数据库,它比 localStorage 强大得多。

  • 通过支持多种类型的键,来存储几乎可以是任何类型的值。
  • 支撑事务的可靠性。
  • 支持键值范围查询、索引。
  • 和 localStorage 相比,它可以存储更大的数据量。

对于传统的 客户端-服务器 应用,这些功能通常是没有必要的。IndexedDB 适用于离线应用,可与 ServiceWorkers 和其他技术相结合使用。

根据规范 www.w3.org/TR/IndexedD… 中的描述,IndexedDB 的本机接口是基于事件的。

我们还可以在基于 promise 的包装器(wrapper),如 github.com/jakearchiba… 的帮助下使用 async/await。这要方便的多,但是包装器并不完美,它并不能替代所有情况下的事件。因此,我们先练习事件(events),在理解了 IndexedDB 之后,我们将使用包装器。

数据在哪儿?

从技术上讲,数据通常与浏览器设置、扩展程序等一起存储在访问者的主目录中。

不同的浏览器和操作系统级别的用户都有各自独立的存储。

打开数据库

要想使用 IndexedDB,首先需要 open(连接)一个数据库。

语法:

let openRequest = indexedDB.open(name, version);
  • name —— 字符串,即数据库名称。
  • version —— 一个正整数版本,默认为 1(下面解释)。

数据库可以有许多不同的名称,但是必须存在于当前的源(域/协议/端口)中。不同的网站不能相互访问对方的数据库。

调用之后会返回 openRequest 对象,我们需要监听该对象上的事件:

  • success:数据库准备就绪,openRequest.result 中有了一个数据库对象“Database Object”,我们应该将其用于进一步的调用。
  • error:打开失败。
  • upgradeneeded:数据库已准备就绪,但其版本已过时(见下文)。

IndexedDB 具有内建的“模式(scheme)版本控制”机制,这在服务器端数据库中是不存在的。

与服务器端数据库不同,IndexedDB 存在于客户端,数据存储在浏览器中。因此,开发人员无法随时都能访问它。因此,当我们发布了新版本的应用程序,用户访问我们的网页,我们可能需要更新该数据库。

如果本地数据库版本低于 open 中指定的版本,会触发一个特殊事件 upgradeneeded。我们可以根据需要比较版本并升级数据结构。

当数据库还不存在时(从技术上讲,其版本为 0),也会触发 upgradeneeded 事件。因此,我们可以执行初始化。

假设我们发布了应用程序的第一个版本。

接下来我们就可以打开版本 1 中的 IndexedDB 数据库,并在一个 upgradeneeded 的处理程序中执行初始化,如下所示:

let openRequest = indexedDB.open("store", 1);
openRequest.onupgradeneeded = function() {
  // 如果客户端没有数据库则触发
  // ...执行初始化...
};
openRequest.onerror = function() {
  console.error("Error", openRequest.error);
};
openRequest.onsuccess = function() {
  let db = openRequest.result;
  // 继续使用 db 对象处理数据库
};

之后不久,我们发布了第二个版本。

我们可以打开版本 2 中的 IndexedDB 数据库,并像这样进行升级:

let openRequest = indexedDB.open("store", 2);
openRequest.onupgradeneeded = function(event) {
  // 现有的数据库版本小于 2(或不存在)
  let db = openRequest.result;
  switch(event.oldVersion) { // 现有的 db 版本
    case 0:
      // 版本 0 表示客户端没有数据库
      // 执行初始化
    case 1:
      // 客户端版本为 1
      // 更新
  }
};

请注意:虽然我们目前的版本是 2onupgradeneeded 处理程序有针对版本 0 的代码分支(适用于初次访问,浏览器中没有数据库的用户)和针对版本 1 的代码分支(用于升级)。

接下来,当且仅当 onupgradeneeded 处理程序没有错误地执行完成,openRequest.onsuccess 被触发,数据库才算是成功打开了。

删除数据库:

let deleteRequest = indexedDB.deleteDatabase(name)
// deleteRequest.onsuccess/onerror 追踪(tracks)结果

我们无法使用较旧的 open 调用版本打开数据库

如果当前用户的数据库版本比 open 调用的版本更高(比如当前的数据库版本为 3,我们却尝试运行 open(...2),就会产生错误并触发 openRequest.onerror)。

这很罕见,但这样的事情可能会在用户加载了一个过时的 JavaScript 代码时发生(例如用户从一个代理缓存中加载 JS)。在这种情况下,代码是过时的,但数据库却是最新的。

为了避免这样的错误产生,我们应当检查 db.version 并建议用户重新加载页面。使用正确的 HTTP 缓存头(header)来避免之前缓存的旧代码被加载,这样你就永远不会遇到此类问题。

并行更新问题

提到版本控制,有一个相关的小问题。

举个例子:

  • 一个用户在一个浏览器标签页中打开了数据库版本为 1 的我们的网站。
  • 接下来我们发布了一个更新,使得代码更新了。
  • 接下来同一个用户在另一个浏览器标签中打开了这个网站。

这时,有一个标签页和版本为 1 的数据库建立了一个连接,而另一个标签页试图在其 upgradeneeded 处理程序中将数据库版本升级到 2

问题是,这两个网页是同一个站点,同一个源,共享同一个数据库。而数据库不能同时为版本 1 和版本 2。要执行版本 2 的更新,必须关闭对版本 1 的所有连接,包括第一个标签页中的那个。

为了解决这一问题,versionchange 事件会在“过时的”数据库对象上触发。我们需要监听这个事件,关闭对旧版本数据库的连接(还应该建议访问者重新加载页面,以加载最新的代码)。

如果我们不监听 versionchange 事件,也不去关闭旧连接,那么新的连接就不会建立。openRequest 对象会产生 blocked 事件,而不是 success 事件。因此第二个标签页无法正常工作。

下面是能够正确处理并行升级情况的代码。它安装了 onversionchange 处理程序,如果当前数据库连接过时(数据库版本在其他位置被更新)并关闭连接,则会触发该处理程序。

let openRequest = indexedDB.open("store", 2);
openRequest.onupgradeneeded = ...;
openRequest.onerror = ...;
openRequest.onsuccess = function() {
  let db = openRequest.result;
db.onversionchange = function() {
    db.close();
    alert("Database is outdated, please reload the page.")
  };
  // ……数据库已经准备好,请使用它……
};
openRequest.onblocked = function() {
  // 如果我们正确处理了 onversionchange 事件,这个事件就不应该触发
  // 这意味着还有另一个指向同一数据库的连接
  // 并且在 db.onversionchange 被触发后,该连接没有被关闭
}; 

……换句话说,在这我们做两件事:

  • 如果当前数据库版本过时,db.onversionchange 监听器会通知我们并行尝试更新。
  • openRequest.onblocked 监听器通知我们相反的情况:在其他地方有一个与过时的版本的连接未关闭,因此无法建立新的连接。

我们可以在 db.onversionchange 中更优雅地进行处理,提示访问者在连接关闭之前保存数据等。

或者,另一种方式是不在 db.onversionchange 中关闭数据库,而是使用 onblocked 处理程序(在浏览器新 tab 页中)来提醒用户,告诉他新版本无法加载,直到他们关闭浏览器其他 tab 页。

这种更新冲突很少发生,但我们至少应该有一些对其进行处理的程序,至少在 onblocked 处理程序中进行处理,以防程序默默卡死而影响用户体验。

以上就是IndexedDB浏览器内建数据库并行更新问题详解的详细内容,更多关于IndexedDB数据库并行更新的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:

相关文章

  • Navicat快速导入和导出sql文件的方法

    Navicat快速导入和导出sql文件的方法

    Navicat是MySQL非常好用的可视化管理工具,功能非常强大,能满足我们日常数据库开发的所有需求。今天教大家如何导入和导出SQL文件,感兴趣的朋友跟随小编一起看看吧
    2021-05-05
  • Navicat15安装教程超详细步骤(最靠谱)

    Navicat15安装教程超详细步骤(最靠谱)

    Navicat是一款轻量级的用于MySQL连接和管理的工具,非常好用,使用起来方便,简洁。下面讲讲其安装的过程
    2021-06-06
  • 主键与聚集索引

    主键与聚集索引

    表通常具有包含唯一标识表中每一行的值的一列或一组列。这样的一列或多列称为表的主键 (PK),用于强制表的实体完整性。在创建或修改表时,您可以通过定义 PRIMARY KEY 约束来创建主键。
    2009-07-07
  • 分布式缓存Redis与Memcached的优缺点区别比较

    分布式缓存Redis与Memcached的优缺点区别比较

    Redis和Memcached都是基于内存key-value的数据存储系统。两者都可以通过缓存数据结果,HTML片段或其他可能产生成本很高的内容来帮助加快应用程序的速度。与memcached相比,Redis功能更强大,更受欢迎并且得到更好的支持。
    2022-12-12
  • 从零开始用DataGrip的安装及使用教程

    从零开始用DataGrip的安装及使用教程

    这篇文章主要介绍了从零开始用DataGrip的安装以及使用,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • Linux的HBASE数据库集群部署方法

    Linux的HBASE数据库集群部署方法

    HBase是一种针对海量数据的key-value型NoSQL数据库,本文详细介绍了在Linux系统下HBase的安装与配置步骤,本文给大家介绍Linux的HBASE数据库集群部署方法,感兴趣的朋友一起看看吧
    2024-10-10
  • OLEDB和ODBC的区别(优缺点)

    OLEDB和ODBC的区别(优缺点)

    ODBC是一种连接数据库的开放标准,OLEDB(对象链接和嵌入数据库)位于ODBC层与应用程序之间. 在你的ASP页面里,ADO是位于OLEDB之上的应用程序. 你的ADO调用先被送到OLEDB,然后再交由ODBC处理
    2012-09-09
  • SQL中where和having的区别详解

    SQL中where和having的区别详解

    这篇文章主要给大家介绍了关于SQL中where和having区别的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02
  • MySQL与Oracle 差异比较之一数据类型

    MySQL与Oracle 差异比较之一数据类型

    这篇文章主要介绍了MySQL与Oracle 差异比较之一数据类型,需要的朋友可以参考下
    2017-04-04
  • 使用 Navicat 创建数据库并用JDBC连接的操作方法

    使用 Navicat 创建数据库并用JDBC连接的操作方法

    这篇文章主要介绍了使用 Navicat 创建数据库并用JDBC连接的操作方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11

最新评论