【问题标题】:how to fetch huge data size efficiently?如何有效地获取大量数据?
【发布时间】:2025-12-16 07:55:02
【问题描述】:

我有一个经过逆向工程的 API 端点。我用它来搜索名称,它在一次请求中返回不超过 100 个实体。但是我想要获取这些实体中的大约 130 万个以上。

这是来自响应的实体示例:

{
 "name":"COMPANY NAME",
 "regNo":"100-H",
 "newRegNo":"191101000018",
 "type":"Company"
}

我可以通过nameregNo 进行搜索。搜索没有最小字符限制。我想过按字母顺序搜索,但由于它一次返回不超过 100 个实体,我无法获取其余实体。所以,我尝试通过regNo 获取它。 regNo 可以从 1 到 1000000。

这是我编写的通过regNo 获取所有实体的脚本:

const test = async () => {
  const data = {};
  try {
    const requests = [];
    // since it returns no more than 100 entities at once it adds 100 
    // to the search query on every loop

    for (let i = 100; i < 10000; i += 100) {
      requests.push(fetchData(i));
    }
    const result = await Promise.all(requests);

    result.forEach(res => {
      res.entityList.forEach(entity => {
        data[entity.regNo] = entity;
      });
    });

    // You can ignore this part
    fs.writeFile("data.json", JSON.stringify(data), err => {
      console.log(err);
    });
    console.log(Object.keys(data).length);
  } catch (err) {
    console.log(err);
  }
};

获取 9100 个实体大约需要 15 秒(进行了 100 个循环) 而且每个regNo都有一个像11000-H这样的字母后缀

如果我获取 100,它将返回如下内容:

entityList: [
    {
      name: "COMPANY NAME",
      regNo: '100-H',
      newRegNo: '191101000018',
      type: 'Company'
    },
    {
      name: "COMPANY NAME",
      regNo: '1000-V',
      newRegNo: '193901000021',
      type: 'Company'
    },
    {
      name: "COMPANY NAME",
      regNo: '10000-T',
      newRegNo: '197001000604',
      type: 'Company'
    },
    {
      name: "COMPANY NAME",
      regNo: '100000-D',
      newRegNo: '198301004377',
      type: 'Company'
    },
    {
      name: "COMPANY NAME",
      regNo: '1000001-W',
      newRegNo: '201001012078',
      type: 'Company'
    },
    {
      name: "COMPANY NAME",
      regNo: '1000002-K',
      newRegNo: null,
      type: 'Company'
    },
    {
      name: "COMPANY NAME",
      regNo: '1000003-U',
      newRegNo: '201001012079',
      type: 'Company'
    },
    {
      name: "COMPANY NAME",
      regNo: '1000004-V',
      newRegNo: '201001012080',
      type: 'Company'
    },
    {
      name: "COMPANY NAME",
      regNo: '1000005-D',
      newRegNo: '201001012081',
      type: 'Company'
    },
    {
      name: "COMPANY NAME",
      regNo: '1000006-A',
      newRegNo: '201001012082',
      type: 'Company'
    },
 .......

如您所见,它不会返回从 0 到 99 的实体。我假设最高的 regNo1000000-suffixLetter,如果我可以在循环中从 100 获取到 1000000,我将获取大约 100 万个实体。但是这里的技巧regNo 有一个后缀字母。假设如果我获取 100,它会从 100-A 返回到 199-A。但还有其他实体,如100-B100-C

如何在不丢失数据的情况下高效地获取 130 万多个实体?

【问题讨论】:

  • 如果这是一次性操作,那么您使用的方法有什么问题?
  • 效率是否与推测可能探测没有条目的查询(因此毫无价值但不可避免的努力)有关,或者正如 Patrick87 所指的那样,与运行的探测次数的性能有关?
  • fetchData(100) 是否返回从 0 到 99 的所有 regNo 实体?
  • @Ben 谢谢你的回复。我更新了问题以使其更清楚。请看一下)
  • 您需要枚举regNos 的所有可能组。对于它们中的每一个,您都需要进行 API 调用并将结果附加到数据结构(或文件或数据库)中。您需要考虑避免在服务器上触发拒绝服务保护,因此将调用速率限制为在任何时候(例如)有 10 个请求在进行中。对于当前信息,这是我能提供的最好帮助。

标签: javascript database algorithm sorting fetch


【解决方案1】:

好的,看起来您正在查看 250 MB 的数据,并且请求往返大约需要 15 毫秒。假设您的下载速度为 100 Mbps,理论上您可以做到的最大速度为 20 秒。

由于这里涉及的大部分时间似乎都在等待网络往返,您可以尝试大规模并行化。您可以循环并创建多个线程,而不是让一个线程循环。在某种程度上,我希望您可以通过这样做获得几乎成比例的加速,因为您在这里几乎没有计算时间,几乎完全是 I/O。过了那个点,您可能会开始耗尽可用的网络资源并再次以这种方式遇到瓶颈。但是,您的系统应该能够同时处理许多独立的请求。

鉴于您的单线程程序每个请​​求大约需要 15 毫秒,并且可能有大约 130 万个请求,因此您会看到大约 195,000 秒以这种方式运行。使用另一个线程,如果您在 100,000 秒左右没有看到任何东西,我会感到惊讶。使用四个线程,您可能会得到大约 50,000 秒。您可能需要尝试更小的范围并切换线程数,直到看到最佳吞吐量。

注意:您正在访问的网站可能已经(或者如果您开始执行上述操作,可能会快速安装)限速器来限制来自单一来源的大量流量。在开始以这种方式使用之前,请考虑您调用的 API 是否可以处理这么多的流量。

【讨论】:

  • JavaScript 将在单个线程上进行评估,但我们可以预期不同的 fetch 调用将由线程池在运行时的幕后进行?
  • @Ben 啊,你的意思是节点默认情况下已经发送了多个重叠请求,你可以按照答案配置并行度。但真的会吗?执行fetchData 的部分在一个普通的for 循环中;代码肯定会阻止它,因为您可以在进行下一次迭代之前使用响应来跳出循环? OP在这里没有这样做,但可以。 node.js 是否进行了足够的静态分析来做出有根据的猜测,或者只是假设它应该是同步的?
  • Node.js API 将被同步调用,网络调用将被分流到底层的线程池,并异步触发回调。脚本的进度会被Promise.all 之类的东西延迟,但由于 fetch API 的异步特性,执行的主运行时线程不会被阻塞。
  • @Ben 我想我明白了,fetchData 实际上并没有发送请求,只是创建发送请求的任务......实际上是 Promise.all 开始发送跨度>