餓了麼分散式KV架構與實踐

tianxiaoxu發表於2018-09-11

本文根據趙子明老師在2018年5月12日【第九屆中國資料庫技術大會(DTCC)】現場演講內容整理而成。

講師簡介:

趙子明,一個熟悉基礎架構和應用架構的雙料胖子,曾在大麥網、京東工作。設計開發過Zookeeper多活(跨機房雙向複製)、分散式kv、API閘道器等元件,搞過幾十個微服務組成的交易系統的梳理優化,有單應用到微服務的演化經歷,有高併發下系統效能優化經驗。

摘要:

隨著網際網路和大資料的發展,各種NoSQL資料庫層出不窮,在不同的場景下衍生出了不同的產品,本次分享和大家聊聊餓了麼分散式kv的設計架構與實踐總結。主要內容:1.餓了麼分散式kv的架構和實現;2.和其他NoSQL對比有什麼異同;3.在哪些場景下使用以及遇到什麼問題;4.思考與總結。

分享大綱:

  • 背景介紹

  • 餓了麼分散式kv架構簡介

  • 使用場景&問題

  • 總結&展望

正文演講:

一.背景介紹

隨著網際網路的快速發展,資料量的極速增加,效能要求的提高……這些要求傳統資料庫往往會因擴充套件性和效能方面的制約而無法滿足,NoSQL在此時大顯神威,通過放棄表關聯、多行事務等特性來追求效能、擴充套件性以及低成本。

舉個例子,在關係型資料庫中多表關聯查詢join是很容易做到,但在分散式場景下面,不同的表會分佈在不同的機器上,中間的關聯關係比較複雜,如果要把這麼多機器裡的不同資料片段做聚合成本太高了,同時效能也會下降。

隨著開源資料庫越來越多,NoSQL資料庫也百花齊放,各種表格、kv在很多場景下都應用得很好。

我挑選了三款比較典型的NoSQL資料庫來做對比,首先是HBase,HBase是建在HDFS之上的分散式、面向列的表格系統,雖然它是半實時的,但更多面向的是資料分析場景,並在列存和行存之間取了個平衡。Dynamo是亞馬遜大牛提出的,經典的分散式KV,應用了很多分散式的技術。Pika也是很好用的開源產品,可持久化的大容量Redis儲存服務。

HBase資料分片方式是有順序的region,Dynamo用的是Hash。HBase是中心架構,中間有主從,而Dynamo是去中心架構p2p,其應用的很多技術都有借鑑意義,例如叢集管理、資訊發現、向量時鐘等等。Pika更像是一個單機Redis,主從、叢集都需要自己做。

HBase支援強一致,如果更確切的來說是支援單行某個列族的強一致,Dynamo的一致性是可配置的,要在一致性和讀寫效能之間做一些取捨,Pika這裡就不多談了。

HBase基於HDFS,有些情況下會做遠端呼叫,它在大資料讀寫分析的場景下表現很好,但如果用線上上,讀寫效能會稍微弱一點。Dynamo採用本地儲存,讀寫效能較高,並且可以根據Quorum NRW做一些場景化配置優化來提高效能。Pika相對於記憶體級Redis來說效能會差一些,但是作為磁碟來說,它是一個高效能的儲存。

HBase需要部署zookeeper、HDFS等等,部署維護比較複雜。Dynamo都是對等節點,部署維護比較簡單。 把Pika類比成單機Redis,如果我們要做叢集,相對來說成本會更大一些,需要自己處理資料分片規則、主從結構,資料複製,異常恢復等問題。

在餓了麼資料量的快速增長,但內部沒有專門的KV元件,很多同學都是按照自己的習慣選擇儲存元件,對於一些簡單的資料,有人選擇存MySQL、Redis,也有人會存MongoDB、Cassandra。

Redis這種記憶體級的元件是更適合超熱的資料,效能很高。但在超大資料集的大資料場景下不可能把資料全部放入記憶體,需要將Redis結合其他磁碟儲存元件一起使用,但很多時候沒有整體的解決方案,成本較高,效能較差。

這樣的背景下體現那些問題?擴充套件性差、效能不達標,其次就是運維成本比較高,以及無法滿足某些場景下對於資料可靠的高要求。

我們對KV的要求:

  • 首先因為我們這邊大多是線上場景,所以要求高效能,延遲要在毫秒級別;

  • 其次是大資料量,能達到幾十TB;

  • 第三是高可靠,一旦出現問題最好能通過自運維的方式自愈;

  • 第四是在任何情況下都要實現高可用;

  • 第五是易運維,系統實現的複雜一點,運維能夠更方便;最後是強一致,無論叢集怎麼掛或者切換,資料都保證不丟失。

二.餓了麼分散式kv架構簡介

上圖是整體架構,我們從去年下半年開始著手做這件事,兩三個月之後上線。能用較短的時間地完成上線要感謝開源,我們基於開源產品做封裝提供服務。在這個架構中,我們做了一個代理層,支援Java、Python、Go等客戶端。

底層架構是一箇中心化的架構,MetaServer存叢集的元資訊,會來處理一些心跳上報、叢集管理命令等等。對於DataServer如果是要求強一致就使用TiKV,如果是要求低成本就使用Pika。

下面重點介紹一下proxy,proxy基於Go語言開發的,支援多種協議實現。現在支援KV和Redis協議以及結構實現。除此之外,還包括路由、KVsdk、限流、監控等模組。

總體來說我們這個架構是基於開源,支援多協議,目前主要支援Redis協議;

易擴充套件,在底層採用TiKV時新節點加入叢集,可以自動做到資料的均衡和資料的遷移;

資料一致性與高可用,採用Raft,能保證資料不丟;

可定製,我們可以根據不同的場景定製不同的功能需求和效能優化。

使用場景,主要使用在搜尋、推薦,促銷、智慧排程,商品分類,排序等場景。從餓了麼首頁往下點,大概有80%的操作流程和KV有關,這些KV資料多是屬性資訊,比如使用者畫像、商品屬性等等。

經過半年多的發展,我們現在有十幾套叢集分佈在四個機房,線上資料已有幾十TB,每天百億級呼叫。某些叢集高峰QPS大於10萬,平均響應時間在1到2毫秒。

三.使用場景&問題

在這個過程中,我們遇到了很多問題,首先是上線之後,我們發現一些場景下key的Value特別大,達到幾十K甚至上M,比如在使用者畫像。kv在這種value比較大的情況下效能損耗會比較大。我們的處理方式比較很簡單,就是壓縮。

我們綜合儲存、IO、網路等因素做了一些測試,上圖分別是高壓縮比、低圧縮比和中壓縮比的測試資料,通過對比,我們發現LZ4的效果最明顯,雖然壓縮率一般但是效能高。所以我們把它做到了proxy,從進入系統開始就壓縮,使得整個鏈路包括中間Raft協議的幾次傳輸,代價更小。 通過加壓縮,一些場景效能提高了50%。

另一個問題是跨機房操作慢,餓了麼已經做了異地多活,大多數情況下都是讀本機房的資料,但個別場景需要跨機房來運算元據,延遲很大,比如北京上海之間延遲時間達到20到30毫秒。我們的解決方法是pipeline。

我們採用了批量加並行的優化手段,類似於Redis的pepiline,如圖左邊顯示set key n次請求有2n次網路傳輸,我們將多個set 操作打成一個batch 只需2次網路傳輸。通過減少中間網路互動,效能提升效果明顯,我們基於Go語言做的proxy,內部基於協程做了並行處理也提高了部分效能。

Hash多行操作超慢是實現方式的問題,我們將Hash結構分多行儲存,原始Key和原始Feild拼成一個key,多個Field順序儲存。

涉及成百上千行資料的多行操作,需要掃描很多行,效能損耗較大。我們做了取捨,把Hash只用一個Key來儲存,這樣只要這個Hash小於1M且Key的 數量不多的話,效能提高效果明顯。

除此之外,還遇到一個問題——加ttl以後效能損耗比較大。加ttl其實就相當於Hash加了一個metakey,metakey裡面存在Hash超出時間戳,讀的時候拿時間戳做校驗。(內部用定時任務清理過期Key)。但這樣做會多一次讀取校驗影響效能。

其實,很多場景並不需要嚴格的ttl校驗或型別校驗。我們做了場景化的優化,針對某些場景單獨做開關,避免了meta的ttl驗證。通過這種方式,效能比之前提高了30%。

四.總結&展望

我們的KV規劃支援多協議,目前主要支援Redis協議。大多數開發同學對Redis比較熟悉,學習成本低。為了迅速的推廣,我們優先支援Redis協議。之後我們會支援更多其他的協議。

TiKV容錯能力很強,因為它底層採用的是Raft,一臺機器掛掉,其他機器會選主,繼續提供服務。從軟體層協議實現上去解決一致性問題以及高可用問題。運維成本會降低。

我們準備做資源池化加全自動化運維,比如每次使用者申請接入,他們只要提交一個工單,然後我們就調一下指令碼,自動化把叢集建立出來,並根據需求特性做不同的配置。有一個資源池,如果需要申請服務自動從資源池裡提取、部署,如果叢集下線或資料縮容的情況,就把它退回資源池。

異地多活,為了適應餓了麼整體架構,我們也規劃做到異地多活,多地之間的雙向同步。

最後為大家分享下個人的總結和思考。

第一個是分佈和均衡,在儲存裡面的資料分佈是需要得多方考慮權衡的事情,例如選擇那種資料分佈方式?Hash還是Region?Hash在資料分佈上更均衡Region儲存方式利於順序掃描,但不利於分佈均衡,如果某個Region資料量越來越大,那麼就需要Region分裂,如果某Region資料量隨著資料刪除資料量變小就需要合併。除了資料分佈的均衡,還要考慮流量均衡,Hash天然就比較均衡,而Region則需要做一些特殊處理來做到均衡,如果寫順序寫入會存在熱點寫問題。總體來看,Region分佈的成本更高一點。故障隔離,隔離有好多等級,比如節點級、機架級、機房級等。如果分佈的越散,這個系統的可用性越高,如果越集中,效能越高,所以都得綜合考量,在效能和高可用之間做選擇。

實現成本和運維成本,在分散式儲存裡實現Raft,開發測試成本很大,但是運維成本降低,叢集容忍單機故障,軟體層面能自動容錯。使用Pika等單機儲存,我們需要自己做叢集管理、主從,用哨兵等方式來做監控,這樣的方式自己部署的元件較多,運維成本比較大。我們直接使用了比較牛的開源元件,效果不錯。不過以後肯定會遇到更多底層問題,這就需要我們投入更大的成本來解決問題。

輪子這麼多為什麼還要造?我們拿到了一個開源元件,想要它適合我們的技術體系、業務場景,需要做很多打磨。一些開源產品在大規模商用場景下,運維成本比較高。自己造輪子能從中需積累專業底層經驗,總結最佳實踐,優化效能和資源。更大限度提高穩定性優化資源 。

最後我們要感謝開源,開源產品充實我們的技術力量,幫我們降低成本、提高效率。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31137683/viewspace-2213967/,如需轉載,請註明出處,否則將追究法律責任。

相關文章