「可擴展性」可擴展性最佳實踐:來自eBay的經驗教訓

在eBay,我們每天都在爭論的主要架構力量之一是可擴展性。它為我們制定的每一個架構和設計決策著色和推動。全球有數億用戶,每天超過20億的頁面瀏覽量,以及我們系統中的數PB數據,這不是一個選擇 - 它是必需的。

在可擴展的體系結構中,資源使用應該隨負載線性增加(或更好),其中可以在用戶流量,數據量等中測量負載。在性能與單個工作單元相關的資源使用情況下,可伸縮性是關於如何隨著工作單元數量或大小的增加,資源使用情況發生變化。換句話說,可伸縮性是價格 - 性能曲線的形狀,而不是其在該曲線中的一點處的值。

可伸縮性有許多方面 - 事務性,操作性,開發性工作。在本文中,我將概述我們隨著時間的推移學習的幾個關鍵最佳實踐,以擴展基於Web的系統的事務吞吐量。大多數這些最佳實踐對您來說都很熟悉。有些人可能沒有。所有這些都來自開發和運營eBay網站的人們的集體經驗。

最佳實踐#1:按功能劃分

無論您將其稱為SOA,功能分解還是簡單的良好工程,相關的功能都屬於一體,而不相關的功能則屬於不同。此外,不相關的功能可以解耦得越多,就越需要彼此獨立地擴展它們。

在代碼級別,我們都會一直這樣做。 JAR文件,包,包等都是我們用來隔離和抽象一組功能的機制。

在應用層,eBay將不同的功能劃分為單獨的應用程序池。銷售功能由一組應用程序服務器提供,競標功能由另一組應用程序服務器提供,另一組應用程序服務器進行搜索。總的來說,我們將大約16,000個應用服務器組織到220個不同的池中。這允許我們根據其功能的需求和資源消耗,彼此獨立地擴展每個池。它進一步允許我們隔離和合理化資源依賴性 - 例如,銷售池只需要與相對較小的後端資源子集進行通信。

在數據庫層,我們遵循相同的方法。 eBay上沒有單一的整體數據庫。相反,有一組用於用戶數據的數據庫主機,一個用於項目數據的集合,一個用於購買數據的集合等。 - 在400個物理主機上共有1000個邏輯數據庫。同樣,這種方法允許我們為每種類型的數據獨立地擴展數據庫基礎結構。

最佳實踐#2:水平分割

雖然功能分區使我們成為一種方式,但對於完全可擴展的架構而言,它本身並不足夠。由於一個功能可能與另一個功能分離,因此單個功能區域的需求可能並且將隨著時間的推移而超過任何單個系統。或者,正如我們想提醒自己的那樣,“如果你不能拆分它,你就無法擴展它。”因此,在特定的功能區域內,我們需要能夠將工作量分解為可管理的單元,其中每個單元保持良好的性價比。這是水平分割的來源。

在應用層,eBay的交互是設計無狀態的,水平分割是微不足道的。使用標準負載平衡器來路由傳入流量。因為所有應用程序服務器都是相同的,並且沒有保留任何事務狀態,所以它們中的任何一個都可以。如果我們需要更多處理能力,我們只需添加更多應用服務器。

數據庫層出現了更具挑戰性的問題,因為根據定義數據是有狀態的。在這裡,我們沿著主要訪問路徑水平分割(或“分片”)數據。例如,用戶數據當前分為20個主機,每個主機包含1/20的用戶。隨著用戶數量的增長,以及我們為每個用戶存儲的數據增長,我們會添加更多主機,並進一步細分用戶。同樣,我們對項目,購買,帳戶等使用相同的方法。不同的用例使用不同的方案來劃分數據:一些基於密鑰的簡單模數(以1結尾的項目ID轉到一個主機,以2結尾的那些等等,其中一些在一系列ID(0-1M,1-2M等)上,一些在查找表上,一些在這些策略的組合上。然而,無論分區方案的細節如何,一般的想法是支持數據分區和重新分區的基礎設施將比不支持分區和重新分區的基礎設施更具可擴展性。

最佳實踐#3:避免分佈式事務

此時,您可能想知道如何通過事務保證在功能和水平方面對數據進行分區。畢竟,幾乎任何有趣的操作都會更新多種類型的實體 - 用戶和物品會立即浮現在腦海中。正統的答案是眾所周知且易於理解的 - 使用兩階段提交在各種資源之間創建分佈式事務,以保證所有資源的所有更新都發生或不發生。不幸的是,這種悲觀的方法帶來了巨大的成本。協調成本會對擴展,性能和延遲產生負面影響,隨著您增加依賴資源和傳入客戶端的數量,協調成本會惡化。可用性同樣受限於所有相關資源可用的要求。務實的答案是放寬不相關係統的交易保證。

事實證明,你不能擁有一切。特別是,通常既不需要也不可能保證跨多個系統或分區的立即一致性。大約10年前由Inktomi的Eric Brewer提出的CAP定理指出,分佈式系統的三個非常理想的特性 - 一致性(C),可用性(A)和分區容差(P) - 你只能選擇兩個一度。對於高流量網站,我們必須選擇分區容差,因為它是擴展的基礎。對於24x7網站,我們通常會選擇可用性。因此,即時一致性必須讓位。

在eBay,我們絕對不允許任何類型的客戶端或分佈式事務 - 沒有兩階段提交。在某些明確定義的情況下,我們將單個數據庫上的多個語句組合成單個事務操作。但是,在大多數情況下,單個語句是自動提交的。雖然這種對正統ACID屬性的有意放鬆並不能保證在任何地方都能立即保持一致,但實際情況是大多數系統在絕大多數情況下都是可用的。當然,我們採用各種技術來幫助系統達到最終的一致性:仔細排序數據庫操作異步恢復事件以及協調結算批次。我們根據特定用例的一致性要求選擇技術。

對於架構師和系統設計師來說,關鍵的一點是,不應將一致性視為一個全有或全無的命題。大多數真實世界的用例根本不需要立即一致性。正如可用性不是全部或全部,我們經常將其與成本和其他力量進行權衡,同樣我們的工作也會根據特定操作的要求定製適當的一致性保證。

最佳實踐#4:異步解耦功能

擴展的下一個關鍵要素是積極使用異步。如果組件A同步調用組件B,則A和B緊密耦合,並且該耦合系統具有單一的可伸縮性特徵 - 為了擴展A,您還必須擴展B.同樣有問題的是它對可用性的影響。回到邏輯101,if A implies B, then not-B implies not-A。換句話說,如果B下降則A下降。相反,如果A和B異步集成,無論是通過隊列,多播消息傳遞,批處理過程還是其他方式,每個都可以獨立於另一個進行縮放。此外,A和B現在具有獨立的可用性特徵 - 即使B關閉或受困,A仍可繼續前進。

這個原則可以而且應該在基礎設施上下應用。諸如SEDA(分階段事件驅動架構)之類的技術可用於在單個組件內部進行異步,同時保留易於理解的編程模型。在組件之間,原理是相同的 - 儘可能避免同步耦合。通常情況下,這兩個組件在任何情況下都沒有業務直接對話。在每個級別,將處理分解為階段或階段,並將它們異步連接,對於擴展至關重要。

最佳實踐#5:將處理轉移到異步流程

現在您已異步解耦,請將盡可能多的處理移動到異步端。在快速回復請求的系統中,這可以大大減少請求者所經歷的延遲。在web站點或交易系統中,用數據或執行延遲(我們完成所有工作的速度有多快)換取用戶延遲(用戶得到響應的速度有多快)是值得的。活動跟蹤,計費,結算和報告是屬於後臺的處理的明顯示例。但是,處理主要用例的重要步驟通常可以分解為異步運行。任何可以等待的東西都應該等待。

同樣重要但同樣重要的是異步可以大大降低基礎設施成本。同步執行操作會迫使您根據峰值負載擴展基礎架構 - 它需要在最後一秒處理最糟糕的第二天。但是,將昂貴的處理轉移到異步流可以讓您根據平均負載而不是峰值來擴展基礎架構。隊列不是需要立即處理所有請求,而是隨著時間的推移擴展處理,從而抑制峰值。系統負載越尖銳或變化越大,這種優勢就越大。

最佳實踐#6:在所有級別進行虛擬化

虛擬化和抽象無處不在,遵循舊的計算機科學格言,即每個問題的解決方案都是另一個層次的間接。操作系統抽象硬件。許多現代語言中的虛擬機抽象了操作系統。對象關係映射層抽象數據庫。負載平衡器和虛擬IP抽象網絡端點。當我們通過功能和數據劃分來擴展我們的基礎架構時,這些分區的額外虛擬化水平變得至關重要。

例如,在eBay,我們虛擬化數據庫。應用程序與數據庫的邏輯表示交互,然後通過配置將其映射到特定的物理機器和實例。應用程序類似地從拆分路由邏輯中抽象出來,拆分路由邏輯將特定記錄(例如,用戶XYZ的記錄)分配給特定分區。這兩種抽象都是在我們自己開發的O / R層實現的。這允許操作團隊在物理主機之間重新平衡邏輯主機,通過分離它們,合併它們或移動它們 - 所有這些都不需要觸及應用程序代碼。

我們同樣虛擬化搜索引擎。為了檢索搜索結果,聚合器組件在多個分區上並行化查詢,並使高度分區的搜索網格作為一個邏輯索引顯示給客戶端。

這裡的動機不僅是程序員的便利性,還有操作靈活性。硬件和軟件系統發生故障,需要重新路由請求。添加,移動和刪除組件,計算機和分區。通過明智地使用虛擬化,您的基礎架構的更高級別可以倖免於未發現這些變化,因此您可以自由地製作它們。虛擬化使得擴展基礎架構成為可能,因為它使得擴展可管理。

最佳實踐#7:正確緩存

擴展的最後一個組成部分是明智地使用緩存。這裡的具體建議不太普遍,因為它們往往高度依賴於用例的細節。在一天結束時,高效緩存系統的目標是在存儲限制內最大化緩存命中率,滿足可用性要求以及對陳舊性的容忍度。事實證明,這種平衡可能非常難以實現。一旦受到打擊,我們的經驗表明,它也很可能隨著時間而改變。

例如,最明顯的緩存機會來自緩慢變化的讀取主要數據 - 元數據,配置和靜態數據。在eBay,我們積極地緩存這類數據,並使用拉動和推送方法的組合,以使系統在面對更新時合理地保持同步。減少對相同數據的重複請求可以而且確實產生重大影響。更具挑戰性的是快速變化的讀寫數據。在大多數情況下,我們故意在eBay上回避這些挑戰。我們傳統上沒有在請求之間進行任何臨時會話數據的緩存。我們同樣不會在應用程序層中緩存共享業務對象,如項目或用戶數據。我們明確地根據可用性和正確性來緩存這些數據的潛在好處。應該注意的是,其他網站確實採取不同的方法,做出不同的權衡,並且也是成功的。

毫不奇怪,很可能有太多好事。為緩存分配的內存越多,可用於為單個請求提供服務的可用性就越少。在通常受內存限制的應用層中,這是一個非常真實的權衡。但更重要的是,一旦你開始依賴緩存,並採取了極其誘人的步驟來縮小主要系統以處理緩存未命中,那麼沒有它,你的基礎設施可能無法生存。一旦您的主系統無法再直接處理負載,您的站點的可用性現在取決於緩存的100%正常運行時間 - 這是一種潛在的危險情況。即使像重新平衡,移動或冷啟動緩存這樣的常規操作也會成為問題。

如果操作正確,一個好的緩存系統可以將您的縮放曲線彎曲到線性以下 - 後續請求從緩存中便宜地檢索數據,而不是相對更昂貴的主存儲。另一方面,緩存不佳會帶來大量額外的開銷和可用性挑戰。我還沒有看到一個沒有重要緩存機會的系統。但關鍵是要確保您的緩存策略適合您的情況。

總結

可伸縮性有時被稱為“非功能性需求”,暗示它與功能無關,並強烈暗示它不那麼重要。沒有東西會離事實很遠。相反,我想說,可擴展性是功能的先決條件 - 一個“優先級為0”的要求,如果有的話。

我希望您發現這些最佳實踐的描述很有用,並且它們可以幫助您以新的方式思考您自己的系統,無論其規模如何。

參考

  • eBay's Architectural Principles (video)
  • Werner Vogels on scalability
  • Dan Pritchett on You Scaled Your What?
  • The Coming of the Shard
  • Trading Consistency for Availability in Distributed Architectures
  • Eric Brewer on the CAP Theorem
  • SEDA: An Architecture for Well-Conditioned, Scalable Internet Services


分享到:


相關文章: