Logo
新用户订阅 RPC,首月享 6.5 折优惠查看优惠
OnFinality Learn
基础设施与运维阅读约 14 分钟

Polygon 平面文件:无需 RPC 限制的批量历史链上数据

使用 Polygon 平面文件批量回填历史区块、交易和日志数据,而不是耗尽 JSON-RPC 请求预算。

TL;DR

Polygon 平面文件是定期生成的、不可变的原始区块、交易和日志数据导出文件,存储在对象存储中,按区块范围寻址,而不是实时查询端点。它们的存在是因为通过 JSON-RPC 回填数百万个区块需要数百万次 eth_getBlockByNumber 或 eth_getLogs 调用,消耗大量请求单元预算并触发 HTTP 429 响应。平面文件将相同的数据作为少量大型顺序对象下载进行传输。使用实时 JSON-RPC 获取当前状态和最近区块,使用归档节点进行历史状态查询(如过去区块的 eth_call),使用平面文件处理您将离线批量处理的历史区块、交易和日志数据。平面文件的新鲜度仅取决于最后一次导出,是特定于网络的,其确切的存储桶布局和格式因提供商而异,必须从该提供商的文档中读取。

Polygon 平面文件究竟是什么

Polygon 平面文件是定期生成的、不可变的文件,包含 Polygon 链的原始区块、交易和日志数据。它们存储在对象存储中,通常是兼容 S3 的存储桶,并按区块范围寻址,而不是交互式查询。每次导出都是一个快照:一旦写入,文件就不会改变,下一个范围会出现一个新文件。这与 JSON-RPC 端点有本质区别,后者响应实时请求以获取当前状态和最近区块。

格式通常是 CSV 或 Parquet 风格的列式导出,但确切的模式和压缩方式因提供商而异。由于它们是文件,您可以使用标准对象存储工具下载它们,并在本地或自己的管道中解析。它们不是 RPC 的替代品;它们是用于历史分析、索引和回填的批量数据传输接口。有关数据访问接口的更广泛概述,请参阅访问历史区块链数据。

  • 不可变、定期生成的区块、交易和日志数据导出。
  • 存储在对象存储中(例如兼容 S3 的存储桶),并按区块范围寻址。
  • 不是实时查询端点;没有 eth_call 或 eth_getLogs 语义。
  • 格式和布局因提供商而异;请务必阅读提供商的文档。

平面文件存在的原因:RPC 回填问题

需要回填数百万个区块的索引器否则将发出数百万次 eth_getBlockByNumber 或 eth_getLogs 调用。每次调用都会消耗请求单元,持续的高容量读取会迅速耗尽速率限制,产生 HTTP 429 响应。JSON-RPC 2.0 规范定义了一种针对交互式查询优化的请求-响应协议,而不是批量顺序提取。以太坊 JSON-RPC 规范记录了批量提取所替代的区块、交易和日志方法。

平面文件颠覆了这种模式:您不是进行数百万次小请求,而是执行少量大型顺序对象下载。数据量相同,但请求数量减少了几个数量级,并且避免了每次请求的开销和速率限制压力。这就是为什么平面文件是历史回填的首选接口,而 RPC 仍然是获取当前状态和最近区块的正确工具。有关速率限制机制的更深入探讨,请参阅 Polygon RPC 429 和速率限制。

  • 数百万次 RPC 调用消耗请求单元并触发 429。
  • 平面文件将相同的数据作为少量大型对象下载进行传输。
  • RPC 针对交互式查询进行了优化,而不是批量顺序提取。
  • 使用平面文件进行回填;使用 RPC 获取当前状态和最近区块。

Polygon 的三条数据路径:实时 RPC、归档节点和平面文件

Polygon 暴露了三种不同的数据访问接口,选择错误的接口是浪费预算和回填失败的最常见原因。实时 JSON-RPC 提供当前状态和最近区块;它是钱包、dApp 和实时监控的正确接口。归档节点提供需要过去区块 eth_call 的历史状态查询,例如检查特定高度的代币余额。平面文件提供您将离线批量处理的历史区块、交易和日志数据。

这种区别很重要,因为归档节点和平面文件不可互换。归档节点可以回答任何区块的状态查询,但通过它查询数百万个区块仍然会产生每次请求的成本和速率限制。平面文件根本无法回答状态查询,但它们能更高效地传递历史区块和日志数据。有关节点类型的详细比较,请参阅归档节点与全节点和 Polygon 归档节点与历史 RPC。

  • 实时 JSON-RPC:当前状态、最近区块、实时 dApp。
  • 归档节点:历史状态查询(过去区块的 eth_call)。
  • 平面文件:用于批量离线处理的历史区块、交易和日志数据。

实用决策表:选择正确的接口

使用此表将任务映射到最佳数据接口。目标是避免将实时 RPC 端点用于批量历史提取,并避免将平面文件用于任何需要状态或链尖的内容。该表反映了已记录的行为;提供商特定的细节(如存储桶布局和导出频率)各不相同,应在提供商的文档中确认。

当任务跨越多个接口时,将其拆分:使用平面文件进行历史批量处理,使用 RPC 处理最近的尾部数据和任何状态查找。这种混合模式可以保持较低的请求单元消耗,同时保持状态相关逻辑的正确性。

  • 回填数百万个区块的交易/日志 → 平面文件 → 避免数百万次 RPC 调用和 429。
  • 查询当前代币余额 → 实时 JSON-RPC → 状态只能通过 RPC 或数据库获得。
  • 检查区块 N 的历史余额 → 归档节点 → 平面文件不包含状态。
  • 为仪表板流式传输最近区块 → 实时 JSON-RPC → 平面文件落后于链尖。
  • 构建历史分析数据集 → 平面文件 → 批量顺序下载效率高。
  • 解决链尖附近的 reorg → 实时 JSON-RPC → 平面文件是不可变快照,不是实时的。

平面文件的布局和枚举方式

平面文件按区块范围组织,并存储在对象存储桶中的前缀下。要枚举它们,请列出目标区块范围的存储桶,下载相关键,必要时解压缩,并流式解析内容,而不是将所有内容加载到内存中。确切的键结构、文件命名和压缩方式因提供商而异,因此请务必阅读提供商的文档以获取权威布局。Polygon 的开发者文档位于 docs.polygon.technology,描述了数据访问接口和 RPC 端点,而平面文件存储桶的具体细节由提供商定义。

典型的工作流程是:确定所需的区块范围,列出覆盖该范围的存储桶前缀,下载每个对象,并逐行或逐行解析。由于文件是不可变的,您可以在本地缓存它们并重新处理而无需重新下载。对于大范围,在您的带宽和内存限制内并行处理文件,但保持解析流式以避免内存不足错误。

  • 列出区块范围的存储桶前缀以发现可用文件。
  • 按键下载;文件不可变,可以在本地缓存。
  • 如果提供商存储压缩导出,则解压缩。
  • 流式解析,而不是将整个文件加载到内存中。
  • 在提供商的文档中确认确切的布局和格式。

可运行示例:列出并流式解析平面文件对象

以下 Node.js 示例列出了区块范围的兼容 S3 的平面文件前缀,下载一个对象,并流式解析它以计算交易数量。它使用 AWS SDK v3,可与任何兼容 S3 的端点一起使用。将存储桶、前缀和凭证替换为您的提供商的值。代码假定换行分隔的 JSON 或 CSV;调整解析器以匹配提供商的格式。

此示例演示了核心模式:枚举、下载、流式解析。它不会将整个文件加载到内存中,这对于大型导出至关重要。对于完整的生产管道,请在您的资源限制内添加错误处理、重试和并行下载。

const { S3Client, ListObjectsV2Command, GetObjectCommand } = require('@aws-sdk/client-s3');
const { createGunzip } = require('zlib');
const readline = require('readline');

const s3 = new S3Client({
  region: 'us-east-1',
  endpoint: 'https://your-s3-compatible-endpoint',
  credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY },
  forcePathStyle: true,
});

async function listFlatFiles(bucket, prefix) {
  const res = await s3.send(new ListObjectsV2Command({ Bucket: bucket, Prefix: prefix }));
  return (res.Contents || []).map((o) => o.Key);
}

async function streamCountTransactions(bucket, key) {
  const res = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
  const stream = res.Body.pipe(createGunzip());
  const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
  let txCount = 0;
  for await (const line of rl) {
    if (!line.trim()) continue;
    const row = JSON.parse(line);
    if (row.transactionHash) txCount++;
  }
  return txCount;
}

(async () => {
  const bucket = 'your-flat-file-bucket';
  const prefix = 'polygon/blocks/19000000/'; // adjust to provider layout
  const keys = await listFlatFiles(bucket, prefix);
  console.log('Found files:', keys.length);
  if (keys.length) {
    const count = await streamCountTransactions(bucket, keys[0]);
    console.log('Transactions in', keys[0], ':', count);
  }
})();

对比:等效的 RPC 循环及其成本

为了理解差异,请考虑前一个示例的 RPC 等效项。通过 JSON-RPC 回填相同的区块范围将需要每个区块一次 eth_getBlockByNumber 调用,如果需要日志则还需要额外的调用。对于一百万个区块的范围,就是一百万次请求,每次消耗请求单元并计入速率限制。以太坊 JSON-RPC 规范记录了这些方法,而 JSON-RPC 2.0 规范定义了请求-响应模型,使得逐区块读取对于批量提取效率低下。

以下代码片段显示了 RPC 循环模式。它对于小范围或最近区块是正确的,但如果不遇到 HTTP 429 响应,它无法扩展到数百万个区块。将其用于最近的尾部数据,而不是历史回填。

const { ethers } = require('ethers');

async function rpcBackfill(providerUrl, fromBlock, toBlock) {
  const provider = new ethers.JsonRpcProvider(providerUrl);
  let txCount = 0;
  for (let n = fromBlock; n <= toBlock; n++) {
    const block = await provider.send('eth_getBlockByNumber', ['0x' + n.toString(16), false]);
    if (block && block.transactions) txCount += block.transactions.length;
  }
  return txCount;
}

// For a large range, this loop will consume enormous request units
// and likely trigger HTTP 429 responses. Use flat files instead.

可复现结果表:衡量您自己的管道

为了做出明智的决策,请根据您自己的端点和提供商衡量您自己的管道。下表是一个模板:用您自己的测量值填充它。不要依赖供应商基准或第三方数字;重要的变量(区块范围、文件大小、网络带宽、提供商速率限制)特定于您的环境。在平面文件和 RPC 上运行相同的逻辑任务,并记录结果。

使用该表比较墙钟时间、传输字节数和 HTTP 429 计数。目标不是证明某个接口总是更快,而是量化您工作负载的权衡。有关 RPC 请求单元的定价影响,请参阅 RPC 定价。

  • 任务:描述操作(例如,计算区块 19,000,000–19,100,000 中的交易)。
  • 方法:平面文件或 RPC 循环。
  • 覆盖区块:确切范围。
  • 墙钟时间:总经过时间(秒)。
  • 字节数:传输的总数据量。
  • HTTP 429:遇到的速率限制响应数量。

平面文件的局限性和权衡

平面文件的新鲜度仅取决于最后一次导出。它们无法服务链尖,也无法回答状态查询,例如过去区块的 eth_call。它们是特定于网络的:Polygon 平面文件适用于 Polygon,不适用于其他链。模式仅在文档化的版本内稳定;如果提供商更改格式,您的解析器必须适应。确切的存储桶布局、文件命名和压缩方式因提供商而异,必须从该提供商的文档中读取。

您仍然需要 RPC 或数据库来获取状态、内存池以及比最新导出更新的任何内容。平面文件是批量历史数据接口,不是通用查询引擎。对于状态相关逻辑,请将平面文件与归档节点或实时 RPC 端点配对。有关节点类型的比较,请参阅归档节点与全节点。

  • 新鲜度仅取决于最后一次导出;无法服务链尖。
  • 特定于网络;不可跨链移植。
  • 模式仅在文档化的版本内稳定。
  • 存储桶布局和格式因提供商而异。
  • 不覆盖状态、内存池或最近区块。

排查常见的平面文件和 RPC 问题

当平面文件管道失败时,原因通常是提供商的布局与您的假设不匹配。如果列出返回没有键,请根据提供商的文档验证前缀和区块范围。如果解析失败,请检查压缩和格式;将 gzip 流馈送到 CSV 解析器会产生错误。如果下载缓慢,请检查您的带宽并考虑在限制内并行下载。如果您看到 HTTP 429 响应,您可能仍在将 RPC 用于批量提取;将历史范围切换为平面文件。

对于 RPC 特定问题,例如速率限制和 429 处理,请参阅 Polygon RPC 429 和速率限制。对于归档节点状态查询,请参阅 Polygon 归档节点与历史 RPC。有关一般 RPC 指南,请参阅 Polygon RPC 指南(RPC Assistant)。

  • 没有列出键:在提供商文档中验证前缀和区块范围。
  • 解析错误:在解析前确认压缩和格式。
  • 下载缓慢:检查带宽;在限制内并行化。
  • HTTP 429:您可能正在使用 RPC 进行批量提取;切换到平面文件。
  • 状态查询失败:平面文件不包含状态;使用归档节点。

后续步骤:将平面文件集成到您的技术栈中

首先确定您需要的历史区块范围以及其平面文件覆盖该范围的提供商。阅读该提供商的文档以了解存储桶布局、格式和导出频率。构建一个小型管道,列出、下载并流式解析一个文件,然后扩展到您的完整范围。使用上面的结果表衡量您的管道,并与相同范围的 RPC 循环进行比较。使用平面文件进行批量历史加载,并将 RPC 保留用于最近的尾部数据和状态查询。

有关网络特定细节,请参阅 Polygon 网络页面。有关 API 和服务选项,请参阅 API 服务。有关更多指南,请访问 OnFinality Learn 中心。有关定价影响,请参阅 RPC 定价。

  • 确定历史区块范围以及其平面文件覆盖该范围的提供商。
  • 阅读提供商的文档以了解布局、格式和频率。
  • 构建一个最小的列出-下载-解析管道,然后扩展。
  • 使用结果表进行衡量,并与 RPC 进行比较。
  • 使用平面文件进行批量历史处理;使用 RPC 处理最近的尾部数据和状态。

永远不用担心基础设施

OnFinality 消除了 DevOps 的繁重工作,让您能够更聪明、更快地构建。

开始