---
title: "我們如何重建歷史日誌以提升速度"
description: "我們重建了 eth_getLogs 背後的儲存與查詢引擎，讓大範圍歷史日誌讀取在每個地區的 p99 速度提升超過 2 倍。"
---

# 我們如何重建歷史日誌以提升速度

<ImageBlock
  src="https://media.alchemy.com/blog/how-we-rebuilt-historical-logs-for-speed-16x9-20260821.png"
  alt="我們如何重建 historical logs 以提升速度"
  width={1920}
  height={1080}
  priority
/>

問問任何一個在做 indexer、DEX aggregator 或鏈上監控系統的團隊，他們的應用程式在哪裡會變慢，答案幾乎都一樣：大範圍的 `eth_getLogs`。

這是一種掃描數千甚至數十萬個區塊來回答單一問題的呼叫。上個月有哪些 transfer 打到這個合約？這個範圍內有哪些 pool 改變了狀態？這個地址自上線以來觸發了哪些事件？這些讀取是真實應用程式的骨幹，也是最容易卡住的讀取。所以我們重建了服務它們的引擎，讓這個方法在每個地區的 p99 都快了超過兩倍。以下說明我們是怎麼做的。

## eth_getLogs 是什麼，為什麼重要？

`eth_getLogs` 是一個 JSON-RPC 方法，回傳智慧合約在執行過程中發出的事件：token transfer、swap、approval，或是合約自訂的任何事件。你可以依合約地址、事件簽章或索引過的 topic 值（例如 `Transfer` 或 `Approval`）過濾，並用 `fromBlock` 和 `toBlock` 指定區塊範圍。簡單說：告訴它要找什麼、要往回找多遠，它就會回傳所有符合的事件。

這是 Ethereum 上事件驅動索引的基礎。沒有它，你就得逐一掃描每一筆交易來找出你關心的那些。有了它，你就能建立活動與 transfer feed、approval 監控，以及任何即時或歷史事件索引器。這是我們所服務的方法中，使用最頻繁、營運上最關鍵的方法之一。如果你對 log 和 topic 還不熟悉，我們的[深入探討 `eth_getLogs`](https://www.alchemy.com/docs/reference/eth-getlogs) 用實例完整說明了請求與回應的結構。

這個方法的其餘部分則塑造了整個工程難題。許多服務商會限制單次查詢能涵蓋的區塊數量。Indexer 必須靠等待確認來處理鏈重組（reorg）。Topic 過濾只能比對已索引的欄位，非索引的參數則要從原始資料中解碼。而靠近鏈頭的呼叫對延遲很敏感，路由到錯誤的節點類型可能導致錯誤或空回應。

其中最影響延遲的部分是範圍大小。查詢單一區塊幾乎是瞬間完成。但對於重播歷史的 indexer，或是持續監控合約數週的監控系統來說，範圍很快就會變大，查詢成本也隨之攀升。這正是延遲從可忽略的誤差，變成限制客戶開發速度的瓶頸的地方。

大家都提供這個方法。但很少有人談論它背後的引擎，因為重建這個引擎成本高昂，多數服務商並沒有這麼做。

## 為什麼 p99 延遲比平均延遲更重要？

真正造成傷害的數字不是平均值，而是 p99：把所有請求從最快排到最慢，p99 就是那百分之一中最慢的一個。平均值會把它藏起來。你可能有 99 個請求在 60 毫秒內完成，但第一百個卻花了整整一秒。

那個最慢的請求，就是讓 dashboard 卡住、或讓 indexer 落後於鏈的那一個，也正是你的使用者盯著看的那一個。平均延遲看起來可能沒問題，但尾端延遲卻悄悄地拖垮整個工作負載。對於運行 indexer 和監控系統的團隊來說，尾端延遲就是工作負載本身。

## 我們如何優化 eth_getLogs？

我們重建了服務這個方法的儲存與查詢引擎，而不是去調校原本既有的系統。

要讓這樣的呼叫變快，有幾種做法：調整查詢參數、加上快取、限制可接受的範圍，或是砸更多同類型的硬體。大多數做法只能換來一點餘裕，並沒有從根本改變什麼。

我們做得更深入。我們沒有依賴把歷史 log 讀取視為一般工作負載的通用型受管理資料庫，而是運行了一套專門針對 `getLogs` 存取模式調校的儲存與查詢層：在非常龐大的資料集上進行快速的過濾掃描，並且跑在我們自行營運的專屬基礎設施上。

我們沒有調一個旋鈕，也沒有在舊系統上外掛一個快取。我們是在方法層級上投入資源，針對客戶最關鍵的呼叫之一背後的引擎進行投資。這是昂貴的、以方法為單位的基礎設施工程，也是為什麼改善會體現在尾端，而不只是平均值上。

## 結果如何？

重點很簡單：在每個地區，p99 都快了超過兩倍。

如果你運行的是 indexer 或監控系統，p99 就是拖垮你工作負載的延遲。更好的平均值固然不錯，但更好的尾端延遲才能讓 dashboard 保持反應靈敏、讓 indexer 跟得上鏈的進度。

已經有以嚴肅規模運行這套系統的團隊看到了成效。其中一家最大的加密貨幣交易所，把他們的歷史 log 讀取效能拿我們的引擎跟他們自己運行的節點做了基準測試，並在他們自己的工作負載上量測到 p99 有大幅改善。這是在生產環境中、用客戶自己的量測工具得出的證據，而不只是我們自己 dashboard 上的數字。

## 這對你來說意味著什麼？

如果你的應用程式依賴歷史 log 讀取來做索引、彙整或監控，你會得到更快的讀取速度，以及在每個地區都有大幅改善的 p99 尾端延遲，而且是跑在我們專為這個方法重建的基礎設施上。以下幾點值得你在實際使用時留意：

1. 在 pay-as-you-go 和 enterprise 方案中，`eth_getLogs` 在 Ethereum、Base、Optimism、Arbitrum、Polygon、Robinhood 及其他主要鏈上都支援單次查詢不限區塊範圍，所以重播歷史不需要自己手動做分頁。
2. 對於鏈頭附近的 log，[WebSocket subscriptions](https://www.alchemy.com/docs/reference/subscription-api) 會在新 log 一產生時就推送給你，這樣你就能把 `eth_getLogs` 保留給它本來設計要處理的歷史讀取。
3. 你的整合方式完全不需要改變。方法一樣、請求格式一樣，只是背後換了更快的引擎在服務。

在方法層級投入資源的意義在於：定義你工作負載的那些讀取，正是我們調校這套引擎所針對的對象。[建立免費的 Alchemy 帳號](https://dashboard.alchemy.com/signup)，把你的 indexer 指向我們，親自用你自己的量測工具看看差異。

## 常見問題

### eth_getLogs 會回傳什麼？

一個符合你過濾條件的事件 log 陣列：合約地址、事件簽章與索引過的 topic 值，以及區塊範圍。每一筆 log 都包含發出事件的地址、topics、資料內容，以及它所屬的區塊與交易。完整的請求與回應規格請參考 [API reference](https://www.alchemy.com/docs/reference/eth-getlogs)。

### 為什麼對 indexer 來說，p99 延遲比平均延遲更重要？

因為 indexer 是依序或分批處理請求，最慢的請求會決定整體步調。平均值很低但尾端很長，代表 indexer 經常卡在那最慢的百分之一上，落後於鏈頭，並回傳過時的資料。p99 測量的正是這些請求。

### 在 Alchemy 上，單次 eth_getLogs 呼叫可以查詢多大的區塊範圍？

在免費方案中，每次查詢限制為 10 個區塊。在 pay-as-you-go 和 enterprise 方案中，Ethereum、Base、Optimism、Arbitrum、Polygon、Worldchain、BNB 以及其他數條鏈的區塊範圍不設上限，回應大小上限為 150MB。完整的各鏈對照表請見 [`eth_getLogs` reference](https://www.alchemy.com/docs/reference/eth-getlogs)。

### 我該如何讓大範圍的歷史查詢維持快速？

盡可能縮小過濾範圍：指定合約地址與所需的事件 topic，而不只是區塊範圍。對於不支援不限範圍的鏈，遇到非常大的範圍時，把查詢拆成較小的區塊分批處理。至於未來的新 log，請改用 [WebSocket subscriptions](https://www.alchemy.com/docs/reference/subscription-api)，而不是用輪詢的方式。
