使用 GUID 作为键缓存“深度”JSON 对象
Posted
技术标签:
【中文标题】使用 GUID 作为键缓存“深度”JSON 对象【英文标题】:Caching a "deep" JSON object using GUIDs as keys 【发布时间】:2014-11-13 02:30:05 【问题描述】:所以我有一个从我的 javascript 应用程序中的 JSON 文件加载的大而简单的对象。
这个文件有大约 9 MB 的数据(不过,一旦我缩小它应该会更低)并且是这样的嵌套结构:
"guid": "guid 1 here",
"children": [
"guid": "guid 2 here",
"other": "properties",
"here": true,
"children": [
"guid": "guid 3 here",
...
,
...
]
,
...
]
我不知道这个对象的深度,我需要使用一个通用函数来根据它的 GUID 属性定位一个节点,而不考虑它在树中的深度。这个递归函数(我知道可以使用 while 循环而不是递归进行优化,但不管它很昂贵)很慢。
我想知道是否最初在加载此对象后,我会创建一个这样的缓存结构:
var cache =
"guid 1 here": [reference to object],
"guid 2 here": [reference to object],
"guid 3 here": [reference to object]
;
我认为这样可以更快地找到对象,因为我只能说
var node = cache[guid];
但是,这实际上最终会提高性能,还是可能导致内存问题?我从未处理过像 cache
这样的变量,其中可能有数十万个属性。
这会帮助还是阻碍这种情况?
一如既往地感谢您的建议,所以,你们太棒了。
【问题讨论】:
@EdBayiates:不,数组在这里没用。 guid 不是数组索引,即使它们是数组也会非常稀疏(这不是一个好主意) 如果我使用数组,我是否必须遍历每个项目,直到找到具有所需 GUID 的项目?var result; for (var i = 0; i < cache.length; ++i) if (cache[i].guid == search_guid) result = cache[i]; break;
具有属性的对象是一个关联数组。抱歉,您上面的语法正确。所有 JavaScript 对象都在内部存储为数组,这就是 cache[guid] 起作用的原因。我编辑了答案以更新术语。
@EdBayiates: …一个没有顺序的关联数组,它留下一个哈希映射。它们是作为数组实现还是(更有可能)不是并不重要。 cache[guid]
不是数组语法,它是一个 object 属性访问。
是的。这就是您应该使用 varbatim 在不增加大量内存的情况下获得最佳性能的方法。
【参考方案1】:
你做出了正确的选择。
对象引用相当小。即使有数千个对象,这样的对象(在内部存储为类似数组的哈希映射)也不应该显着增加内存使用——最大几 MB。当您创建缓存时,您不会制作对象的副本。您正在将一个 pointer 放入对象(数组样式)中,该对象(数组样式)指向您已反序列化的结构中的对象。因此,您不会制作所有数据的第二份副本。只是一个 GUID 和指针的缓存。
这种缓存方法使用了 JavaScript 的一个很棒的特性,即属性索引在内部保持排序。然后使用二进制搜索执行按属性索引(例如 cache[guid]
)的查找。这将比对未排序数据的循环或递归搜索快几个数量级。
【讨论】:
OP 没有使用数组作为缓存?[]
是对象运算符吗?
您说的不是文字,而是查找运算符。【参考方案2】:
但是,这实际上最终会提高性能吗
大概吧。拥有一个大型的持续访问数据结构(即使构建成本很高)通常比重复迭代所有“数十万个”节点要快。
如果您可以优化递归查找功能,例如通过从 guid 派生父节点的位置,它可能没有那么大的好处。
或者这可能会导致内存问题?
是的,大型数据结构总是会导致内存不足的设备出现内存问题。但是,如果您的原始 JSON 数据没有任何问题,这应该不是问题。
【讨论】:
Bergi,他的缓存使用指针。不是数据的第二个副本。 @EdBayiates:我不是在谈论节点的副本吗?以上是关于使用 GUID 作为键缓存“深度”JSON 对象的主要内容,如果未能解决你的问题,请参考以下文章
尝试使用 C# 中的 Guid 作为 Cassandra 中的主键
如何使 EF-Core 使用 Guid 而不是 String 作为其 ID/主键