⚡ 最新消息:ElysiumTestnet 於QuicknodeTestnet Kinetiq 在 HyperEVM 上的 EVM 鏈端點
取得一個endpoint已通過 SOC 2 Type II 認證 · ISO 27001
Solana「讀取層的重建:深入解析Quicknode 的記憶體內快取」
Quicknode 在記憶體中重建了Solana 的讀取層。getProgramAccounts、getLargestAccounts 等函式,其執行速度比原版 Agave 快 10 倍至 1000 倍。

Solana 開發人員在使用會傳回大量資料或需要掃描完整帳戶集的方法時,會遇到阻礙。 getProgramAccounts 遭到限流或停用。 getLargestAccounts 永遠不會返回。其根本原因在於架構層面。Agave RPC 節點是針對寫入進行優化的系統,而對耗資源的讀取方法進行限流是必要之舉,而非政策上的選擇。應用程式最終只能透過重試邏輯、快取層以及輪詢間隔來彌補,而這些措施純粹是為了繞過區塊鏈供應商基礎架構的限制。
儘管整個生態系統現正開始著手解決這個問題,但Quicknode 早已建構出Solana 生態系統真正所需的讀取層。歷經兩年的迭代開發,搭配專為此目的打造的內存架構,其效能比 Agave 的預設值快了 10 倍至超過 1000 倍。
如果您是「Quicknode 」的客戶,則無需額外申請即可享有這些優惠。您的「endpoint 」已為您提供這些服務。
Agave RPC 節點的設計目的是用於處理交易、達成共識並推進帳本。它們所維護的帳戶狀態是該過程的副產品,而非針對讀取優化的儲存庫。該系統沒有查詢規劃器、沒有原生次級索引,且讀寫 I/O 並未分離。
僅靠硬體擴展無法解決此問題。讀取查詢與共識機制在同一台機器上爭奪相同的 CPU 資源,因此增加硬體配置雖需負擔驗證者級別的成本(768 GB 記憶體、高速 NVMe 儲存裝置、高頻寬網路),卻無法消除資源爭用問題。
增加更多 RPC 節點會使問題更加惡化:額外的節點會增加Solana 的閒聊網路和渦輪網路的負載,從而減緩實際需要狀態傳播的驗證者獲取狀態的速度。透過驗證者架構來擴展讀取層,會直接導致寫入層的效能下降。
資料分頁會帶來一致性風險。當新資料區塊出現在頁面之間時,回應所依據的帳戶狀態會在讀取過程中發生變化。針對大型結果集進行迭代的應用程式,會在讀取完成前就收到已發生位移的資料。
Quicknode 我們很早就意識到這個問題,因此將讀取工作負載從驗證器中分離出來,並透過專為此目的打造的基礎架構來處理。目前用於處理流量的架構,正是這些努力的第三個版本。每個版本都更貼近資料本身,剝離了那些只會增加延遲卻無實質價值的抽象層,而這項工作仍在持續進行中。
第 1 輪:關係型資料庫
第一版採用了傳統的做法:將帳戶狀態和總帳資料導入關聯式資料庫,建立索引,並對其進行查詢。這立即解決了資源爭用問題。驗證者可以專注於達成共識,而讀取作業則由獨立的系統處理。 然而,在Solana 這樣的規模下,關聯式資料庫所帶來的開銷便顯得至關重要:查詢規劃、行級鎖定、預寫日誌(WAL)管理、自動真空(autovacuum),以及基於磁碟儲存的固有延遲。對於每 400 毫秒產生一個區塊的區塊鏈而言,每次讀取延遲的每微秒,都會在每日數百萬次請求中產生累積效應。隨著Solana 的流量規模擴大,磁碟 I/O 便成為下一個必須突破的瓶頸。
第二輪:專用高效能資料庫
下一輪迭代從資料結構本身著手。equipe 將資料模型進行簡化與去正規化,使其符合Solana 的實際存取模式,並捨棄了已無法帶來相應效益的關聯式結構。這種重構為遷移至高效能的 NoSQL 資料庫鋪平了道路——該資料庫專為寫入密集型資料匯入及低延遲的點讀取而設計,且避免了關聯式架構所伴隨的查詢規劃、行鎖定及預寫等開銷。
吞吐量與延遲均顯著改善,且在大型關聯式系統運作時所伴隨的營運困擾(例如自動真空操作停滯、鎖定爭用激增、WAL 膨脹等)也隨之消失。
第 3 次迭代:完全內存架構
接下來這一步的動機則有所不同。無論調校得多麼完善,基於磁碟的系統都面臨著一個難以突破的硬性限制:每次讀取最終都會接觸到一種比 RAM 慢幾個數量級的儲存媒介。要突破這個限制,讀取路徑本身就必須擺脫對磁碟的依賴。
當前的架構完全將磁碟從讀取路徑中剔除。AccountsDB 的狀態儲存在記憶體中,並透過專為Solana 特定查詢模式量身打造的手工設計資料結構進行索引。這裡沒有查詢規劃器、沒有 B 樹遍歷、也沒有頁面快取未命中:僅需直接存取記憶體中的預先索引資料即可。
轉向全內存架構是一項經過深思熟慮的決定,旨在針對單一目標進行優化:原始讀取效能。
LedgerDB(儲存每個區塊鏈中區塊、交易及元資料的歷史紀錄)的快取機制遵循相同的设计理念,但採用不同的儲存策略。Solana 的帳本過於龐大,無法完全載入記憶體中,因此採用分層設計:鏈尖資料會保留在記憶體中以確保快速存取,而歷史資料則由針對該工作負載進行調校的高效能分散式資料庫提供。
Quicknode Solana Cache 是一個獨立的二進位檔,可處理所有鏈端方法,且無需任何外部依賴。僅有歷史帳本查詢會調用外部資料庫。
AccountsDB(用於儲存每個帳戶的當前狀態)的快取完全位於記憶體中,而使其運作迅速的設計選擇絕非偶然。四種機制相互配合,以消除讀取與寫入路徑上所有可避免的延遲來源。
領先於鏈條
資料新鮮度是一種效能特性,其作用在任何讀取請求送達之前便已開始。 快取直接從 Shred-Stream 資料來源擷取資料,接收帳戶更新資訊的時間比典型 Agave RPC 節點(該節點需等待完整區塊形成)早 200 至 400 毫秒。這些更新透過低延遲寫入路徑(採用near 無鎖設計的記憶體內寫入)傳入,因此這份先機能貫穿端到端全程,而非被擷取開銷所消耗。
這種綜合效果在尖峰時段可量測:在生產流量下,快取的最新可觀察時槽始終比配置相似的 Agave RPC 節點領先 1 至 2 個時槽。對於交易系統、市場可提取價值(MEV)、清算引擎及即時儀表板等需釐清「當前狀態為何」的工作負載而言,這正是依據即時狀態採取行動,與依據已過時的資料採取行動之間的關鍵差異。
自訂資料結構
用於驅動帳戶查詢、代幣查詢及程式帳戶篩選的索引,是基於經效能測試選定的低階資料結構所建置的;這並非最通用的選擇,而是針對系統實際遇到的存取模式所測得速度最快的方案。共享狀態在可行範圍內盡可能減少鎖定操作,確保並行讀取者互不干擾。
針對硬體進行優化
該系統在硬體利用率方面著墨甚深,透過將資料導入、索引建立及查詢處理作業固定分配至專用的 CPU 核心,以將上下文切換的開銷降至最低;同時根據各元件的特定記憶體分配模式,為其選用相應的記憶體分配器,藉此在負載下減少記憶體碎片化。
預計算
在可行情況下,系統會在匯入帳戶狀態時預先計算並維護衍生視圖:供應彙總、排序後的帳戶清單、經過篩選的程式快取。此舉將 CPU 運算負荷從對延遲敏感的讀取路徑,轉移至以吞吐量為導向的寫入路徑。查詢絕不會觸發運算,而是直接讀取已存在的結果。17 毫秒 getSupply 基準測試中的回應正是此機制在實際運作中的體現:當查詢送達時,彙總結果已經存在。
getProgramAccounts 這項任務聽起來很簡單:回傳符合這些篩選條件且由該程式所擁有的所有帳戶。挑戰在於,要為其建立良好的索引相當困難。該程式可能是數千個已部署程式中的任何一個,而每個程式都有其專屬的帳戶架構。
這些濾鏡通常是 memcmp 在特定位元組偏移處執行的操作,這些偏移值只有在已知程式內部資料佈局的情況下才有意義。A memcmp 在偏移量 32 處,這在 SPL Token 中代表一種含義,但在 DEX 程式中卻代表完全不同的另一種含義。
並不存在一種適用於所有情況的通用索引策略。索引 getProgramAccounts 若要有效率地處理,必須了解每個程式的帳戶結構。Quicknode 的索引器透過兩種策略協同運作來解決此問題:
原生索引程式
針對一小部分流量龐大且運作機制明確的計畫,Quicknode 會隨著帳戶的更新,持續建立並維護專用的原生索引。這些正是主導Solana 流量的計畫:SPL Token(舊版)、SPL Token-2022 以及 Stake 計畫。
帳戶的佈局已知,客戶使用的篩選模式也已知,而Quicknode 設計的索引正是為了直接利用這些具體特性。無論程式擁有多少百萬個帳戶,這些索引都能在不到一毫秒的時間內處理查詢,且涵蓋了絕大多數的 getProgramAccounts 流量,因為最熱門的查詢都集中在少數幾個程式上。
動態索引程式
至於其他所有情況,例如每個自訂的 DeFi 協議、上週部署的每個程式,以及每個利基應用場景,索引器都會採用動態策略。 當呼叫者首次針對特定程式請求某種篩選器組合時,快取會自動在執行過程中即時建立新的索引。該首次請求將以未優化的方式執行。索引器必須先掃描程式的帳戶並建立索引,才能做出回應,因此呼叫者需承擔暖化快取的一次性成本。即便如此,這條冷路徑的效能仍優於 Agave。
之後每次對同一篩選條件的請求,皆由一個即時且以增量方式維護的快取處理,該快取會隨著鏈上帳戶的變更而保持最新狀態。flow 透過與原生索引程式相同的串流管道進行更新,因此快取視圖永遠不會與實際狀況產生偏差。
從那時起,無論資料包大小或程式熱門程度如何,延遲都可視為恆定不變。無論是返回十個帳戶的篩選器,還是返回一百萬個帳戶的篩選器,其回應速度都在同一數量級上,因為相關運算工作早已完成。回應是直接從預先組裝好的記憶體中串流傳輸,而非針對每個請求重新計算。
新程式在部署當天即可獲得支援。無需進行資料結構整合、無需手動設定,也無需等待工程團隊equipe 來新增支援。首位查詢新程式的客戶端便會觸發索引建立,此後的所有客戶端皆可受惠於此。
原生索引能針對絕大多數實際流量提供最佳效能。動態索引器可確保沒有任何程式或篩選模式無法被支援。
p50
節目表 | 帳戶 | Quicknode 快取預熱 TTFB | Agave Direct TTFB | Quicknode 對陣龍舌蘭 |
|---|---|---|---|---|
Jupiter v6 | 60 | 0.2 毫秒 | 2.0 毫秒 | 速度提升 10 倍 |
Marinade Finance | 1K | 2.5 毫秒 |
p99
節目表 | 帳戶 | Quicknode 快取預熱 TTFB | Agave Direct TTFB | Quicknode 對陣龍舌蘭 |
|---|---|---|---|---|
Jupiter v6 | 60 | 0.3 毫秒 | 6 毫秒 | 快 20 倍 |
Marinade Finance | 1K | 4 毫秒 |
getProgramAccounts 這是 AccountsDB 層面上最棘手的問題,因此它擁有專屬的優化策略。其餘所有操作(點查詢、聚合、標記查詢、區塊與槽位元資料)皆從相同的記憶體內狀態中處理,並遵循相同的設計原則:在可行情況下預先計算、讀取路徑上無鎖,並透過單次查詢而非掃描來解決。
其結果是,每個 AccountsDB 方法(而不僅是主要方法)在並發負載下都能以記憶體內速度運行。沒有任何受支援的方法會在流量擴展或帳戶集增加時,因負載而導致效能下降。
以下數據來自與同一網路上的標準 Agave 驗證器進行的效能比較。這種效能差異源於架構設計,而非硬體規格更優所致。
對於簡單的點查詢, getAccountInfo, getBalance,以及 getTokenAccountBalance,Quicknode 的索引器在 2-4 毫秒內可與 Agave 匹敵。這些方法的熱路徑為單次索引讀取。響應時間主要取決於網路往返時間,而非隨帳戶集規模而增加的工作量。至於需要聚合、排序檢視或驗證器未維護的次級索引的方法,其效能表現則會有所差異。
方法 | Quicknode 快取 p50 | 龍舌蘭 p50 | Delta |
|---|---|---|---|
getTokenLargestAccounts (USDC) | 4 毫秒 | 97,535 毫秒 | 快 24,384 倍 |
getTokenLargestAccounts (BONK) | 4 毫秒 | 8,463 毫秒 | 快 2,116 倍 |
getLargestAccounts |
有三項結果特別引人注目:
getTokenLargestAccounts 以 USDC 為例:Agave 需要近 100 秒。每次請求都會迫使驗證者遍歷完整的 SPL 代幣程式碼,依鑄造者進行篩選、排序,並返回前 20 項結果,且沒有快取結果,也沒有預先排序的索引。Quicknode 的索引器會維護一個按鑄造者排序的集合,並隨著餘額變化進行增量更新。
getLargestAccounts 在 Agave 上無法於五分鐘內完成。驗證器會在每次請求時,針對整個帳戶集從頭重新計算一個已排序的彙總值。Quicknode 的索引器會將其維持為一個持續運行的索引。
getTokenAccountsByDelegate 在 Agave 上不支援此功能。Quicknode 的索引器能在個位數毫秒內完成處理。
延遲數據顯示的是單一請求的速度。並發情況則有所不同,但同樣重要。當單一 Agave getTokenLargestAccounts 查詢耗時 97 秒,該請求佔用了服務槽位,並導致後續的所有查詢陷入停滯:一個緩慢的請求會引發連鎖反應,導致所有並行呼叫者的延遲驟升。
Quicknode的索引器並不會出現該種故障mode 。每個查詢皆由預先建置的內存索引處理,因此沒有任何單一請求的執行時間會長到足以耗盡並發流量的資源。在持續負載下,AccountsDB 快取每秒可處理數萬次請求,同時將 P50 延遲控制在 10 毫秒以內,其吞吐量主要受限於硬體容量,而非帳戶集大小或查詢複雜度。
並非每種方法都能在 10 毫秒內完成。針對原生索引集以外程式的冷請求,在首次呼叫時需支付 11 至 40 秒的初始化成本,期間需建立訂閱並填充過濾後的快取。之後對同一過濾器的所有請求,皆會從溫存的記憶體快取中取得資料。
大規模的質押計畫查詢展現了中階範圍的表現。Marinade 的 81,000 個質押帳戶在Quicknode 的索引器上約需一秒即可完成查詢,而在 Agave 上則需 6.7 秒。索引查詢速度依然迅速,但在如此大的資料量下,組裝和序列化回應確實會造成實質的 CPU 負載。
在 Raydium V4 的規模下(約 70.5 萬個帳戶、約 869 MB 的 JSON 資料),回應速度會遇到另一種瓶頸:首次字節傳輸時間為 1.5 秒,而完整傳輸所需時間則更長,這取決於透過網路傳輸 869 MB 資料的物理限制。
各方面都展現出顯著的效能提升。每種方法的執行速度都比原生 Agave 驗證器快 10 倍至 1000 倍。在單一方法的負載測試中,單一執行個體可持續處理 350,000 getTransaction 每秒請求數,延遲低於 40 毫秒,且達 120,000 getAccountInfo 每秒請求數,P50 值低於 20 毫秒。
總體而言,AccountsDB 與 LedgerDB 的快取共同涵蓋了約 95% 的生產環境 RPC 流量,以及約 90% 的Solana JSON-RPC 規範。
今日所呈現的成果,是歷經兩年反覆迭代的結果。這並非最終狀態。Quicknode 對Solana 讀取層的投資是持續進行的。該架構將持續提速,方法涵蓋範圍將不斷擴大,而彌合開發者需求與基礎架構所能提供之功能之間差距的工作,也將永不停歇。
Solana讀取與寫入工作負載本質上是截然不同的問題。它們理應採用根本不同的架構。Quicknode 耗時兩年,從零開始建構了Solana 生態系統真正所需的讀取層,且未在效能上做出任何妥協。對這項基礎架構的持續改進並非可選擇性的功能,而是Quicknode Solana endpoint 現已提供的服務。
在Quicknode 上建立一個Solana endpoint ,並開始在專為此工作負載量身打造且持續優化的讀取層上進行建置。
Quicknode 成立於 2017 年,為開發者與企業部署機構級的區塊鏈基礎設施。憑藉 99.99% 的正常運行時間以及對 76 條以上區塊鏈的支持,各團隊得以在無須妥協的情況下,開發並擴展鏈上應用程式。
將最新的工程見解、產品更新及 Web3 新聞直接送至您的收件匣。
12 毫秒
快 4.8 倍 |
張量 | 30K | 0.7 毫秒 | 213 毫秒 | ~快 304 倍 |
虎鯨漩渦 | 91K | 約 2.0 毫秒 | 3,515 毫秒 | 快約 1,760 倍 |
Phoenix DEX | 109K | 約 2.6 毫秒 | 783 毫秒 | ~快 301 倍 |
Drift Protocol | 273K | 約 6.4 毫秒 | 3,469 毫秒 | ~542 倍更快 |
SPL 名稱服務 | 341K | 約 8.7 毫秒 | 6,764 毫秒 | ~777 倍更快 |
Pump.fun | 507K | 約 15 毫秒 | 27,958 毫秒 | 速度快約 1,864 倍 |
Raydium V4 | 705K | 約 18.5 毫秒 | 10,224 毫秒 | ~553 倍更快 |
15 毫秒
快 3.8 倍 |
張量 | 30K | 1.2 毫秒 | 234 毫秒 | 約 195 倍快 |
虎鯨漩渦 | 91K | 約 2.3 毫秒 | 3,668 毫秒 | 速度約快 1,595 倍 |
Phoenix DEX | 109K | 約 2.8 毫秒 | 845 毫秒 | ~302 倍更快 |
Drift Protocol | 273K | 約 7.0 毫秒 | 3,720 毫秒 | ~531 倍更快 |
SPL 名稱服務 | 341K | 約 8.9 毫秒 | 7,571 毫秒 | ~851 倍更快 |
Pump.fun | 507K | 約 16 毫秒 | 29,718 毫秒 | 速度快約 1,857 倍 |
Raydium V4 | 705K | 約 20 毫秒 | 11,120 毫秒 | ~556 倍更快 |
7 毫秒
>300,000 毫秒(超時) |
>快 42,857 倍 |
getSupply | 17 毫秒 | 6,231 毫秒 | 快 367 倍 |
getTokenAccountsByDelegate | 4 毫秒 | 不支援 (錯誤) | Agave 無法提供此服務 |
getProgramAccounts:所有者持有的 Token-2022 | 8 毫秒 | >15,000 毫秒 | 速度約快 1,875 倍 |
getProgramAccounts:Orca Whirlpool(9.1 萬個帳戶)(溫和) | 167 毫秒 | 3,515 毫秒 | 快 21 倍 |
getProgramAccounts:Pump.fun(50.7萬個帳戶)(預熱中) | 795 毫秒 | 27,958 毫秒 | 快 35 倍 |