> ## Documentation Index
> Fetch the complete documentation index at: https://docs.intention.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# 索引器

> 已提交的区块如何变成可查询的数据——从全节点到 REST 与 WebSocket 接口的流式路径。

链提交的是区块。而应用问的是*这个账户上周成交了什么*、*订单簿现在长什么样*——以区块为形态的记录很难回答这类问题。索引器就是把前者变成后者的那一层。

在大多数链上，索引器都是基础设施里的关键一环：链发出的是不透明的事件，由第三方从中重建含义，于是你的应用信什么，取决于它信哪个索引器。在这里，重建这一步根本不存在。[内核](/zh/protocol/architecture/kernel)发出的是带类型、带归属的输出——每一次状态变更都已经绑定到引起它的那笔交易——所以索引器只是把数据重新塑形，不去推断。

这改变了索引器的定位：它是**查询服务层**，不是事实来源。它报出来的任何东西都可以对着链核验，一旦出现分歧，那就是索引器的缺陷，不是悬而未决的问题。

<h2 id="the-path">
  路径
</h2>

<div className="dg" data-dg="indexer-path">
  <div className="dg-c" style={{aspectRatio:"720 / 318"}}>
    <svg className="dg-w" viewBox="0 0 720 318" aria-hidden="true">
      <path className="dg-wire dg--blue dg-soft" d="M 123.00 102.00 L 155.98 75.97" />

      <path className="dg-head dg--blue" d="M 161.00 72.00 L 158.70 79.42 L 153.25 72.51 Z" />

      <path className="dg-wire dg--blue dg-soft" d="M 123.00 102.00 L 156.23 131.73" />

      <path className="dg-head dg--blue" d="M 161.00 136.00 L 153.30 135.01 L 159.16 128.45 Z" />

      <path className="dg-wire dg--sky dg-soft" d="M 349.00 72.00 L 381.98 98.03" />

      <path className="dg-head dg--sky" d="M 387.00 102.00 L 379.25 101.49 L 384.70 94.58 Z" />

      <path className="dg-wire dg--sky dg-soft" d="M 349.00 136.00 L 382.23 106.27" />

      <path className="dg-head dg--sky" d="M 387.00 102.00 L 385.16 109.55 L 379.30 102.99 Z" />

      <path className="dg-wire dg--green" d="M 536.00 102.00 L 551.60 102.00" />

      <path className="dg-head dg--green" d="M 558.00 102.00 L 551.60 106.40 L 551.60 97.60 Z" />
    </svg>

    <div className="dg-band" style={{left:"20.8333%",top:"8.1761%",width:"29.1667%",height:"47.7987%"}}><span className="dg-cap">按新旧分开</span></div>
    <div className="dg-b dg--blue" style={{left:"0.0000%",top:"19.4969%",width:"16.3889%",height:"25.1572%"}}><span className="dg-t">全节点</span><span className="dg-s">已提交区块，以带类型的记录流出</span></div>
    <div className="dg-b dg--sky" style={{left:"23.0556%",top:"14.4654%",width:"24.7222%",height:"16.3522%"}}><span className="dg-t">缓存</span><span className="dg-s">近期 — 从内存提供</span></div>
    <div className="dg-b dg--sky" style={{left:"23.0556%",top:"34.5912%",width:"24.7222%",height:"16.3522%"}}><span className="dg-t">文件存储</span><span className="dg-s">历史 — 持久化</span></div>
    <div className="dg-b dg--sky" style={{left:"54.4444%",top:"19.4969%",width:"19.4444%",height:"25.1572%"}}><span className="dg-t">数据服务</span><span className="dg-s">把两者呈现为一条流</span></div>
    <div className="dg-b dg--green" style={{left:"78.0556%",top:"19.4969%",width:"21.9444%",height:"25.1572%"}}><span className="dg-t">网关 → REST · WebSocket</span><span className="dg-s">访问控制、配额与路由</span></div>
    <div className="dg-b dg-dashed dg-left" style={{left:"0.0000%",top:"67.2956%",width:"48.8889%",height:"27.6730%"}}><span className="dg-t">为什么要拆开</span><span className="dg-s">跟随链头对延迟敏感、数据量小；历史查询对吞吐敏感、数据量大。共用一条路径，一次回填就能把实时那条拖住。</span></div>
    <div className="dg-b dg-dashed dg-left" style={{left:"51.1111%",top:"67.2956%",width:"48.8889%",height:"27.6730%"}}><span className="dg-t">查询服务层，不是事实来源</span><span className="dg-s">内核发出的是带类型、带归属的输出，所以索引器只是重新塑形，不去推断。与链不一致，那就是索引器的缺陷。</span></div>
  </div>
</div>

**全节点**是起点。交易记录从这里以带类型的数据流出，不是还需要再解释一遍的原始交易。

**缓存与文件存储**按数据新旧把这条流分开。近期数据从内存提供，因为多数消费者要的就是它，而且延迟很重要。历史数据写进持久化的文件存储，因为把一切都留在内存里算不上一种策略。来取旧数据的消费者和跟随链头的消费者，实际由不同的地方服务，而两边谁也感觉不到。

**数据服务**把两者呈现为一条流。客户端可以请求从任意位置开始的区间；这个区间由缓存提供、由文件提供，还是两者共同提供，不是客户端要操心的事。

**网关**处理介于服务与公网之间的那些事：访问控制、配额和路由。

**REST 与 WebSocket 接口**才是应用实际使用的东西——行情数据、订单与成交历史、仓位、账户状态、资金费支付，以及实时订阅。端点级别的细节见 [API 参考](https://testnet-openapi.intention.xyz/)。

<h2 id="why-the-split-exists">
  为什么要拆开
</h2>

一个服务既要跟随链头，又要回答历史查询，两件事都做不好。跟随链头对延迟敏感，数据量小；历史查询对吞吐敏感，数据量大，一次大规模回填就能把实时路径拖住。

分开之后，给一个新消费者回填几个月前的数据，不会拖慢正跟着链头的做市商那条流；两者还能各自独立扩容——也确实需要，因为它们的负载特征毫无共同之处。

<h2 id="what-it-is-safe-to-rely-on">
  什么可以放心依赖
</h2>

**可以依赖**：索引器提供的、由已提交区块派生出来的一切——成交、订单、仓位、资金费支付、转账、行情数据。这些都是链上输出重新塑形的结果。

**不是一回事**：任何尚未提交的东西。订单被[内存池](/zh/protocol/architecture/mempool)接受了，还没有被排序，索引器对它无话可说。在索引器里查不到，意思是“还没有被提交”，不是“被拒绝了”。

**可核验**：如果某个答案足够重要——一次结算争议、一次审计、一次账务对账——那就直接对着链核验，不必从索引器里取。自己跑一个全节点是这件事最彻底的形式，也是那些承担不起出错代价的参与者应该做的。见[运行节点](/zh/developers/run-a-node)。

<Note>
  两个消费者读同一段已提交区间，应该得到同样的答案。如果不一样，差异出在服务链路上，是应当上报的缺陷——不是“透过索引器读链”这件事的固有属性。
</Note>

<h2 id="where-to-go-next">
  后续阅读
</h2>

<CardGroup cols={2}>
  <Card title="状态模型" href="/zh/protocol/architecture/state/model">
    索引器读的是什么，以及哪一种表示是权威的。
  </Card>

  <Card title="开发者" href="/zh/developers/overview">
    REST 与 WebSocket 接口、SDK 与工具。
  </Card>

  <Card title="运行节点" href="/zh/developers/run-a-node">
    自己对着链核验，以及这个集合为什么是封闭的。
  </Card>

  <Card title="计划服务" href="/zh/protocol/architecture/programs">
    谁在消费这条流来计算派生的账户状态。
  </Card>
</CardGroup>
