---
title: "Alchemy 如何在 Solana 上打造最快的歸檔方法"
description: "了解讓我們的歸檔方法成為目前市場上最快解決方案的架構。"
---

# Alchemy 如何在 Solana 上打造最快的歸檔方法

<ImageBlock
  src="https://media.alchemy.com/1771818629-blog-archival-methods-solana.png"
  alt="顯示 Solana 上歷史資料的圖表"
  width={5760}
  height={2700}
  priority
/>

去年年底，我們[發布了全新的 Solana 產品](https://www.alchemy.com/blog/solana-infrastructure)，其中一項關鍵功能就是我們封存方法（archival methods）的效能：比市面上任何其他解決方案快up to 20x。

透過我們的 Solana 服務，開發者可以用標準 JSON RPC 介面以前所未有的速度查詢歷史交易、區塊和簽名。這個功能並不容易打造，這篇文章會說明我們是如何做到的。

## 什麼是 Solana 上的封存方法？

在 Solana 中，封存方法指的是用來取得歷史區塊鏈資料的 RPC 呼叫，例如 `getTransaction`、`getBlock` 和 `getSignaturesForAddress`。

這些封存端點是鏈上應用的基礎。錢包、索引器、分析服務提供商、區塊瀏覽器等等，都仰賴這些歷史資料來為使用者提供更豐富的資訊、打造完整的使用體驗。

然而，在 Solana 上取得封存資料一向十分困難。資料集非常龐大、彼此高度關聯，查詢時的運算負擔也很重。對大多數開發者來說，這些端點正是延遲與不一致問題最先出現、也最嚴重的地方。

## Solana 封存方法的效能問題

目前大多數 Solana 基礎設施都建立在 Google Bigtable 加上基於 validator 的 RPC 節點之上。

這套架構部署容易（這也是許多團隊選用它的原因），但並非為規模化而設計。它耗費大量 CPU 與記憶體，處理大批次請求時也力不從心。在大規模查詢時，這些方法可能會限制吞吐量、拖慢應用程式的速度，甚至在回應中直接漏掉資料。

這就是為什麼即使是信譽良好的服務商，在查詢歷史資料時也經常回傳不完整或延遲的結果。對開發者來說，這些缺漏的區塊和緩慢的呼叫很快就會演變成應用程式內部功能異常。

我們想解決這個問題。不只是靠增加硬體（雖然我們也確實增加了硬體），而是徹底改造我們基礎設施的架構。

## 我們在 Google Bigtable 上遇到的優化困境
和許多 Solana 基礎設施服務商一樣，我們一開始也是使用 Google Bigtable，隨著時間過去，我們發現了上述的效能問題。而這些問題又因兩個因素而更加嚴重。

首先，也是最主要的原因，是 Solana 區塊的體積龐大。Bigtable 在資料列大小約為 1KB 時效能最佳，而 Solana 的交易資料或帳戶狀態往往遠大於此。要抓取一個龐大的二進位物件，需要對 Google 底層儲存系統（Colossus）進行更多 I/O 操作。

其次是成本問題。雲端資料庫十分昂貴。Solana 上有很多資料（例如投票交易）本身價值不高，把這些資料一直放在雲端維護在經濟上並不划算。此外，Google Bigtable 是依用量計價，這進一步增加了成本壓力，也讓我們更難提供更高的 RPS。

儘管有這些限制，我們仍然嘗試對這套架構做一些優化。舉例來說，我們優化了交易的抓取流程。在 Google Bigtable 的預設架構下，當你用 `getTransaction` 請求一筆交易時，系統會先對 `tx` 資料表發出一次查詢，透過簽名找出區塊 ID，然後抓取所有區塊才能回傳單一一筆交易。為了從遠端雲端資料庫取得這麼一小筆資料，得做這麼多工作，實在划不來。

為了讓這個常見的請求更有效率，我們新增了一張叫做 `tx-full` 的資料表，裡面存的不只是區塊的參照，而是整筆交易本身。這對回應時間帶來了很大的改善，但我們仍然因為資料中心的地理位置較遠，而受到延遲增加的困擾。

即便我們很努力想遷就這套預設架構，開發速度依然非常緩慢。光是從 full RPC 節點／validator 進行一次簡單的寫入操作，就是一大麻煩：每次變更都需要完整重啟節點，整個過程可能耗時 30 分鐘到數小時不等。資料庫無法設置在地理位置更近的地方、也無法控制其中某些參數，這些都讓一切變得更困難、成本也更高。經過一番努力後，我們決定最好的方式是重寫整套架構，從資料擷取到 RPC 伺服器本身都重新打造。

## 我們的解決方案？重寫整套架構

我們投入這項工作的主要目標，是縮短開發時間。這代表不再需要同時負責寫入與提供資料的重型 RPC 節點。取而代之的是，我們把資料寫入和資料提供拆分成多個獨立的小型服務，並改用自架的開源資料庫。

這套新系統提供：

- **硬體與軟體協同優化：** 架構中的每一層，從磁碟配置到記憶體存取，都經過調校，以最大化吞吐量、最小化運算開銷。
- **多區域資料分散：** 封存資料分散在全球各地，確保不論請求來自哪裡都能享有低延遲存取。
- **三重驗證擷取：** 每筆記錄都會寫入兩次，透過程式驗證正確性，並持續掃描確認完整性。
- **自我修復管線：** 一旦偵測到差異，系統會自動重新擷取遺漏的項目，並交叉核對每個區塊最多 30 到 50 個相關地址，以確保資料完整性。

接下來，我們會逐一說明這個解決方案的每個部分，這些內容我們也[開源了](https://github.com/dexterlaboss/archival-rpc)（程式碼位於 [DexterLab](https://www.alchemy.com/dapps/dexterlab) 的 repo 中，該公司[去年被 Alchemy 收購](https://www.theblock.co/post/354709/alchemy-acquires-solana-infrastructure-provider-dexterlab-as-it-continues-expansion-beyond-ethereum)）。

## ArchivalRPC

我們把封存服務從頭重建，打造出效能高出許多的服務，只需幾秒鐘就能啟動或重啟。相較於過去使用 full RPC 節點時，啟動需耗時 30 分鐘到數小時，這是一項顯著的進步。

我們仍然使用相同的官方 Solana 函式庫來序列化與反序列化資料，因此這項服務永遠能與持續的變更保持相容。也就是說，即使換上這套全新的自訂架構，我們完全不會更動資料本身，確保我們提供的資料在處理過程中不會被損毀。

而且，由於這是一個依賴資料庫的輕量服務，要按需擴充規模（只需幾秒鐘）並應付大量流量非常容易。這對流量起伏劇烈的應用特別有用，而且僅靠單一資料庫實例，這項服務就能依資料庫設定達到 100,000 到 200,000 RPS 的數量級。

## ArchivalRPC 擷取器（ingestor）

架構中的這個部分，負責從多個來源拉取 Solana 資料，並寫入我們的資料庫層。它同時處理批次匯入（例如用於啟動新實例時的原始 gzip 區塊封存檔）以及來自 Kafka 等系統的即時串流。

我們也在擷取器中內建了細粒度的控制，讓我們可以精確選擇某個實例該儲存哪些資料類型（例如只存區塊、只存簽名、只存交易，或任意組合）。這種選擇性擷取模式減少了單點故障的風險、提升了可靠度，也讓我們能根據不同使用情境，有效控制儲存成本。

## Hbase

在改善讀寫效能之後，我們把注意力轉向下一個挑戰：雲端資料庫的延遲問題。ArchivalRPC 從一開始的設計就支援多種儲存後端，這讓我們能夠輕鬆探索其他替代方案。

我們最終選擇了 HBase，一個開源、自架的 BigTable 替代方案，因為它讓我們能把資料庫和 RPC 伺服器放在同一地點，把儲存延遲實質上降到零。這個實作方式也讓我們能沿用既有的資料模型與實作原則，在不引入架構風險的前提下，獲得效能上的好處。

但即使 HBase 有這些優點，我們仍然遇到一些問題。舉例來說，為了處理 Solana 區塊體積龐大的問題，我們原本嘗試在 Hbase 中使用 `BucketCache`，它可以設定使用堆外記憶體（off-heap memory）或快速 SSD 作為第二層快取，讓更多資料保持在「熱」狀態。

然而，我們最終發現 `BucketCache` 並不可靠。由於資料的隨機性及其變動速度之快，它反而拖慢了我們的系統。因此，我們最終改用另一種方式來處理大型資料：實際改變了區塊的儲存方式。我們不再把完整的資料塊存進資料庫（因為資料庫本來就不是設計來處理這類大型資料結構），而是只在資料庫中儲存中繼資料，這些中繼資料會指向 CAR 檔案，而這些檔案可以存放在任何儲存空間上，具有很高的擴充性。

### 服務優化與多區域分散

要打造一套能夠擴展到數百萬、甚至數十億次請求的解決方案，需要對整個架構進行優化：光是把一種資料庫架構換成另一種，並不足以達到我們想要的效能。這需要我們在不同硬體上嘗試多種不同的部署方式，並嘗試不同的架構。經過一段時間，我們持續改進擷取與提供服務，最終達到了預期的成效。

優化硬體與軟體以應付大量請求是我們必須解決的其中一項挑戰，另一項則是對抗延遲問題。對於負擔較重的封存呼叫，我們發現要把延遲控制在合理範圍內，就必須把硬體擴展到多個區域，以確保全球各地都能獲得穩定且快速的回應。

這套多區域架構同時也提升了我們的可用性，並協助我們維持更高的可靠度標準。服務中斷是無法避免的，而擁有橫跨多個地理區域的備援選項，能確保即使某個區域發生故障，我們仍能持續為客戶提供服務。這套相同的多區域配置也有助於我們擴充規模、分散流量：當某個區域接近容量上限時，我們可以在擴充部署的同時，把流量導向另一個區域。

### 三重驗證擷取

這項產品有很大一部分工作是在提升效能，但那只是問題的一半。Solana 封存方法長期存在的另一個問題，是資料經常會直接遺漏。我們必須提升系統的可靠度。

許多以 Bigtable 為基礎的服務商，隨著擷取管線規模擴大，會在不知不覺中漏掉區塊、簽名或交易。這些遺漏源自 validator 預設的資料擷取方式：每次重啟都可能在資料寫入時造成缺口。

而且，即使你被迫維護多個 validator 來重複寫入相同的資料，也無法保證資料的一致性，因為並沒有任何二次檢查機制來確認資料是否被正確寫入。身為使用這套基礎設施的開發者，你根本不會知道資料有缺漏，直到下游出了問題才會發現。

我們的基礎設施從架構上就避免了這個問題。在資料寫入之後，我們會執行二次管線，讀取每一筆記錄，持續驗證資料，並在發現不一致時自動修復。我們也會在初始建置完成後對資料庫做完整檢查，並持續以接近即時的方式，對即時寫入的資料進行檢查。

我們追求的不只是更快的資料，而是值得信賴的資料。

### 自我修復管線

對於某些複雜的資料，例如 `signatures`，備援方法並不可靠。這代表前面提到的三重驗證流程，有時仍然不夠。

為了應對這個問題，我們使用程式化驗證，針對每個區塊交叉核對最多 30 到 50 個地址的交易資訊。一旦系統偵測到差異，我們的備份與復原流程就會自動修復遺漏的項目。

這個流程包括抓取一個作為「真實來源」的區塊，並從中擷取所有地址與簽名之間的關聯。為了在掃描 HBase 時控制成本，我們會針對一個很長的連續 slot 範圍，把每個地址的對應關係累積起來，然後執行一次掃描查詢，而不是逐一以 key 為基礎進行查詢。接著比對結果，藉此找出 SIGS 叢集中的缺口。

一旦找出這些缺口，我們會把該區塊重新送回索引管線（送到對應此資料類型的 kafka topic）並重新建立索引。我們也新增了一個一致性檢查流程，如果缺口沒有被補上，就會重複執行這個操作，若故障狀態持續存在，最終會通知我們的團隊。

正是這種冗餘與自我修復機制，確保你可以放心依賴我們的方法，作為 Solana 上最完整、最一致的歷史資料來源。

## 把這些努力轉化為吞吐量

所有這些努力，最終都轉化為實質的處理能力：

- `getTransaction` 每區域 100,000 RPS
- `getSignaturesForAddress` 每區域 50,000 RPS
- `getBlock` 每區域 2,000 RPS

我們會持續投資在 Solana 服務上，並將很快擴展到更多區域，帶來更好的可靠度與延遲表現。這只是個開始。

## 親自試試看
如果你想試試我們在 Solana 上的封存方法，可以[建立一個 dashboard 帳號](https://dashboard.alchemy.com/)，免費生成你的 API 金鑰，開始使用。接著你可以到[我們的文件](https://www.alchemy.com/docs/reference/solana-api-quickstart)中探索 Solana 相關端點。

## 常見問題

### 什麼是 Solana 上的封存方法？

封存方法是用來取得歷史區塊鏈資料的 RPC 呼叫，例如 `getTransaction`、`getBlock` 和 `getSignaturesForAddress`。這些端點讓開發者能透過標準 JSON RPC 介面，存取過去的交易、區塊和簽名。

### 為什麼 Solana 的封存方法通常速度較慢？

大多數 Solana 基礎設施都運行在 Google Bigtable 上，這套架構相當耗費 CPU 與記憶體，處理大批次請求時也力不從心。這套架構並非為規模化而設計，可能導致吞吐量受限、效能下降，甚至在回應中直接漏掉資料。

### Alchemy 在 Solana 上的封存方法有多快？

Alchemy 的封存方法比其他解決方案快 up to 20x，容量方面，`getTransaction` 每區域可達 100,000 RPS，`getSignaturesForAddress` 為 50,000 RPS，`getBlock` 為 2,000 RPS。

### Alchemy 如何確保資料的完整性與可靠度？

我們採用三重驗證擷取機制：每筆記錄會寫入兩次，經過程式化驗證確認正確性，並持續掃描確認完整性。自我修復管線會透過交叉核對每個區塊最多 30 到 50 個相關地址，自動重新擷取遺漏的項目。

### Alchemy 為 Solana 封存資料使用什麼資料庫？

我們使用 HBase，一個開源、自架的資料庫，讓資料能與 RPC 伺服器放在同一地點，把儲存延遲實質上降到零。這取代了 Google Bigtable，藉此提升效能並降低成本。

### Alchemy 如何處理 Solana 龐大的區塊體積？

Alchemy 不再把完整的區塊存進資料庫，而是只儲存指向 CAR 檔案的中繼資料，這些檔案可以存放在任何儲存空間上，並具備高度擴充性。這種方式能更妥善地處理 Solana 區塊體積龐大的問題。

### 為什麼 Alchemy 要從頭重寫整套封存架構？

使用 full RPC 節點的預設架構，每次重啟需耗時 30 分鐘到數小時，讓開發變得緩慢且昂貴。Alchemy 把資料寫入與提供拆分成多個小型服務，這些服務能在數秒內重啟，並依需求擴充規模。

### Alchemy 如何在全球維持低延遲？

封存資料分散在全球多個區域，確保不論請求來自哪裡都能享有低延遲存取。這套多區域架構同時也提升了可用性，並有助於在高需求期間分散流量。
