SubQuery事件驱动架构详解:从区块同步到数据持久化

SubQuery事件驱动架构详解:从区块同步到数据持久化

【免费下载链接】subql SubQuery is an Open, Flexible, Fast and Universal data indexing framework for web3. Our mission is to help developers create the decentralised products of the future. 【免费下载链接】subql 项目地址: https://gitcode.com/gh_mirrors/su/subql

引言:解决区块链数据索引的痛点

你是否还在为区块链数据索引的低效率而烦恼?是否在面对海量区块数据时感到无从下手?本文将带你深入了解SubQuery的事件驱动架构,从区块同步到数据持久化,全面解析SubQuery如何高效处理区块链数据,帮助你轻松构建去中心化应用。

读完本文,你将能够:

  • 理解SubQuery事件驱动架构的核心组件
  • 掌握区块数据从同步到持久化的完整流程
  • 了解SubQuery如何处理高并发和数据一致性
  • 学会使用SubQuery构建高效的区块链数据索引服务

SubQuery架构概览

SubQuery是一个开源、灵活、快速且通用的Web3数据索引框架。其事件驱动架构设计使其能够高效地从区块链中提取、转换和存储数据,为去中心化应用提供强大的数据支持。

核心组件

SubQuery的事件驱动架构主要由以下几个核心组件构成:

  1. 区块获取服务(Fetch Service):负责从区块链节点获取区块数据
  2. 区块调度器(Block Dispatcher):管理区块处理队列,协调区块数据的处理流程
  3. 索引器(Indexer):处理区块数据,提取事件和交易信息
  4. 存储服务(Store Service):负责数据的持久化存储
  5. 查询服务(Query Service):提供GraphQL接口,供前端应用查询数据

SubQuery架构示意图

区块同步:数据入口的高效处理

区块同步是SubQuery架构的第一步,负责从区块链网络获取区块数据并将其传递给后续处理流程。

Fetch Service:区块数据的获取者

Fetch Service通过区块链节点的RPC接口获取区块数据。它维护了两个关键高度:最新 finalized 高度和最新 best 高度,以确保数据的准确性和实时性。

// packages/node-core/src/indexer/fetch.service.ts
async getFinalizedBlockHead(): Promise<void> {
  try {
    const currentFinalizedHeader = await this.blockchainSevice.getFinalizedHeader();
    if (this._latestFinalizedHeight === undefined || currentFinalizedHeader.blockHeight > this._latestFinalizedHeight) {
      this._latestFinalizedHeight = currentFinalizedHeader.blockHeight;
      this.unfinalizedBlocksService.registerFinalizedBlock(currentFinalizedHeader);
      if (!this.nodeConfig.unfinalizedBlocks) {
        this.eventEmitter.emit(IndexerEvent.BlockTarget, {
          height: this.latestFinalizedHeight,
        });
      }
    }
  } catch (e: any) {
    logger.error(e, `Having a problem when getting finalized block`);
  }
}

Fetch Service还实现了高效的区块获取策略,包括:

  • 批量获取区块以提高效率
  • 使用字典服务(Dictionary Service)快速定位包含目标事件的区块
  • 支持跳过指定区块,提高索引灵活性

区块调度:高效的任务管理

获取到区块数据后,Block Dispatcher负责管理区块处理队列,确保数据能够有序、高效地被处理。

// packages/node-core/src/indexer/blockDispatcher/block-dispatcher.ts
async fillNextBlockBuffer(initBlockHeight: number): Promise<void> {
  let startBlockHeight: number;
  let scaledBatchSize: number;

  const getStartBlockHeight = (): number => {
    return this.blockDispatcher.latestBufferedHeight
      ? this.blockDispatcher.latestBufferedHeight + 1
      : initBlockHeight;
  };

  while (!this.isShutdown) {
    startBlockHeight = getStartBlockHeight();
    scaledBatchSize = this.blockDispatcher.batchSize;
    const latestHeight = this.latestHeight();

    if (this.blockDispatcher.freeSize < scaledBatchSize || startBlockHeight > latestHeight) {
      await delay(1);
      continue;
    }

    // 区块获取和处理逻辑
    // ...
  }
}

Block Dispatcher采用了双队列设计:

  • 一个用于管理区块获取(fetch queue)
  • 一个用于管理区块处理(process queue)

这种设计可以有效平衡区块获取和处理的速度,避免出现内存溢出或处理瓶颈。

事件处理:核心业务逻辑的实现

区块数据被调度后,Indexer组件负责提取和处理其中的事件和交易数据。这是SubQuery架构中最核心的部分,也是用户自定义业务逻辑的主要入口点。

数据处理流程

  1. 区块解析:解析区块数据,提取交易和事件
  2. 数据过滤:根据用户定义的规则过滤相关数据
  3. 数据转换:将原始区块链数据转换为用户定义的数据模型
  4. 数据存储:将处理后的数据存入数据库

自定义业务逻辑

SubQuery允许用户通过自定义映射函数(mapping functions)来定义如何处理区块链事件。这些函数可以通过CLI工具自动生成框架代码,用户只需填充业务逻辑即可。

// 示例:处理ERC20转账事件的映射函数
export async function handleTransfer(event: TransferEvent): Promise<void> {
  // 创建新的转账记录
  const transfer = new TransferRecord(event);
  
  // 填充数据
  transfer.from = event.from;
  transfer.to = event.to;
  transfer.value = event.value;
  transfer.blockNumber = event.block.number;
  transfer.timestamp = new Date(event.block.timestamp);
  
  // 保存到数据库
  await transfer.save();
}

数据持久化:确保数据安全和高效查询

处理后的区块链数据需要被持久化存储,以便后续查询。SubQuery采用了PostgreSQL作为主要数据存储引擎,并实现了一系列优化来确保数据查询的高效性。

Store Service:数据持久化的核心

Store Service负责管理数据的存储和检索,提供了一系列优化来提高性能和确保数据一致性。

// packages/node-core/src/indexer/store.service.ts
async initCoreTables(schema: string): Promise<void> {
  if (this.config.proofOfIndex) {
    const usePoiFactory = (await this.useDeprecatePoi(schema)) ? PoiFactoryDeprecate : PoiFactory;
    this.poiRepo = usePoiFactory(this.sequelize, schema);
  }

  this._schema = schema;
  await this.setMultiChainProject();

  this._metaDataRepo = await MetadataFactory(
    this.sequelize,
    schema,
    this.isMultichain,
    this.subqueryProject.network.chainId
  );

  if (this.isMultichain) {
    this._globalDataRepo = GlobalDataFactory(this.sequelize, schema);
  }

  await this.sequelize.sync();

  this._historical = await this.getHistoricalStateEnabled(schema);
  logger.info(`Historical state is ${this.historical || 'disabled'}`);

  this.modelProvider.init(this.historical, this.metaDataRepo, this.poiRepo);
  this._metadataModel = this.modelProvider.metadata;

  await this.initHotSchemaReloadQueries(schema);
  await this.metadataModel.set('historicalStateEnabled', this.historical);
}

历史数据和数据一致性

SubQuery支持历史数据查询,通过__block_range字段记录数据的生命周期,使得用户可以查询特定区块高度的数据状态。

// packages/node-core/src/indexer/store.service.ts
if (this.historical) {
  sequelizeModel.addHook('beforeFind', (options) => {
    (options.where as any).__block_range = {
      [Op.contains]: this.getHistoricalUnit(),
    };
  });
  sequelizeModel.addHook('beforeValidate', (attributes, options) => {
    attributes.__block_range = [this.getHistoricalUnit(), null];
  });
}

此外,SubQuery还实现了Proof of Index(PoI)机制,通过加密哈希确保数据的完整性和一致性,防止数据被篡改。

性能优化:应对高并发和大数据量

SubQuery架构中内置了多种性能优化机制,以应对区块链网络的高并发和大数据量挑战。

并行处理

SubQuery使用工作线程(worker threads)实现区块数据的并行处理,充分利用多核CPU资源。

// packages/node-core/src/indexer/blockDispatcher/worker-block-dispatcher.ts
private async processBlocks(blocks: IBlock<FB>[]): Promise<void> {
  const worker = this.getWorker();
  if (!worker) {
    throw new Error('No available workers');
  }

  try {
    const result = await worker.processBlocks(blocks);
    this.handleProcessingResult(result, blocks);
  } finally {
    this.releaseWorker(worker);
  }
}

缓存策略

为了减少数据库访问压力,SubQuery实现了多级缓存机制:

  1. 内存缓存:缓存热点数据,减少数据库查询
  2. 查询缓存:缓存重复查询结果
  3. 预加载:预加载可能需要的数据,提高响应速度

数据库优化

SubQuery对数据库进行了多方面优化:

  1. 索引优化:自动为常用查询字段创建索引
  2. 批量操作:将多个数据库操作合并为批量操作
  3. 连接池:管理数据库连接,提高并发处理能力

实际应用:构建高效的区块链应用

SubQuery的事件驱动架构使其非常适合构建各种区块链应用,包括:

  1. 区块链浏览器:高效查询区块、交易和账户数据
  2. 去中心化金融(DeFi)应用:实时跟踪资产价格和流动性数据
  3. NFT市场:索引和查询NFT元数据和交易历史
  4. 链上分析工具:分析区块链活动和趋势

快速开始

要开始使用SubQuery,只需几个简单步骤:

  1. 安装CLI工具
npm install -g @subql/cli
  1. 初始化项目
subql init --name my-project --network ethereum
cd my-project
  1. 定义数据模型:编辑schema.graphql文件定义数据结构
  2. 编写映射函数:编辑src/mappings目录下的文件实现事件处理逻辑
  3. 构建和部署
yarn build
yarn start:docker

总结与展望

SubQuery的事件驱动架构为区块链数据索引提供了一个高效、灵活和可扩展的解决方案。通过分离区块获取、事件处理和数据存储等环节,SubQuery能够轻松应对不同区块链网络的特点和挑战。

未来,SubQuery将继续优化性能,支持更多区块链网络,并提供更丰富的数据处理和分析功能,帮助开发者构建更强大的去中心化应用。

如果你对SubQuery感兴趣,可以通过以下资源深入学习:

  • 官方文档README.md
  • 代码仓库:https://gitcode.com/gh_mirrors/su/subql
  • 社区支持:加入SubQuery Discord社区获取帮助和最新资讯

SubQuery正在改变我们与区块链数据交互的方式,为Web3开发者提供强大的数据基础设施。无论你是构建简单的区块链浏览器还是复杂的DeFi应用,SubQuery都能帮助你快速、高效地获取和处理区块链数据,让你专注于构建核心业务逻辑。

【免费下载链接】subql SubQuery is an Open, Flexible, Fast and Universal data indexing framework for web3. Our mission is to help developers create the decentralised products of the future. 【免费下载链接】subql 项目地址: https://gitcode.com/gh_mirrors/su/subql

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值