千萬級併發分佈式KV存儲系統設計實現和運營

設計背景

整體架構

如上圖所示,即爲我們的業務整體系統架構,在我們的整體架構中,VDE(VIP Data Engine)分佈式KV存儲系統是作爲最核心的底層基礎存儲服務存在的,在整體架構中爲周邊各類系統提供數據解耦合、數據共享的功能,大大簡化了業務的整體架構,但同時,由於VDE分佈式KV存儲系統在架構中的基礎和核心地位,系統的可用性、實時性、可靠性和穩定性就極爲的重要了。

推薦業務對底層分佈式KV存儲系統的要求:

千萬級的併發量,10ms響應超時,持久化存儲。
支持動態擴容、縮容、自動容錯恢復。
高性能,減少資源佔用,節省成本。
架構優秀,維護簡單方便。

redis-twemproxy

redis-twemproxy爲Twitter開源、應用範圍最廣、穩定性最高、最久經考驗的分佈式中間件,但是在我們的業務場景中, 對系統高併發、實時性、可擴展性要求非常高,redis-twemproxy的應用就存在着一些制約業務發展的問題:

無法動態擴容、縮容,大促時變更復雜,且業務需要同步變更,變更成本太大。
存儲路徑長、涉及組件多,機器資源佔用比較多,同時,也加大了出問題風險, 歷史上已經發生了若干由於底層數據庫異常而導致數百萬損失的問題。
無法自動容錯,需要較多人工介入恢復,運維不夠友好。

tair

tair是淘寶開源的一套分佈式kv緩存系統,支持動態擴容、縮容、動態容錯恢復。

業務場景

1.數據主要是key-value結構,value的數據爲pb結構或簡單字符串,主要爲put、get、remove操作。
2.數據記錄會不斷覆蓋更新,不要求強一致性,能夠容忍特殊場景下極少量數據的語義不一致。

推薦業務的本質是從低價值的海量數據中進行各種分析計算,得到相對更有價值的數據,然後基於這些數據進行個性化的排序推薦,爲用戶提供差異化的服務體驗,因此,極其少量的數據在系統極端異常場景下出現數據語義不一致並不會對整體效果產生影響,這對於我們在系統設計時,在一致性、高可用、可靠性及成本等方面合理取捨有重要的考量作用。

3.數據也會有併發修改更新的需求,需要確保併發修改更新場景下的數據一致性。

我們會爲數據帶上版本號,業務先get數據,修改後再更新的場景下,系統只會處理第一個更新請求,後續的基於同一版本數據的修改操作均會失敗。

4.數據讀寫需要非常高的實時性,最壞情況下只能接受小於0.5%的10ms超時率,同時,業務峯值併發爲每秒數百萬筆讀寫請求,未來還可能會持續增長,系統設計需要考慮支持千萬級QPS併發能力。
5.數據需要支持持久化存儲,避免集羣異常情況下的大量數據丟失,從而導致集羣不可用。

分佈式KV存儲系統設計實現

我們選擇借鑑tair系統架構和和部分代碼,同時,針對我們的業務場景,自主設計實現了一套VDE分佈式KV存儲系統,系統以高併發、低延遲、可擴展爲主要設計目標,多個副本間採用異步複製策略,數據保證最終一致性。同時,我們在系統設計和實現的過程中,也會在收益與成本之間進行合理評估和取捨,如同二八原則所描述,有時候80%的成本,只能產生20%的收益。

系統架構及設計

系統服務模塊組成

ConfigServer

負責集羣所有DataServer節點信息、數據路由表等元數據的管理,在檢測到下面的事件發生後,ConfigServer會重建路由表,同時會將變更後的路由表下發給DataServer跟Client,引起數據遷移跟數據讀寫重定位:
1.group配置文件更新。
2.data server狀態發生變化(比如原來活着變爲死掉,或者死掉變活)。

DataServer

負責集羣實際的數據存儲跟數據讀寫功能。
1.與Config server進行心跳交互,上報自己的心跳和狀態統計數據。
2.數據更新時,先更新master dataserver節點,成功後立即返回客戶端,然後在後臺異步發送到所有的slave dataserver節點。
3.集羣狀態變化引起路由表重建和數據遷移後,由master dataserver節點負責按桶遷移數據到所有的目標節點。

Client

內部封裝了與集羣的交互過程,包括路由表拉取、更新、數據定位、數據讀寫等功能,爲上層業務提供一組完全透明跟友好的api接口,支持C/C++、Java、Python的api接口。
1.初始化時訪問config server拉取路由表,同時,會將路由表信息緩存到本地內存。
2.接口內部會根據路由表自動計算,定位到對應的dataserver節點進行實際的數據讀寫操作。
3.檢測是否需要更新路由表(判斷錯誤碼是否爲DataServer不work、不是在master dataserver節點讀寫),如果發現需要更新路由表,則去config server拉取最新路由表並進行更新。

集羣路由表

穩定狀態下,ConfigServer、DataServer均保存有3張路由表:快表、遷移表、目標表,
快表

顧名思義,快表即爲用於快速恢復讀寫的路由表,當集羣狀態變化時,會快速得到一張路由表返回給客戶端恢復讀寫操作。以如下路由表中A節點下線爲例,ConfigServer會把對應bucket的節點重新組織,如果下線的節點爲master節點,則會在該桶剩餘其他的node中選擇承載bucket最少的node提升爲master接管數據讀寫操作。如果下線的爲slave節點,則直接刪除對應bucket中節點列表中該下線節點即可。

遷移表

遷移表在集羣穩定狀態下跟快表、目標路由表相同,在集羣狀態變化時,dataserver會根據快表和目標表決定自己要遷移哪些bucket到哪些節點,當dataserver完成某個bucket的遷移後,會上報到config server,config server會生成最新的遷移狀態表,其實,遷移表就是快表到目的對照表的過度狀態表,剛開始與快表相同,當dataserver遷移完所有bucket後,遷移表最終會與目的路由表一樣。

目標路由表

在集羣穩定狀態下,快表、遷移表、目標路由表完全一樣,當集羣狀態變化時,configserver首先會根據集羣負載均衡策略構建最新的目標路由表,該目標路由表即爲集羣再次達到穩定後的狀態。

數據讀寫

上圖所示即爲我們系統的數據讀寫流程,在剛開始或者集羣狀態有變更時,我們會去ConfigServer拉取最新的快表並緩存在自己的內存裏,如果後續集羣狀態正常,客戶端會直接根據內存裏的快表進行hash計算,映射到一個bucket上,然後到這個bucket對應的master node上進行讀寫。

在我們實際的線上運營過程中,我們讀寫都是隻針對master node,我們做了如下考慮和設計:

  1. 儘可能爲業務保證數據的讀寫一致性,如果讀取slave node,則對於實時併發更新的業務而言,容易拿到slave node的一些old的數據進行更新,但是基於讀取的old數據更新後提交到master node會失敗,進而增加了業務的失敗率。
  2. 我們在構建路由表的時候,其實就已經考慮到了master node的負載均衡,master node的讀寫負載跟master node承載的bucket有關,我們構建路由表的策略之一就是使master node承載的bucket儘量均衡。

對於ConfigServer的可用性設計,我們考慮並採用了典型的master + slave模式,master支持元數據讀寫操作,slave只支持元數據讀取操作,我們做了如下考慮和設計:

  1. 主備ConfigServer之間的元數據同步,在一些異常場景下無法保證主備ConfigServer元數據的強一致性,比如master更新完了元數據,在同步給slave之前掛了,如果直接進行主備切換,則有可能會出現集羣狀態異常,甚至無法恢復的嚴重問題,因爲元數據是集羣極爲重要的核心數據。
  2. 我們的集羣一旦達到穩定狀態,集羣的元數據就幾乎不會變更,絕大部分情況下是元數據讀操作,因此,可用性主要是要保證可讀性,且客戶端會緩存元數據,ConfigServer即使短暫的不可用也不影響服務。典型的master + slave機制可以以極低的代價保證我們ConfigServer的可用性,而實際在我們線上近2年的運營過程中,該方案完全滿足了我們對ConfigServer可用性的要求。
  3. 當然,我們採用master + slave機制非常依賴於我們及時發現和恢復master ConfigServer節點,否則,一旦master節點掛掉我們沒有及時恢復,期間如果有dataserver下線,那麼集羣將會無法達到穩定狀態,並且會持續對業務產生影響,比如如果集羣有400個節點,每個節點承載1024/400=2.56個bucket,掛掉一個節點,那麼大概會有2.56/1024的概率會讀寫失敗。如果存在master configserver,則會立馬重建路由表(快表)恢復讀寫,然後啓動數據遷移工作,如果master Configserver掛掉沒有恢復,那麼影響會持續。基於以上考慮,支持configserver的自動主備切還是有必要的,在我們的後續版本中已規劃採用外部組件zookeeper來存儲我們的元數據,以保證我們元數據的強一致性,而不用自己去重新設計實現一套raft或paxos的強一致性協議。

對於數據的持久化和恢復策略,我們做了如下考慮和設計:

  1. mdb存儲引擎本身支持內存映射文件存儲(文件存儲在tmpfs內存文件系統),當節點掛掉、機器未掛掉的情況下,可以直接從內存映射文件中進行恢復。
  2. 在正常狀態下,如果掛掉的節點數少於副本數,則完全可藉助於數據遷移機制完成副本數據的恢復,但是,還存在一種極端的異常場景,bucket對應的副本節點所在的機器全部掛掉,則後面即使機器重啓,內存映射文件會被刪除,對於這種極端場景,考慮到對系統性能影響,我們會將數據操作在後臺異步的記錄到binlog文件,同時會定期的進行checkpoint,當機器掛掉重啓後,啓動副本節點時,可以通過checkpoint + binlog文件進行恢復,當然,可能由於異步寫binlog的機制在節點異常時可能會丟失極少量數據,但是一方面我們討論的這種一個bucket對應三個副本全部掛掉的這種極端異常場景極少發生,另一方面,如果發生了這種極端異常場景,這種極少量數據丟失的損失是我們優先考慮性能收益的設計所能夠預期的。

數據遷移

數據遷移流程

  1. dataserver(ds)定期向configserver(cs)上報心跳消息,cs判斷是否有集羣狀態變更,比如是否有dataserver節點上線或下線。
  2. cs如果檢測到集羣有狀態變更,比如發現超時時間內節點c還沒有給cs發送心跳消息,則認爲節點c已下線,此時,cs會重建路由表,得到最新的快表、遷移表、目標表。同時更新版本號。
  3. ds定期向cs上報心跳消息,心跳消息中會攜帶自己的版本號,cs收到心跳消息後,會判斷ds的版本號跟自己的版本號是否一致,如果不一致,則下發cs中最新的路由表給ds。
  4. ds接收到最新的路由表後,會計算得到自己要遷移的bucket和遷移的目標ds,然後,ds開始逐個bucket進行遷移,將ds上該bucket承載的數據遷移到目標ds,單個bucket遷移完成後,會向cs發送bucket遷移完成消息。
  5. cs收到ds的bucket遷移完成消息後,會更新cs中的遷移表。同時,定期檢查是否所有節點的待遷移bucket均已遷移完成,如果已遷移完成,則將快表、遷移表、目標表全置爲相同,並且,更新版本號,ds下一次心跳消息過來後會判斷併發送最新的路由表給ds,至此,遷移流程結束,集羣最終達到穩定狀態。

動態擴容

proxy機制

這裏要注意的是ds是逐個bucket進行遷移的,期間cs收到單個bucket遷移完成消息,會修改遷移表,但不會修改快表,客戶端依然還是用的原來的快表操作。當桶3的數據被從C遷移到D後,請求依然會落到節點C,而C遷移完成後已經不承載桶3的數據讀寫,如果不對這種情況做處理,則客戶端會出現較高的失敗率,針對此種場景,我們會讓ds進行proxy,會將C上對桶3的操作proxy到節點D。

動態縮容

動態縮容過程中,proxy機制同樣會生效,當master節點上的bucket遷移到其他節點時,遷移完成前對該bucket的後續請求依然會落到該master節點,master節點會proxy到其他節點。

負載均衡和數據安全的思考

集羣的負載分佈依賴於集羣的路由表構建策略,集羣的路由表是通過cs構建的,cs構建路由表的時機:

  1. group文件發生變化,比如修改了group相關配置。
  2. 集羣狀態發生變化,比如有節點上線或下線。

集羣路由表與集羣的數據分佈和讀寫負載分佈對應,因此,要讓集羣的數據分佈和讀寫負載均衡,路由表構建的過程就非常重要了,如下爲三副本下我們的系統典型的路由表結構:

以3副本、5節點、1023個bucket的集羣配置爲例,我們對於我們的系統的負載均衡,在路由表構建時的設計和實現過程中,主要考慮瞭如下的幾個方面的問題:

  1. 每個作爲master的節點平均需要承載1023/5=204個master bucket,同時,每個節點平均需要承載31023/5=613個bucket,每個master節點在平均需要承載的master bucket數量的基礎上,額外最多可承載的bucket數爲1023%5=3,同時,每個節點在平均承載的bucket數基礎上,額外最多可承載的bucket數爲31023%5=4,同時,會考慮將多餘的bucket平均分佈在不同的節點上。
  2. 重建路由表的過程中,還會最終做一次均衡bucket的操作,儘量把所有bucket平均分佈到所有節點上,把所有承載master bucket數跟總bucket數多於平均值的節點上的多餘的bucket數遷移到承載到master bucket跟總bucket數少於平均值的節點上。
  3. 如果系統在重建路由表的過程中,如果發現某個bucket對應的3個副本節點全部掛掉了,那麼我們不會對該bucket重新選擇副本節點,因爲,如果我們重新選擇了節點,那麼這個bucket對應的數據就無法定位到了,數據就丟失了,我們不希望這種場景下我們的數據丟失,因此,我們會跳過這種場景,直到這個bucket對應的3個副本節點有一個節點成功啓動且已經恢復數據後,我們纔會對這個bucket重新選擇缺失的幾個副本,之後選擇的幾個副本會通過數據遷移機制同步到全量的數據。
  4. 關於數據安全性的考量,同一個bucket對應的3個副本,必須要分佈在不同的機器上,否則,數據的安全性和可靠性無法保證,這裏,我們暫時沒有考慮跨機房的數據分佈,我們的集羣都是部署在同一個機房內的機器,因此,在這裏我們只考慮了同機房的不同機器間的數據安全策略。

數據一致性的考慮和設計

關於數據的一致性,我們系統保證的是最終一致性,數據寫入時採用異步複製的策略,對於數據寫入的的異步複製策略,我們做了如下考慮和設計:

  1. 爲了儘可能提高系統的併發性,我們的主備複製採用的是異步複製策略,數據一旦成功寫入master的dataserver節點,立即返回客戶端成功,同時,master dataserver會將同步請求扔到隊列,由後臺線程從對列獲取同步請求並異步的發送給slave的dataserver,同時,如果異步發送過程中發現類似發送失敗、響應超時的同步請求,由於kv操作時冪等的操作,我們可以對這種超時的同步請求進行重試,保證數據最終最終達成一致的。

在上面超時重試的邏輯中,我們還需要解決數據的時序性問題,讓我們考慮這樣一種場景:
key1第一次發送了同步請求給slave ds,還在超時時間內等待異步響應的時候,key1第二次又發送了同步請求給slave ds,如果第二次同步成功,第一次同步失敗,則經過超時時間後,我們會將key1的第一次同步請求進行重試,則會將最新的第二次同步請求覆蓋,從而導致兩者數據不一致。
對於上述的問題,我們在異步發送會在定時器中查找相同的key,取消比自己old的相同的key的請求的超時定時器。

  1. 另一方面,異步的主備複製策略會導致一些極端場景下數據語義不一致的問題,比如,master dataserver更新完本地後,返回了客戶端成功,然後未來得及發送給slave的dataserver就掛掉了,那麼後面客戶端就拿不到這條數據了,對於系統而言,這條記錄是操作失敗的,但是對客戶端而言,操作是成功的,這就產生了語義的不一致,對於這個問題,我們做了如下的考慮和設計:
  1. 我們系統的主要設計目標是高性能及高併發,我們採用最終一致性的異步複製策略,是綜合考慮了我們的系統成本、複雜性和可靠性以及業務場景等多方面因素的結果,採用異步複製的策略,讓我們大大簡化了系統的設計實現,同時,儘可能提高了我們系統的併發能力,如果採用raft、paxos等強一致性協議,雖然保證了數據強一致性,但必然會帶來系統併發能力,相應的增加了系統成本。
  2. 雖然如此,但是我們也需要考慮異步複製的場景所帶來的問題,同時盡力去降低問題帶來的影響,因此,爲了儘可能保證業務數據的一致性,我們在讀寫策略上也做了一些限制,業務只會讀寫master節點的數據,同時,我們採用同機房部署,儘量減少master寫入數據並返回成功情況下,slave未同步到數據的極端異常場景的影響,因爲只有這種情況纔有可能出現數據的語義不一致場景。在我們實際的近2年的線上運營過程中,我們完全保證了極端異常場景下我們數據的可靠性達到個9個9 ~10個9以上。
  1. 在上面的異常場景中,master節點寫入成功,並將數據扔到後臺隊列中,此時,master掛掉,如果後臺隊列中的數據未發送到其他副本,我們認爲此時會產生會將的不一致或者丟失,但是,由於異步發送延遲極低,我們預期這種極端場景下的記錄數影響極少,在我們的測試盒驗證過程中,我們發現即使在高併發的場景下,也只有偶爾會出現極少的若干條記錄存在這種問題,數據幾乎是一扔到後臺隊列,就立馬通過異步的方式發送出去了,在我們線上運營的集羣中,如果單個節點異常的情況下,我們的系統可靠性至少可以達到10個9以上,如果多個節點出現異常,數據可靠性也可達到9個9以上。
  2. 如CAP的理論所介紹,在分佈式系統的設計中,沒有一種設計可以同時滿足一致性,可用性,分區容錯性 3個特性,最多隻能滿足其中兩個,設計時選擇的關鍵點取決於業務場景。業界的redis cluter、mongodb等系統在設計上其實也是面對有類似的問題,redis cluster、mongodb在主備切換時採用了raft算法,raft算法選擇主節點的關鍵在於選擇數據更新最新的節點作爲主節點,但是,由於redis cluster、mongodb均是採用的異步複製或拉取策略,主備切換時必然也存在新的主節點沒有同步到原主節點的所有數據,因此,也會存在數據不一致的場景,即業務數據寫入並得知成功了,但下次去讀取的時候,有可能讀取不到數據。當然,mongodb還提供了writeConcern機制,用以滿足對數據一致性要求較高的業務,總計,還是一句話,選擇的關鍵在於系統所面對的業務場景。

運營

性能數據

爲了準確評估我們系統的性能和負載能力,同時爲了驗收和確認我們的系統是否滿足業務的要求,我們對我們的系統進行了詳細和完備的性能測試。爲了支持我們的性能測試,我們開發了一個vde_benchmark的壓力測試工具。

  1. 爲了獲得理論的穩定的性能數據,我們的vde_benchmark工具支持平滑速度的請求發送。
  2. 爲了獲得線上真實環境的性能數據,我們的性能測試環境的機器及集羣部署方式與我們的線上環境機器配置、部署方式完全一致。

我們的性能測試環境的具體配置如下:
三機三副本、單機四節點配置、可運營cpu佔用50% ~ 60%左右、10ms超時率低於0.1%,單機的操作系統版本及硬件配置如下:
CentOS release 6.6 (Final)、gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11)、24 * Intel® Xeon® CPU E5-2630 v2 @ 2.60GHz、256G內存

在上面的環境下,我們的理論壓測性能數據爲:

壓測類型 key+value長度(字節) ds1 cpu、網卡(KB/s) ds2 cpu、網卡(KB/s) ds3 cpu、網卡(KB/s) TPS(#/s)
set 500 59.9%、r(191763)、t(140874) 59.5%、r(174312)、t(127277) 59.4%、r(175298)、t(129682) 208891
set 1000 60.6%、r(292312)、t(207941) 61.1%、r(270940)、t(191009) 60.3%、r(271907)、t(194431) 198764
set 3000 54.9%、r(616387)、t(453439) 55%、r(561414)、t(404225) 57.4%、r(560129)、t(402664) 157274
get 500 47.7%、r(76228)、t(275857) 47.4%、r(69786)、t(256450) 46.5%、r(69122)、t(246354) 998325
get 1000 46.9%、r(67666)、t(412108) 43.9%、r(60778)、t(375198) 45.6%、r(59560)、t(361730) 881707
get 3000 34.2%、r(55013)、t(715608) 31.3%、r(49978)、t(648818) 31.8%、r(54318)、t(715514) 565959

大促場景下系統資源評估案例

以100臺機器、單機部署4節點爲例,總節點數爲400,採用3副本部署,那麼理論上在1k記錄長度,10ms超時的要求下,可以支撐約662w/s的純set併發或約2939w/s的純get併發,按3:1 ~ 4:1的讀寫比,理論上可支撐的併發爲2367w/s ~ 2481w/s,但是在線上運營時,我們的請求記錄長度主要集中在0 ~ 5K,還有部分請求記錄長度在5K ~ 數百K,同時,受請求波動影響嚴重,我們評估時不能僅僅從理論數據評估,一般都是綜合考慮線上實際的cpu波動情況,以及我們要求的cpu安全運營範圍、還有資源安全冗餘等各類因素,再結合我們的經驗,得到的一個相對保守的用以支撐大促的對外承諾的系統可支撐的最大併發量,然後,我們再根據這個對外承諾的最大併發量,與相關業務溝通確認大促場景下峯值併發請求成倍增長的情況下,推動和確認業務方的降級相關工作,從而確保我們的系統穩定支撐大促。

線上的實際運營環境遠比理論的壓測環境要複雜的多,我們在線上運營過程中實際評估系統資源時,我們會考慮系統可能面對的問題場景,根據線上實際運營情況綜合評估, 主要考慮的問題場景包括瞭如下幾種:

  1. 記錄長度不確定,在線上的請求中,0 ~ 5K長度的請求佔比97%以上,3%的請求分佈在5K ~ 1024K之間。

我們在線上評估時,我們可以主要以0 ~ 5K的記錄長度作爲主要的性能評估依據,但是過長的記錄對系統性的影響也應當考慮,同時,需要思考如何在系統層面儘量降低過長的記錄對系統性能的影響。

  1. 業務請求併發波動,我們的系統接入線上業務時,單機客戶端連接數超過1w+,對於系統而言,容易出現併發波動的情況,無法保證所有請求均勻到達我們系統。

針對上面的問題,我們一方面是盡力去推動業務對我們的系統請求做平滑改造,另一方面,我們也會在資源評估時兼顧瞬時峯值和平均值的問題,儘量以合理的base併發量評估資源,避免過多的資源浪費。

  1. 業務請求中set、get操作均有,且讀寫比有可能不定時變化,大促期間跟平常時候讀寫比也不一樣。

我們線上的業務請求讀寫比主要在3 : 1 ~ 4 : 1左右,線上的讀寫比也應當作爲我們線上評估時需要考慮的因素之一。

線上運營

1.集羣運營數據

當前我們系統部署的節點數700+(全量集羣460+,熱備集羣260+),總存儲記錄條數550億+(全量集羣380億+,熱備集羣170億+),單集羣歷史峯值併發600w/s左右,10ms超時率低於1/10w,99.99%的請求時耗爲0~2ms。
線上業務請求讀寫比爲3:1 ~ 4:1,全量集羣平常情況下承載的讀寫請求2000億次+/天,熱備集羣1000億次+/天,大促情況下每天的讀寫請求還會增長數百億次甚至上千億次。

2.常見問題
在我們的線上運營過程中,我們在平時不斷的去預防各類問題的發生,在平時就把一些可能出現的問題或者推動業務層改造,或者從系統層本身進行優化或限制,避免大促時小風險累計成大風險,甚至造成集羣異常不可用。也正是由於我們對待底層系統的這種敬畏之心,保證了我們系統在近2年的線上運營過程中,雖然歷經多次大促,期間也出現了一些波折,但是,我們的系統始終穩定、可靠的支撐和滿足了我們的業務要求,沒有出現過一次線上問題。

請求熱點

在我們的線上運營過程中,我們經常會碰到熱點數據的問題,熱點數據指的是同一條請求在短時間內被大量集中訪問,這裏的集中訪問又分爲兩種情況:

  1. 同一個進程短時間內對同一條記錄的大量訪問,這種場景一般可以通過在業務層設置緩存來保護底層系統。
  2. 分佈式計算環境下的不同進程對同一條記錄的大量併發訪問,這種場景下我們一般會設法在業務層錯開對同一條記錄的併發訪問,或者讓業務讀取多個副本。

我們目前線上默認設置只讀寫master節點,由於我們在構建集羣路由表的時候,會盡量使每個節點承載的master bucket個數基本均勻,因此,正常情況下,落到每個節點上的請求數也會是基本均勻的。

線上請求的波動性和集中性

線上的請求一個最大的特點就是請求的不確定性,你永遠不會知道下一秒有多少請求會過來,也永遠不會知道每一秒之間接收的請求量會有多大的差異。我們當前的併發量統計是按照1min的總量來得到的平均值,因此,實際的併發量級併發波動情況對我們準確評估資源干擾很大,如果按照波動峯值來評估,則會造成資源浪費。

針對上面的問題,我們主要是從業務層去推動改造業務請求db的模式,由之前的同步、異步無限制方式統一改造爲異步平滑請求模式,我們推動業務改造後,大大降低了業務對系統請求的波動,同時,由於業務統一改成異步請求方式,相同的併發請求下,大大降低了客戶端的資源佔用。

過長的記錄

我們系統支持的最大記錄長度爲1M左右,但是在記錄長度超過500k時,在高併發的場景下,就很容易導致一些請求時耗的抖動,由於mdb存儲引擎在操作時會加鎖,一個請求在mdb存儲引擎中的時間過長,則會阻塞其他的請求,導致其他的請求處理時耗也變長。

針對上面的問題,我們做了如下的處理:

  1. 一方面,我們內部的處理模型爲單個隊列、多個線程消費的模式,避免單個的請求阻塞影響後面請求的處理(這種情況主要是優化不是阻塞在存儲引擎中的場景)。
  2. 另一方面,我們會從系統層面找出這些影響系統性能的請求對應的記錄,然後推動業務去進行優化和改造。當然,這個由於涉及到數據生產和使用方業務處理邏輯的改造,代價會相對較大,因此,一般情況下,我們會對業務明確我們對請求長度的要求,讓業務方在設計數據時就考慮到對存儲系統的影響,避免後續的不必要的修改。

無效請求

在我們的線上運營過程中,發現了大量的無效請求,當然,我們從系統層面無法界定請求是否有效,我們判斷請求是否有效是從業務層面來確定的,這裏的一個主要場景是業務方在讀取的時候,組裝成了一些key是明顯不存在的,我們完全可以推動業務方從業務層面去過濾這種key,以降低對系統的請求,避免資源被浪費,在我們線上的運營過程中,我們發現這種無效的請求有時會佔用10%的請求以上,造成了很大的資源浪費。

過載保護

在我們線上運營的過程中,我們還碰到在大促時由於業務程序的異常,導致集羣併發瞬時暴增,這種情況下,我們及時開啓了我們系統的過載保護功能,限制了我們系統每秒最大的處理請求數,在業務請求併發超過我們的限制時,我們直接丟掉了部分請求,通過提供有損的服務來避免了在這種異常場景下的系統異常甚至雪崩的問題。

權限控制

在我們系統逐漸在部門內部廣泛使用後,不斷的有新的業務接入我們系統,這就帶來了另一個問題,即新的業務方有可能在我們不知情的情況下接入我們系統,帶來了諸如請求波動、過長記錄等各種問題,還導致了大促期間的資源評估難以準確評估,因此,我們對業務方的接入做了權限限制,只允許白名單列表中的機器能夠接入我們的系統,不在白名單列表的老業務暫時依然可以訪問集羣,但後續會慢慢出現異常直至不可用,不在白名單的新業務則直接無法接入系統和訪問集羣,所有新接入的業務必須與我們確認和溝通,當然這種限制不夠徹底,但是能夠很好的控制和評估新用戶接入的問題,對於老用戶和業務,一般情況下,我們會根據從系統層面獲取的業務數據及時主動去溝通和確認,及時控制系統風險,從而保證了無論是在平時還是大促期間,我們的底層kv存儲系統的穩定和可靠運營。

異地雙集羣熱備

如上圖所示,我們目前的跨機房備份採用的是雙集羣熱備模式,分別在兩個機房部署了獨立的兩套集羣,其中一個爲主集羣,460節點,存儲的是全量的業務數據,另一個爲備集羣,260節點,存儲的是核心的業務數據,平常情況下,我們生產數據的業務方會在兩個不同的機房部署完全相同的業務,主集羣接入的是全量的數據生產業務,備集羣只會接入核心的數據生產業務。
線上請求會經過流量分發,70%的線上流量落入主集羣,30%的線上流量落入備集羣(這裏主要是核心的業務場景的的的流量),平常情況下兩套集羣均會正常對外提供服務,一旦有一個機房的集羣出現問題,則會將其他流量全部切換到另一個機房的集羣。

  1. 如果主集羣故障或網絡中斷,則馬上切換到熱備集羣時,但是,服務將會是有損的,因爲備集羣只存儲了核心業務場景的數據,因此,只能支持核心的業務場景。如果備集羣故障或網絡中斷,則所有流量馬上切換到主集羣,此時,數據是全量的,不會對業務場景產生影響。
  2. 我們這裏的雙集羣熱備模式也會存在比較明顯的問題,對業務層不夠友好,且業務端需要重複部署數據生產的程序,且業務越多,這類問題影響越明顯,在這樣的問題背景下,我們在後續的版本中會規劃跨機房數據同步方案的設計和落地。

統計監控

爲了及時和準確觀察我們系統的健康狀態,我們接入了一套實時的監控系統,我們vde系統會定期的上報我們系統的各類數據給監控系統,監控系統收到我們上報的數據後,會做一個全面的展示,這對於我們快速定位線上問題和分析歷史數據提供了很好的方法和手段。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章