如何将多层对象映射到indexedDB以获得最佳效率

Gar*_*ary 5 indexeddb

我的问题是在indexedDB中布局数据结构.我开始构建一个小型网页功能,这个功能已经发展成为一种网络学习工具,现在更接近于独立的渐进式Web应用程序.使用localStorage运行良好,但由于该工具已经增长,5MB限制可能会成为某些用户的问题; 所以,需要切换到indexedDB.

该应用程序仅适用于桌面,允许用户构建模块组合并将数据作为JSON字符串保存到硬盘驱动器.当用户打开(上传)在应用程序的文件,该字符串被解析并在整个投资组合写入localStorage的再次但是只有一个模块在任一个时刻写入的运行时间目标.从不同字段和索引搜索数据的角度来看,不需要"真正的"数据库,但只需要更大的存储量,因为如果投资组合中的每个模块对用户来说太混乱了必须是一个单独的文件.

保存到localStorage的大多数数据来自三级对象,并且根据用于保存和检索数据的对象路径创建密钥.例如,object.level_1 [key_1] .level_2 [key_2] .level_3 [key_3] .height = 10保存为localStorage.setItem('k1.k2.k3.h',10).

我的问题是,移动时IndexedDB的,这是比较有效:单一对象存储很像localStorage的成立,或单独的对象存储的每个三级投资组合的?

如果可以将单个objectStore视为类似于每个单独数据点的一行(一个键和一个值)的两列表,则行计数将大于三个objectStores的行计数总和,其中每一行是一个键,一个是多个数据点的对象; 但是,要更新三个objectStores之一中的单个数据点,必须将数据库对象写入临时对象,更新数据点,然后将其写回objectStore.

那么问题是,哪个更有效:在一个包含许多行的表中搜索指向一个不太复杂的值的单个唯一键,或者在三个表中搜索一个行数较少但是必须执行我认为的行相当于JSON解析,值更新和JSON stringify来更新数据库中的相同值?

尽管未明确设置限制,但单个投资组合中预期的最大level_1对象数约为25,其中每个对象最多可包含100个level_2对象,而每个对象最多可包含大约5个level_3对象.任何大于此的东西都很可能会导致用户简单地构建单独的投资组合.

因此,level_1 objectStore约为25行,level_2 objectStore约为2500行,level_3 objectStore约为12,500行.每个level_1对象有大约40个数据点; 每个level_2对象有大约100个数据点; 每个level_3对象有大约20个数据点.因此,我认为单个objectStore将具有等效于(25)(40)+(2500)(100)+(12,500)(20)= 501,000行.

我在从非常大的数据库中使用SQL提取数据方面经验丰富,但对于如何设置数据库以按键定位数据一无所知.如果必须从上到下搜索501,000行中的每一行,直到找到匹配的键,那么一个objectStore对于三个objectStore而言似乎是一个荒谬的选择.但是,如果索引资料采用比较有效的方法,然后一个对象存储可能可能取决于它是如何有效的三个objectStores中的一个的对象更新属性值是更有效的.

我不是贸易程序员; 所以,如果我的一些术语是不精确的,我会道歉并且我意识到我的问题是一个相当基本的水平; 但我无法找到任何有关如何以高效的方式将对象"映射"到对象数据库的信息.

感谢您阅读我的问题以及您可能提供的任何方向.

编辑/更新:

感谢Josh,他花时间回答我的问题并提供了许多值得思考的项目.我还没有考虑在应用程序的哪些阶段,不同类型的数据写入浏览器存储会影响对象存储数量的确定.

在用户会话期间,通常只发生两次大数据移动:从硬盘上传要解析并写入浏览器存储的JSON字符串,然后将浏览器存储读取到要进行字符串化和下载的对象中到硬盘.用户最有可能期望这两个步骤至少需要足够的时间来要求某种形式的简短进度指示器.重要的时间项是存储数据编辑和创建新数据元素所需的时间.

根据Josh的评论,或许,设置对象存储的一个好方法是考虑何时以及什么数据被屏幕写入浏览器存储,因为缺少更好的术语.在我的应用程序中,任何时候都只有一个模块(组合中的level_1对象)被加载到运行时对象中.模块级数据有一个屏幕.退出该屏幕后,模块级数据中的任何更改都将写入存储.

模块中的每个level_2对象都有自己的屏幕,当用户在level_2对象屏幕之间导航时,将根据运行时对象的更改值检查屏幕输入元素中的内容,并将任何更改写入存储.

在level_2对象屏幕上,用户通过调用出现在level_2屏幕顶部的窗口,将level_3对象添加到特定的level_2元素.当每个窗口关闭时,执行类似的检查,并将任何数据更改写入存储.

创建与每个屏幕上显示和收集的数据对齐的对象存储似乎是有意义的,当然,与对象级别对齐.但是,它仍然无法回答哪种数据结构最有效,从而提供最佳的用户体验.

除了数据库效率的某些经验法则之外,针对我的特定问题和环境的可能最佳方法是以两种方式对其进行编码,使用大于预期数量的最大模块填充组合,以及level_2和level_3对象,并进行测试写入和读取数据到indexedDB的性能.单个对象存储的第一种方法应该相当容易编码,因为它的设置几乎与localStorage完全相同.使用至少三个对象存储库的第二种方法将花费更多时间,但对于我在这些领域具有有限背景的人来说,这可能是必要且有价值的学习体验.

如果我成功了,我将在不久的将来在这里分享结果.谢谢.

编辑:

感谢您的进一步解释.我不打算以这种方式查询数据库,而是仅存储基于唯一键的检索数据.但是,您之前关于在多个表中存储相同数据的评论最终在我的脑海中注册,我认为这大大简化了我的整个问题和方法.从本地存储的角度来看,我的想法太多了.

我认为可以正常工作的是多个对象存储:一个对象存储包含每个模块的一个完整对象或组合中的level_1数据,以及三个或四个对象存储,其中包含仅用于"活动"或已加载模块的数据子集.

当用户选择要加载的模块时,它将在一个步骤中从模块对象存储器完整地加载它,并且该模块的子集(不同的对象级别)将被写入多个不同的对象存储器.当用户在任何级别编辑模块数据时,编辑将存储在适当的子集对象存储中,因为这将更快.

如果用户正确地退出/关闭模块,那么此时加载的对象将被完整地写入模块对象存储,并且子集对象存储将被清空.子集对象存储用于在用户未能正确退出或存在电源或操作系统故障的情况下保留更改.

打开应用程序时,将测试浏览器存储以确定是否存在数据库,如果存在,则确定子集对象存储是否为空.如果为空,则执行适当的关闭和保存模块.如果不为空,则无论出于何种原因,对模块的编辑都没有进入模块对象存储区,并且系统会提示用户恢复或放弃保存在子集对象库中的编辑.如果用户选择恢复,则子集对象存储中的数据必须一起收集到一个完整的模块中并写入模块对象存储.

对于此应用程序中任何单个模块的预期最大大小,这应该可以正常工作; 但是如果模块的大小在整个加载时对于浏览器变得太大,那么子集对象存储可以用于填充屏幕; 当用户退出模块时,子集可以聚集在一起构建一组完整的模块数据并写入模块对象存储库,就像恢复一样.

当然,如果浏览器由于模块过大而运行得太慢并且在那时改变了方法,则无法在运行时进行测试.我的意思是,如果在我测试大型样本模块时,观察到浏览器运行速度太慢,那么需要实现第二种方法.

我意识到我的特定问题并不像响应中列出的项目那么有趣.但是,阅读这些一般概念有助于我更好地理解如何解决我对indexedDB的不那么有趣的使用,并避免在为简单问题编写不必要的复杂性时会产生大量麻烦.再次感谢.

Jos*_*osh 5

我认为你有自己的答案,所以我在这里的回答只是为了推动你前进。

nosql 和传统 sql 数据库的主要区别在于缺乏查询计划。查询计划是 sql 数据库提供的功能,它接受您的查询,对其进行解析,然后将其转换为一种算法,该算法查找匹配的记录并将它们以结果集中的形式返回给您。查询规划涉及选择最优化的方法,通常通过尝试最小化所涉及的步骤数、所涉及的内存量或将要经过的时间量来最小化。另一方面,您可以独自使用 nosql。您必须在一夜之间成为查询计划专家。

这既是一种恩惠,也是一种负担。查询计划对于某些人来说是一个复杂的悬崖,您很快就会发现自己正在阅读一些令人困惑的内容。但是,如果您正在寻找更具技术性的答案,那么您将朝着这个方向学习更多有关数据库如何进行查询计划的知识。

为了加快速度,我将应用有关规范​​化和非规范化的相同传统知识。Boyce-Codd 和正常形式 1-5 等等。nosql 处于极端的非规范化端。您存储的项目的“逻辑”结构无关紧要。使用 nosql,您的目标不是一个好的传统和直观的模式。您的目标是高效地执行存储操作和查询。

因此,要回答这个问题,您必须先对您的运营进行简单的分析。枚举您的应用程序执行的操作。哪些操作最频繁?你认为哪一个需要最长的时间才能完成?通过操作,我不是在这里谈论低级查询,也不是在 nosql/sql 中你的数据库架构。这是一个太深的抽象层次。更抽象地思考。枚举诸如“加载所有满足这些条件的人的信息”、“删除那边的人”之类的内容。我回答了你提到的一些问题,但我没有选择一个明确的清单,这个清单是正确答案的重要标准。

一旦你列举了这些操作,那么我认为你更接近回答你的问题。作为一个玩具示例,考虑更新。更新频繁吗?频繁的更新表明一个对象存储是不好的,因为你必须加载大量不相关的东西来改变一个对象的一个​​属性。考虑粒度。你需要一个对象的所有属性,还是只需要一些?想想最频繁的操作是什么?它是否根据某些标准加载对象列表?是删除还是更新内容?想想同时加载了哪些东西(协同定位)。当您加载 2 级对象的一个​​实例时,其他实例通常也加载了吗?如果不是,那为什么要把它们放在一起呢?远离你的规范化模式,忘记它。您需要一个非规范化模式,您可以在其中以优化查询的方式存储数据。最终结果可能与您想象的完全不同。

也许一个好的思想实验就是这样。对可以完成实际繁重工作的函数进行伪代码。您将直接遇到问题并确定可能非常慢的函数部分。那么你的问题的答案基本上是什么数据结构会真正加快这些部分的速度,或者至少比其他数据结构减慢它们的速度。

编辑:一个小后续。nosql 数据库和非规范化的一个相当违反直觉的特性是您最终可能会多次存储数据。有时将相同的数据存储在多个地方是有意义的。因为它加快了查询速度。是的,它引入了不一致的空间,并违反了 sql 的无功能依赖性规则。但是您可以通过使用多存储事务和一些注意来强制执行数据完整性(一致性)。进一步详细说明,您想要的存储可能只是您计划执行的查询的字面结果。是的。为您计划执行的每个查询创建一个对象存储。在所有这些之间冗余存储数据。是的,这听起来很疯狂和极端。而且有点夸张。但是这种方法在使用 nosql 时很常见,并且得到了推广。

编辑:这是一个粗略的第一次尝试,只是集思广益,这是尝试根据猜测您实际尝试做的事情为您提供更具体的答案

您想要的是一个名为“设置”的对象存储。商店中的每个对象都代表一个 Settings 对象。单个设置对象具有设置 id、设置属性名称、设置属性值、级别 1 属性、级别 2 属性、级别 3 属性等属性。

您的基本读取查询可能如下所示SELECT * from Settings WHERE level1 = 'a' && level2 = 'b'

更进一步,您可以使用索引针对某些视图进行优化。我们可以在 level1 属性上创建索引,在 level2 属性上创建索引,然后在 level1+level2 属性上创建索引。

假设您需要最快的最频繁操作是加载属于级别 1、2 和 3 的特定组合的所有设置。在所有 3 级上创建一个索引,然后只需对其进行迭代指数。

这个头脑风暴示例中的模式是一个单一的对象存储,以及一些索引来加速某些查询。鉴于索引基本上是派生的对象存储,您可以提出实际上使用多个存储的概念论点,尽管您实际上只使用一个。无论如何,这可能会变得迂腐。这个例子的目的只是为了证明你的对象存储的模式与你如何概念化投资组合和级别的层次结构完全没有关系。它只与进行快速执行所需的查询有关。