对象序列化到 indexedDB 的效率与 JSON.stringify
Efficiency of serialization of object to indexedDB verus JSON.stringify
我对 browser/CPU/memory 的序列化步骤 JSON.stringify 与将对象写入 indexedDB 中的对象存储的序列化步骤之间的效率差异感兴趣。
问题的动机或上下文是以最有效的方式将对象从数据库写入客户端的硬盘。为此,使用 get 语句检索对象并将其写入对象,该对象使用 JSON.stringify 转换为字符串,然后作为文本写入磁盘。我假设 get 语句从数据库中的序列化形式反序列化对象。如果是,则对象从 indexedDB 形式反序列化,然后序列化为 JSON.stringify 形式。
然后要从磁盘恢复数据,读取文件,将文本转换为带有 JSON.parse 的对象,然后将对象添加或放入数据库中,这似乎需要另一个序列化步骤。所以,JSON字符串被解析为一个对象,然后序列化为indexedDB形式。
我问题的第一部分是能否以序列化形式从数据库中检索对象,以便可以以相同形式将其保存到磁盘并再次写入数据库,而无需执行以下中间步骤stringify/parse?
问题的第二部分是,如果不可能这样做,以及在没有任何索引的情况下使用 indexedDB 的特殊情况,并且从不通过外联唯一键以外的任何值查询数据,将字符串化对象存储到数据库而不是对象有什么好处吗?
在这种情况下,writing/reading 到磁盘将不涉及中间 stringify/parse 步骤,并且当字符串在 get/put 时不会有 unserialization/serialization 步骤数据库,因为它不是对象。但是,要更改数据库中这些字符串之一的值,需要在 get 的结果上使用 JSON.parse 将其转换为对象,然后在更改值之后再使用 JSON.stringify放回数据库中。
但是JSON.parse和JSON.stringify与使用get和put对象时数据库对象的反序列化和重新序列化相比如何呢?我认为将对象序列化为 indexedDB 需要的不仅仅是 JSON.stringify,因为前者可以通过其属性进行查询。
我会尝试看看是否可以观察到差异,但想问问了解这两种序列化的内部工作原理的人是否有潜在的原因,在这种情况下,使用一种方法优于另一种方法.
编辑
显然,如果我没有理解错的话,结构化克隆算法用于在indexedDB中存储对象;但是这个结果在存储之前是否仍然被序列化? JSON 使用词法语法但从不复制对象。
也许,我只是很困惑,在写入 indexedDB 时没有序列化步骤,我观察到的 RAM 使用量增加是为了克隆对象,然后将其存储为对象。如果是这样,那么问题的第一部分就毫无意义了,因为数据库对象总是需要转换为字符串才能保存到磁盘。我的问题的第二部分将成为对浏览器要求更高的部分,1) 将对象转换为字符串或克隆它,以及 2) 从数据库中检索对象与检索字符串(如果另存为 JSON 字符串) ) 并解析它?
在找到有关此的更多信息后,虽然其中大部分都超出了我的理解范围,但我不确定这个问题是否值得考虑。
谢谢。
示例:
@Sam152 你是说你发现,在下面这样的情况下,stingify 版本比将对象放入数据库更快?一个示例是 50 个事务的间隔,第二个示例是单个事务中的 50 个 put
。
// var o = A large object.
var d;
write.x = 0;
write.y=[];
DB_open().then( inter );
function inter() { d = setInterval( write, 1000 ); }
function write()
{
let T = DB_open.base.transaction( [ 'os_name' ], 'readwrite' ),
q = T.objectStore( 'os_name' ),
start = Date.now();
T.oncomplete = function() { write.y.push( Date.now() - start ); }
if ( write.x < 50 )
write.x = write.x + 1;
else
{
clearInterval(d);
console.log( 'done' );
};
o.key = write.x;
q.put( o );
// OR
q.put( { 'key' : write.x, 'd' : JSON.stringify( o ) } );
} // close write
// When complete.
total = 0;
write.y.forEach( ( v ) => { total = total + v; } );
或者,多个 put
语句在同一个事务中。
function write()
{
let T = DB_open.base.transaction( [ 'os_name' ], 'readwrite' ),
q = T.objectStore( 'os_name' ),
start = Date.now(),
i;
T.oncomplete = function()
{ console.log( 'Completed : ' + ( Date.now() - start ) ); }
for ( i = 1; i < 51; i++ )
{
o.key = i;
q.put( o );
// OR
q.put( { 'key' : i, 'd' : JSON.stringify( o ) } );
};
} // close write
将对象转换为字符串并将字符串存储在数据库中没有性能优势。使用 JSON.stringify only 将对象转换为字符串会增加必须发生的步骤数。我强烈建议不要这样做,甚至不要尝试这样做,也不要试图超越 C++ 中的 indexedDB 实现。
indexedDB 序列化发生在 C++ 中,而不是 js。 JSON.stringify 发生在 js 中,而不是 C++ 中。大多数时候,在 js 中发生的任何事情都比 C++ 慢几个数量级。
一个js字符串就是一个js对象。如果我没记错的话,您不能将字符串作为对象存储在 indexedDB 中,因为它本身是一个函数对象,而不是普通对象。另一种表述方式是考虑对象类型层次结构。一切都从 Object
扩展而来。 Function
从对象扩展。 String
从 Function
扩展(好吧,也许不是直接或明确的,但仍然有效)。从 Function
扩展的任何后代,例如使用 class Foo
或 function Foo() {}; new Foo();
或 new Function('foo');
创建的任何后代,都不可序列化。任何从 Object
扩展而不从 Function
扩展的东西都是可序列化的。
如我对您其他问题的回答中所述,函数不可序列化。
当你使用 object literal 语法创建一个 js 对象时,这只是 new Object()
的语法糖,而不是 function Foo()
。这样的对象很容易序列化,因为它们基本上只是字典。也就是说,直到您附加方法为止。同样,方法只不过是作为 属性 绑定到对象的函数。因为即使是具有至少一种方法的普通对象也不再是可序列化的。
与其尝试将字符串直接存储在 indexedDB 中,因为它本身是一个对象(Function
的后代),您只能将字符串存储为某个其他对象的 属性不是函数对象,因为 indexedDB 明确了解 String 类型,并明确声称支持使用字符串值存储对象属性,因为它知道如何在序列化期间强制转换这些值。
结构化克隆算法前段时间改名了。现在是序列化算法还是类似'transferable'的算法,我忘记了。
当你在 indexedDB 中存储一个对象时(例如 {x:1,y:2}
),它将从 JS 发送到 C++,然后 在 C++ 中 它将被序列化。当你从 indexedDB 中检索一个对象时,然后在 C++ 中它会找到它的串行形式的对象,反序列化它,然后将它传递给 JS,然后 JS 将它传递回你(你的函数或 return 任何值) .尽管序列化更具体地指将另一种类型转换为字符串,但您也可以更笼统地使用它来指代两个方向,因此请记住,技术文档中任何地方提到的序列化实际上可能是在谈论反序列化。
这种C++序列化不一定是对象到字符串或字符串到对象,而是某种优化过的字节格式。在 C++ 领域,有时区分字符串和其他值类型的用处不大,因为字符串只是字节的一种任意表示。所以当我们谈论 C++ 中的序列化时,我们只是在谈论改变一组抽象字节的表示。很像 C 语言中的结构体只是表示分解成多个部分的字节序列。在某种意义上,在 C++ 中,字符串可以理解为零成本抽象,具体取决于它的使用方式。事实上,将某些字节组称为一种或另一种类型实际上仅用于阐明应该对这些字节调用哪些操作,以及这些字节应该如何表示。在某种意义上,这里的序列化实际上可能只是用不同的名称调用相同的字节序列,实际上根本没有执行任何处理!事实上,如果您曾经在文本编辑器中打开过二进制文件,或者打开过错误编码(或编码解释)的网页,您可能已经亲眼目睹过这种情况。因此,如果您谈论的是性能差异,那么将 0 步操作与 n 步操作进行比较会得出明显的答案。
也就是说,C++ 序列化可能具有讽刺意味的是最终会转换为字符串,谁知道呢,你不应该知道,这取决于 indexedDB C++ 实现来决定这一点并将它从你身上抽象出来并隐藏它的复杂性.有效存储一堆远远超出序列化性能的字节存在所有类型的其他问题。
总而言之,序列化在 C++ 中的含义与 JSON.stringify 在 Javascript 中的含义不同。在 js 中将值转换为字符串只会减慢程序速度。让 indexedDB 为您解决这个问题。
我对 browser/CPU/memory 的序列化步骤 JSON.stringify 与将对象写入 indexedDB 中的对象存储的序列化步骤之间的效率差异感兴趣。
问题的动机或上下文是以最有效的方式将对象从数据库写入客户端的硬盘。为此,使用 get 语句检索对象并将其写入对象,该对象使用 JSON.stringify 转换为字符串,然后作为文本写入磁盘。我假设 get 语句从数据库中的序列化形式反序列化对象。如果是,则对象从 indexedDB 形式反序列化,然后序列化为 JSON.stringify 形式。
然后要从磁盘恢复数据,读取文件,将文本转换为带有 JSON.parse 的对象,然后将对象添加或放入数据库中,这似乎需要另一个序列化步骤。所以,JSON字符串被解析为一个对象,然后序列化为indexedDB形式。
我问题的第一部分是能否以序列化形式从数据库中检索对象,以便可以以相同形式将其保存到磁盘并再次写入数据库,而无需执行以下中间步骤stringify/parse?
问题的第二部分是,如果不可能这样做,以及在没有任何索引的情况下使用 indexedDB 的特殊情况,并且从不通过外联唯一键以外的任何值查询数据,将字符串化对象存储到数据库而不是对象有什么好处吗?
在这种情况下,writing/reading 到磁盘将不涉及中间 stringify/parse 步骤,并且当字符串在 get/put 时不会有 unserialization/serialization 步骤数据库,因为它不是对象。但是,要更改数据库中这些字符串之一的值,需要在 get 的结果上使用 JSON.parse 将其转换为对象,然后在更改值之后再使用 JSON.stringify放回数据库中。
但是JSON.parse和JSON.stringify与使用get和put对象时数据库对象的反序列化和重新序列化相比如何呢?我认为将对象序列化为 indexedDB 需要的不仅仅是 JSON.stringify,因为前者可以通过其属性进行查询。
我会尝试看看是否可以观察到差异,但想问问了解这两种序列化的内部工作原理的人是否有潜在的原因,在这种情况下,使用一种方法优于另一种方法.
编辑
显然,如果我没有理解错的话,结构化克隆算法用于在indexedDB中存储对象;但是这个结果在存储之前是否仍然被序列化? JSON 使用词法语法但从不复制对象。
也许,我只是很困惑,在写入 indexedDB 时没有序列化步骤,我观察到的 RAM 使用量增加是为了克隆对象,然后将其存储为对象。如果是这样,那么问题的第一部分就毫无意义了,因为数据库对象总是需要转换为字符串才能保存到磁盘。我的问题的第二部分将成为对浏览器要求更高的部分,1) 将对象转换为字符串或克隆它,以及 2) 从数据库中检索对象与检索字符串(如果另存为 JSON 字符串) ) 并解析它?
在找到有关此的更多信息后,虽然其中大部分都超出了我的理解范围,但我不确定这个问题是否值得考虑。
谢谢。
示例:
@Sam152 你是说你发现,在下面这样的情况下,stingify 版本比将对象放入数据库更快?一个示例是 50 个事务的间隔,第二个示例是单个事务中的 50 个 put
。
// var o = A large object.
var d;
write.x = 0;
write.y=[];
DB_open().then( inter );
function inter() { d = setInterval( write, 1000 ); }
function write()
{
let T = DB_open.base.transaction( [ 'os_name' ], 'readwrite' ),
q = T.objectStore( 'os_name' ),
start = Date.now();
T.oncomplete = function() { write.y.push( Date.now() - start ); }
if ( write.x < 50 )
write.x = write.x + 1;
else
{
clearInterval(d);
console.log( 'done' );
};
o.key = write.x;
q.put( o );
// OR
q.put( { 'key' : write.x, 'd' : JSON.stringify( o ) } );
} // close write
// When complete.
total = 0;
write.y.forEach( ( v ) => { total = total + v; } );
或者,多个 put
语句在同一个事务中。
function write()
{
let T = DB_open.base.transaction( [ 'os_name' ], 'readwrite' ),
q = T.objectStore( 'os_name' ),
start = Date.now(),
i;
T.oncomplete = function()
{ console.log( 'Completed : ' + ( Date.now() - start ) ); }
for ( i = 1; i < 51; i++ )
{
o.key = i;
q.put( o );
// OR
q.put( { 'key' : i, 'd' : JSON.stringify( o ) } );
};
} // close write
将对象转换为字符串并将字符串存储在数据库中没有性能优势。使用 JSON.stringify only 将对象转换为字符串会增加必须发生的步骤数。我强烈建议不要这样做,甚至不要尝试这样做,也不要试图超越 C++ 中的 indexedDB 实现。
indexedDB 序列化发生在 C++ 中,而不是 js。 JSON.stringify 发生在 js 中,而不是 C++ 中。大多数时候,在 js 中发生的任何事情都比 C++ 慢几个数量级。
一个js字符串就是一个js对象。如果我没记错的话,您不能将字符串作为对象存储在 indexedDB 中,因为它本身是一个函数对象,而不是普通对象。另一种表述方式是考虑对象类型层次结构。一切都从 Object
扩展而来。 Function
从对象扩展。 String
从 Function
扩展(好吧,也许不是直接或明确的,但仍然有效)。从 Function
扩展的任何后代,例如使用 class Foo
或 function Foo() {}; new Foo();
或 new Function('foo');
创建的任何后代,都不可序列化。任何从 Object
扩展而不从 Function
扩展的东西都是可序列化的。
如我对您其他问题的回答中所述,函数不可序列化。
当你使用 object literal 语法创建一个 js 对象时,这只是 new Object()
的语法糖,而不是 function Foo()
。这样的对象很容易序列化,因为它们基本上只是字典。也就是说,直到您附加方法为止。同样,方法只不过是作为 属性 绑定到对象的函数。因为即使是具有至少一种方法的普通对象也不再是可序列化的。
与其尝试将字符串直接存储在 indexedDB 中,因为它本身是一个对象(Function
的后代),您只能将字符串存储为某个其他对象的 属性不是函数对象,因为 indexedDB 明确了解 String 类型,并明确声称支持使用字符串值存储对象属性,因为它知道如何在序列化期间强制转换这些值。
结构化克隆算法前段时间改名了。现在是序列化算法还是类似'transferable'的算法,我忘记了。
当你在 indexedDB 中存储一个对象时(例如 {x:1,y:2}
),它将从 JS 发送到 C++,然后 在 C++ 中 它将被序列化。当你从 indexedDB 中检索一个对象时,然后在 C++ 中它会找到它的串行形式的对象,反序列化它,然后将它传递给 JS,然后 JS 将它传递回你(你的函数或 return 任何值) .尽管序列化更具体地指将另一种类型转换为字符串,但您也可以更笼统地使用它来指代两个方向,因此请记住,技术文档中任何地方提到的序列化实际上可能是在谈论反序列化。
这种C++序列化不一定是对象到字符串或字符串到对象,而是某种优化过的字节格式。在 C++ 领域,有时区分字符串和其他值类型的用处不大,因为字符串只是字节的一种任意表示。所以当我们谈论 C++ 中的序列化时,我们只是在谈论改变一组抽象字节的表示。很像 C 语言中的结构体只是表示分解成多个部分的字节序列。在某种意义上,在 C++ 中,字符串可以理解为零成本抽象,具体取决于它的使用方式。事实上,将某些字节组称为一种或另一种类型实际上仅用于阐明应该对这些字节调用哪些操作,以及这些字节应该如何表示。在某种意义上,这里的序列化实际上可能只是用不同的名称调用相同的字节序列,实际上根本没有执行任何处理!事实上,如果您曾经在文本编辑器中打开过二进制文件,或者打开过错误编码(或编码解释)的网页,您可能已经亲眼目睹过这种情况。因此,如果您谈论的是性能差异,那么将 0 步操作与 n 步操作进行比较会得出明显的答案。
也就是说,C++ 序列化可能具有讽刺意味的是最终会转换为字符串,谁知道呢,你不应该知道,这取决于 indexedDB C++ 实现来决定这一点并将它从你身上抽象出来并隐藏它的复杂性.有效存储一堆远远超出序列化性能的字节存在所有类型的其他问题。
总而言之,序列化在 C++ 中的含义与 JSON.stringify 在 Javascript 中的含义不同。在 js 中将值转换为字符串只会减慢程序速度。让 indexedDB 为您解决这个问题。