摘要: Tair是阿里巴巴集團自研的彈性緩存/存儲平臺,在內部有着大量的部署和使用。Tair的核心組件是一個高性能、可擴展、高可靠的NoSQL存儲系統。目前支持MDB、LDB、RDB等存儲引擎。本文基於Tair的存儲和訪問原理,對緩存的讀寫熱點問題進行討論,並給出一個知足現階段需求的熱點數據讀寫問題的解決方案。算法
做者:劉歡(淺奕)緩存
1 問題背景
分佈式緩存通常被定義爲一個數據集合,它將數據分佈(或分區)於任意數目的集羣節點上。集羣中的一個具體節點負責緩存中的一部分數據,總體對外提供統一的訪問接口[1]。分佈式緩存通常基於冗餘備份機制實現數據高可用,又被稱爲內存數據網格(IMDG, in-memory data grid)。在雲平臺飛速發展的今天,做爲提高應用性能的重要手段,分佈式緩存技術在工業界獲得了愈來愈普遍的關注和研發投入[2]。彈性緩存平臺[3]是分佈式緩存集羣在雲計算場景下的新形態,其強調集羣的動態擴展性與高可用性。動態擴展性表達了緩存平臺可提供透明的服務擴展的能力,高可用性則表達了緩存平臺能夠容忍節點失效。服務器
Tair是阿里巴巴集團自研的彈性緩存/存儲平臺,在內部有着大量的部署和使用。Tair的核心組件是一個高性能、可擴展、高可靠的NoSQL存儲系統。目前支持MDB、LDB、RDB等存儲引擎。其中MDB是相似Memcached的內存存儲引擎,LDB是使用LSM Tree的持久化磁盤KV存儲引擎,RDB是支持Queue、Set、Maps等數據結構的內存及持久化存儲引擎。數據結構
Tair的數據分片和路由算法採用了Amazon於2007年提出的一種改進的一致性哈希算法[4]。該算法將整個哈希空間分爲若干等大小的Q份數據分區(也稱爲虛擬節點,Q>>N,N爲緩存節點數),每一個緩存節點依據其處理能力分配不一樣數量的數據分區。客戶端請求的數據Key值經哈希函數映射至哈希環上的位置記爲token,token值再次被哈希映射爲某一分區標識。獲得分區標識後,客戶端從分區服務器映射表中查詢存放該數據分區的緩存節點後進行數據訪問。使用該算法對相同數據Key進行計算,其必然會被映射到固定的DataServer上,如圖:架構
此時DataServer單節點的讀寫性能便成了單數據Key的讀寫性能瓶頸,且沒法經過水平擴展節點的方式來解決。因爲阿里巴巴集團內部電商系的促銷活動自然的存在熱點數據,因此要加強整個彈性緩存/存儲平臺的穩定性和服務能力,就必須提高熱點數據的讀寫能力,使其能作到水平擴展。異步
2 解決方案
解決方案分爲三部分:熱點識別、讀熱點方案和寫熱點方案。分佈式
其中讀寫熱點方案都是以服務端能對熱點訪問進行精準的識別爲前提的。另外對於能夠提早預知熱點Key的狀況,也提供相應的客戶端API以支持特定數據Key或者特定Namespace的全部數據Key預先標記爲熱點Key的能力。函數
2.1 DataServer上的熱點統計過程
DataServer收到客戶端的請求後,由每一個具體處理請求的工做線程(Worker Thread)進行請求的統計。工做線程用來統計熱點的數據結構均爲ThreadLocal模式的數據結構,徹底無鎖化設計。熱點識別算法使用精心設計的多級加權LRU鏈和HashMap組合的數據結構,在保證服務端請求處理效率的前提下進行請求的全統計,支持QPS熱點和流量熱點(即請求的QPS不大可是數據自己過大而形成的大流量所造成的熱點)的精準識別。每一個採樣週期結束時,工做線程會將統計的數據結構轉交到後臺的統計線程池進行分析處理。統計工做異步在後臺進行,不搶佔正常的數據請求的處理資源。性能
2.2 讀熱點方案
2.2.1 服務端設計
原始Tair的數據訪問方式是先進行Hash(Key)%BucketCount的計算,得出具體的數據存儲Bucket,再檢索數據路由表找到該Bucket所在的DataServer後對其進行讀寫請求的。因此相同Key的讀寫請求必然落在固定的DataServer上,且沒法經過水平擴展DataServer數量來解決。雲計算
本方案經過在DataServer上劃分一塊HotZone存儲區域的方式來解決熱點數據的訪問。該區域存儲當前產生的全部讀熱點的數據,由客戶端配置的緩存訪問邏輯來處理各級緩存的訪問。多級緩存架構以下:
全部DataServer的HotZone存儲區域之間沒有權重關係,每一個HotZone都存儲相同的讀熱點數據。客戶端對熱點數據Key的請求會隨機到任意一臺DataServer的HotZone區域,這樣單點的熱點請求就被散列到多個節點乃至整個集羣。
2.2.2客戶端設計
2.2.2.1 客戶端邏輯
當客戶端在第一次請求前初始化時,會獲取整個Tair集羣的節點信息以及完整的數據路由表,同時也會獲取配置的熱點散列機器數(即客戶端訪問的HotZone的節點範圍)。隨後客戶端隨機選擇一個HotZone區域做爲自身固定的讀寫HotZone區域。在DataServer數量和散列機器數配置未發生變化的狀況下,不會改變選擇。即每一個客戶端只訪問惟一的HotZone區域。
客戶端收到服務端反饋的熱點Key信息後,至少在客戶端生效N秒。在熱點Key生效期間,當客戶端訪問到該Key時,熱點的數據會首先嚐試從HotZone節點進行訪問,此時HotZone節點和源數據DataServer節點造成一個二級的Cache模型。客戶端內部包含了兩級Cache的處理邏輯,即對於熱點數據,客戶端首先請求HotZone節點,若是數據不存在,則繼續請求源數據節點,獲取數據後異步將數據存儲到HotZone節點裏。使用Tair客戶端的應用常規調用獲取數據的接口便可,整個熱點的反饋、識別以及對多級緩存的訪問對外部徹底透明。HotZone緩存數據的一致性由客戶端初始化時設置的過時時間來保證,具體的時間由具體業務對緩存數據不一致的最大容忍時間來決定。
客戶端存儲於本地的熱點反饋過時後,數據Key會到源DataServer節點讀取。若是該Key依舊在服務端處於熱點狀態,客戶端會再次收到熱點反饋包。由於全部客戶端存儲於本地的熱點反饋信息的失效節奏不一樣,因此不會出現同一瞬間全部的請求都回源的狀況。即便全部請求回源,也僅須要回源讀取一次便可,最大的讀取次數僅爲應用機器數。若回源後發現該Key已不是熱點,客戶端便回到常規的訪問模式。
2.2.2.2 散列比和QPS誤差的關係
設集羣普通QPS爲 C,熱點QPS爲 H,機器數爲 N,則每臺機器QPS爲:
A=(C+H)/N
則普通機器QPS誤差比爲:
P_c=(C/N)/A=(C/N)/((C+H)/N)=C/(C+H) ,當 H=0 時,P_c=1
則熱點機器誤差比爲:
P_h=(C/N+H)/A=(C/N+H)/((C+H)/N)=(C+HN)/(C+H) ,當 H=0 時,P_h=1
進行散列後,設散列機器數爲 M,則熱點機器誤差比爲:
P_(h')=(C/N+H/M)/A=(C/N+H/M)/((C+H)/N)=(CM+HN)/(M(C+H))
設散列比爲 K,即 M=KN,則有:
P_(h')=(CM+HN)/(M(C+H))=(CKN+HN)/(KN(C+H))=(CK+H)/(K(C+H)),當 K=1 時, P_(h')=1
2.3 寫熱點方案
2.3.1 服務端設計
2.3.1.1 處理方式
對於寫熱點,由於一致性的問題,難以使用多級緩存的方式來解決。若是採用寫本地Cache,再異步更新源DataServer的方案。那麼在Cache寫入但還沒有更新的時候,若是業務機器宕機,就會有已寫數據丟失的問題。同時,本地 Cache會致使進行數據更新的某應用機器當前更新週期內的修改對其餘應用機器不可見,從而延長數據不一致的時間。故多級Cache的方案沒法支持寫熱點。最終寫熱點採用在服務端進行請求合併的方式進行處理。
熱點Key的寫請求在IO線程被分發到專門的熱點合併線程處理,該線程根據Key對寫請求進行必定時間內的合併,隨後由定時線程按照預設的合併週期將合併後的請求提交到引擎層。合併過程當中請求結果暫時不返回給客戶端,等請求合併寫入引擎成功後統一返回。這樣作不會有一致性的問題,不會出現寫成功後卻讀到舊數據,也避免了LDB集羣返回成功,數據並未落盤的狀況(假寫)。具體的合併週期在服務端可配置,並支持動態修改生效。
2.3.2 客戶端設計
寫熱點的方案對客戶端徹底透明,不須要客戶端作任何修改。
2.3.3 性能指標
LDB集羣實際壓測效果爲單Key合併能作到單Key百萬的QPS(1ms合併,不限制合併次數),線上實際集羣爲了儘量保證明時性,均採用了最大0.1ms以及單次最大合併次數爲100次的限制。這樣單Key在引擎層的最大落盤QPS就能控制在10000如下(而合併的QPS則取決於應用的訪問頻率)。Tair服務端的包處理是徹底異步化的,進行熱點請求的合併操做並不阻塞對其餘請求的處理。惟一的影響就是增大客戶端對熱點key的寫請求的RT. 按照如今的配置,最壞狀況下,客戶端的熱點key的寫操做會增大0.1ms,這個影響是微乎其微的。
點擊查看原文