高效读取JSON文件的方法?

问题描述 投票:0回答:4

我见过在 Nodejs 中从本地读取 JSON 文件的不同方法。像这样;

  1. 方法

    使用 fs 库

    同步

    var fs = require('fs');
    var obj = JSON.parse(fs.readFileSync('file', 'utf8'));
    

    异步:

    var fs = require('fs');
    var obj;
    fs.readFile('file', 'utf8', function (err, data) {
      if (err) throw err;
      obj = JSON.parse(data);
    });
    

    来源:https://stackoverflow.com/a/10011078/7724032

  2. 方法

    使用 require()

    let data = require('/path/file.json');
    
  3. 方法

    使用Ajax请求 如何使用Jquery和ajax从JSON文件中检索数据?

可能还有其他方法。但我听说使用方法1读取JSON文件比其他方法更高效。

我正在开发一个模块,当每个客户端请求时我必须读取 JSON 文件,并且我当前使用方法 1。这是银行应用程序,性能很重要。那么请帮我找到使用这个 senario 的最佳方法吗?

谢谢,任何帮助将不胜感激!

javascript node.js json ajax
4个回答
2
投票

方法 3) 不予考虑,因为它将其他方法之一与网络请求结合在一起,因此您仍然必须选择其他方法之一。

我假设方法 2) 正在泄漏内存。如果您需要两次,NodeJS 将通过引用返回完全相同的内容:

 require("thing") === require("thing")

因此,如果您曾经需要某样东西,它将永远留在记忆中。如果您多次查找,这会很快,但如果您有很多文件,则会填满内存。

现在只剩下方法 1)了,我会选择异步版本,因为它可以并行执行多个请求,如果您的服务器处于负载状态,这将优于同步方法。


我个人会选择选项 4):

将其存储在数据库中。数据库将数据加载到内存中以便更快地访问,并且它们是为处理大量文件而构建的。当您处理 JSON 时,Mongodb 将是一个不错的选择:

 const db = mongodb.collection("json");

 function getFile() {
    return db.findOne({ "name": "test" });
 }

0
投票

所以我创建了一个大的 json 文件并测量时间以查看哪个更快,创建文件的代码在最后并进行了注释。

const fs = require('fs')

// method 1 - sync
console.time('method_1_sync ')
var obj = JSON.parse(fs.readFileSync('file.json', 'utf8'))
console.log(obj[1000] === 2000)
console.timeEnd('method_1_sync ')

// method 2
console.time('method_2      ')
let data = require('./file.json')
console.log(data[1000] === 2000)
console.timeEnd('method_2      ')

// method 1 - aysnc
console.time('method_1_async')
fs.readFile('file.json', 'utf8', function (err, data) {
  if (err) throw err
  data = JSON.parse(data)
  console.log(data[1000] === 2000)
  console.timeEnd('method_1_async')
})

/*
var obj = {}

for (i=0; i < 1000000; i++){
  obj[i] = i+i
}

var json = JSON.stringify(obj)
fs.writeFile('file.json', json, function() {})
*/

这是我机器上的结果:

method_1_sync : 131.861ms
method_2      : 131.510ms
method_1_async: 130.521ms

method_1_async
似乎是最快的。由于网络延迟,方法 3 不值得测试。


0
投票

我回答了这个问题,并添加了比较 require、readFile 和 readFileSync 的基准here


0
投票

我使用以下,它非常快,但我想让它更快。

它可以一次读取多个文件,直至达到池的限制。

  1. 添加承诺池类:

    export class PromisePool<T> {
        private maxConcurrent: number;
        private currentConcurrent: number;
        private pending: (() => Promise<any>)[] = [];
    
        constructor(maxConcurrent: number) {
            this.maxConcurrent = maxConcurrent;
            this.currentConcurrent = 0;
        }
        async add(fn: () => PromiseLike<T>): Promise<T> {
            if (this.currentConcurrent >= this.maxConcurrent) {
                await new Promise(resolve => this.pending.push(async () => resolve(void 0)));
            }
            this.currentConcurrent++;
            try {
                return await fn();
            } finally {
                this.currentConcurrent--;
                if (this.pending.length > 0) {
                    this.pending.shift()!();
                }
            }
        }
    }
    
    
  2. 定义一些用于文件加载的池,您可以有多个不同的“读取”文件队列和写入文件队列,但您最终可能会超出 fd 限制,而无法清楚地了解您允许的确切数量:

    const fileLoadingPool = new PromisePool(400);
    const fileWritingPool = new PromisePool(200);
    
  3. 现在您可以加载数据了,这取决于您如何加载,但请确保添加到池中:

    const loadFiles = async () => {
        await fs.promises.mkdir(someDir, { recursive: true });
        const dirContents = await fs.promises.readdir(someDir);
    
        await Promise.all(dirContents.map(async mint => {
            const basePath = `${someDir}${mint}`;
            const files = await fs.promises.readdir(basePath);
            return Promise.all(files.map(async file => fileLoadingPool.add(async () => {
                const baseFile = `${basePath}/${file}`;
                if (file.endsWith(".json.disabled") && config.clearDisabledFilesOnStartup) {
                    await fs.promises.rm(baseFile);
                    return;
                }
                if (!file.endsWith(".disabled") && !file.endsWith(".json")) {
                    return;
                }
                const fileData = await fs.promises.readFile(baseFile, "utf-8");
                const fileDataTyped = JSON.parse(fileData, reviver) as SomeType;
                if (file.endsWith(".disabled")) {
                    Cache.disabledFiles.set(fileDataTyped.someStringProp, fileDataTyped);
                    return;
                }
                else if (!file.endsWith(".disabled") && fileDataTyped?.isDisabled) {
                    await fs.promises.rename(baseFile, `${baseFile}.disabled`);
                    logger.log("Disabled swap instruction", `${baseFile}`);
                    Cache.disabledFiles.set(fileDataTyped.someStringProp, fileDataTyped);
                    return;
                }
                Cache.enabledFiles.set(fileDataTyped.someStringProp, fileDataTyped);
            })));
        }));
        return Promise.resolve();
    }
    

您甚至可以将许多此类功能链接在一起,如下所示:

await Promise.all([
    loadFiles(),
    loadSomething(),
]);

并且您可以使用类似的 api 来保存一个间隔:

const writeFiles = async () => {
    const _knownFiles = Array.from(Cache.enabledFiles.entries()).filter(value => value[1].modified === true);
    if (_knownFiles.length > 0) {
        await Promise.all(_knownFiles.map(async known => fileWritingPool.add(async () => {
            const [key, fileDataTyped] = known;
            await fs.promises.mkdir(`${someDir}${fileDataTyped.someStringProp}`, { recursive: true });
            swapInfo.modified = false;
            await fs.promises.writeFile(`${someDir}${fileDataTyped.someStringProp}/${key}.json.new`, JSON.stringify(swapInfo, replacer, 2));
            await fs.promises.rename(`${someDir}${fileDataTyped.someStringProp}/${key}.json.new`, `${someDir}${fileDataTyped.someStringProp}/${key}.json`);
            if (fileDataTyped?.isDisabled) {
                await fs.promises.rename(`${someDir}${fileDataTyped.someStringProp}/${key}.json`, `${someDir}${fileDataTyped.someStringProp}/${key}.json.disabled`);
                logger.log("Disabled file", `${someDir}${fileDataTyped.someStringProp}/${key}.json`);
                Cache.enabledFiles.delete(key);
            }
        })));
    }
}

setInterval(async () => {
    await Promise.all([
        writeFiles(),
        writeSomething(),
    ])
}, 60000);

通过这种方法,我可以无阻塞地保存文件,并且可以在大约 1 分钟内读取内存中数千个微小的 JSON 文件,价值约 3GB 的 JSON 数据(作为地图)。

© www.soinside.com 2019 - 2024. All rights reserved.