【发布时间】:2019-10-24 10:45:39
【问题描述】:
基础知识
所以基本上我已经编写了一个程序,它在 Node 中为 MongoDB 生成测试数据。
我在这个问题上给予了赏金以获得更多答案。要检查当前的解决方案,请向下滚动到下面的块引用!
问题
为此,程序会读取一个模式文件并从中生成指定数量的测试数据。问题是这些数据最终会变得非常大(考虑创建 1M 用户(以及它需要的所有属性)和 20M 聊天消息(使用 userFrom 和 userTo),并且它必须将所有这些都保存在 RAM 中修改/转换/映射它,然后将其保存到文件中。
工作原理
程序是这样工作的:
- 读取架构文件
- 从架构创建测试数据并将其存储在结构中(在下方查看结构)
- 遍历此结构并将所有对象
referenceTo链接到匹配referenceKey的随机对象。 - 在
string[]的 MongoDB 插入语句中转换对象结构 - 将
string[]存储在一个文件中。
这是生成的测试数据的结构:
export interface IGeneratedCollection {
dbName: string, // Name of the database
collectionName: string, // Name of the collection
documents: IGeneratedDocument[] // One collection has many documents
}
export interface IGeneratedDocument {
documentFields: IGeneratedField [] // One document has many fields (which are recursive, because of nested documents)
}
export interface IGeneratedField {
fieldName: string, // Name of the property
fieldValue: any, // Value of the property (Can also be IGeneratedField, IGeneratedField[], ...)
fieldNeedsQuotations?: boolean, // If the Value needs to be saved with " ... "
fieldIsObject?: boolean, // If the Value is a object (stored as IGeneratedField[]) (To handle it different when transforming to MongoDB inserts)
fieldIsJsonObject?: boolean, // If the Value is a plain JSON object
fieldIsArray?: boolean, // If the Value is array of objects (stored as array of IGeneratedField[])
referenceKey?: number, // Field flagged to be a key
referenceTo?: number // Value gets set to a random object with matching referenceKey
}
实际数据
因此,在有 100 万用户和 2000 万条消息的示例中,它看起来像这样:
- 1x IGeneratedCollection (
collectionName = "users")- 1Mx IGeneratedDocument
- 10x IGeneratedField(例如每个用户有 10 个字段)
- 1Mx IGeneratedDocument
- 1x IGeneratedCollection (
collectionName = "messages")- 20Mx IGeneratedDocument
- 3x IGeneratedField (
message, userFrom, userTo)
- 3x IGeneratedField (
- 20Mx IGeneratedDocument
这将导致 1.9 亿个 IGeneratedField (1x1Mx10 + 1x20Mx3x = 190M) 实例。
结论
对于 RAM 来说显然需要处理很多事情,因为它需要同时存储所有这些。
临时解决方案
现在是这样工作的:
- 一次生成 500 个文档(sql 中的行)
JSON.stringify这 500 个文档,并将它们放入带有架构的 SQLite 表中(dbName STRING,collectionName STRING,value JSON)- 从 JS 中删除这 500 个文档,让垃圾收集器完成它的工作
- 重复直到所有数据都生成并在 SQLite 表中
- 一次取一行(每行包含 500 个文档),应用
JSON.parse并在其中搜索键- 重复直到查询完所有数据并检索到所有键
- 一次取一行,应用
JSON.parse并在其中搜索关键引用- 应用
JSON.stringify并在必要时更新行(如果找到并解决关键引用)- 重复直到查询完所有数据并解析所有键
- 一次取一行,应用
JSON.parse并将文档转换为有效的 sql/mongodb 插入- 在带有架构 (singleInsert STRING) 的 SQLite 表中添加插入(字符串)
- 从 SQLite 表中删除旧的和现在未使用的行
- 将所有插入写入文件(如果从命令行运行)或返回 dataHandle 以查询 SQLite 表中的数据(如果从其他 节点应用)
此解决方案确实解决了 RAM 的问题,因为当 RAM 已满时 SQLite 会自动切换到硬盘
但是
如您所见,其中涉及很多
JSON.parse和JSON.stringify,这大大减慢了整个过程我的想法:
也许我应该修改 IGeneratedField 以仅使用缩写名称作为变量 (
fieldName->fn,fieldValue->fv,fieldIsObject->fio,fieldIsArray-> @ 987654347@, ....)这会使 SQLite 表中所需的存储空间更小,但也会使代码更难阅读
使用面向文档的数据库(但我还没有真正找到),更好地处理 JSON 数据
问题
有没有更好的解决方案来处理节点中这样的大对象?
我的临时解决方案可以吗?它有什么不好?可以改成更好的表现吗?
【问题讨论】:
-
我会使用 SQLite 和内存表。 SQLite 是为处理 RAM 和磁盘交换管理而构建的。与解决方案 3 类似,但无需托管数据库或需要网络连接。 SQLite 数据库将存在于目标 PC 上
-
@Jason 感谢您的宝贵时间。你认为使用 SQLite 是个好主意吗?它是一个关系数据库,保存的对象具有嵌套元素等。这在 SQLite 中是否可行? (我对 SQLite 知之甚少)。
标签: javascript node.js performance bigdata test-data