構建高可用分布式Key-Value存儲服務

jopen 9年前發布 | 25K 次閱讀 分布式 NoSQL數據庫

前言

當我們構建服務端應用的時候,都會面臨數據存放的問題。不同的數據類型有不同的存放方式,譬如關系型數據通常使用MySQL來存儲,文檔型數據則會考慮使用MongoDB,而這里,我們僅僅考慮最簡單的kv(key-value)。

kv的使用場景很多,一個很典型的場景就是用戶session的存放,key為用戶當前的session id,而value則是用戶當前會話需要保存的一些信息。因為kv的場景很多,所以選擇一個好的kv服務就很重要了。

對于筆者來說,一個不錯的kv服務可能僅僅需要滿足如下幾點就夠了:

  • 協議簡單
  • 高性能
  • 高可用
  • 易擴容

市面上已經有很多滿足條件kv服務,但筆者秉著no zuo no die的精神,決定使用LedisDB + xcodis + redis-failover來構建一個高可用分布式kv存儲服務。

現有解決方案

在繼續說明之前,筆者想說說曾經考慮使用或者已經使用的一些解決方案。

MySQL

好吧,別笑,我真的說的是MySQL。MySQL作為一個關系型數據庫,用來存儲kv性能真心一點都不差。table的結構很簡單,可能如下:

CREATE TABLE kv (
    k VARBINARY(256),
    v BLOB,
    PRIMARY KEY(k),
) ENGINE=innodb;

當我還在騰訊互動娛樂部門的時候,一些游戲項目就僅僅將MySQL作為kv來使用,譬如用來存放玩家數據,游戲服務器通過玩家id讀取對應的數據,修改,然后更新。鑒于騰訊游戲恐怖的用戶量,MySQL能撐住直接就能說明將MySQL作為一個kv來用是可行的。

不過不知道現在還有多少游戲項目仍然采用這種做法,畢竟筆者覺得,將MySQL作為一個kv服務,有點殺雞用牛刀的感覺,MySQL還是有點重了。

Couchbase

Couchbase是一個高性能的分布式NoSQL,它甚至能支持跨data center的備份。筆者研究了很長一段時間,但最終并沒有決定采用,主要筆者沒信心去搞定它的代碼。

Redis

Redis是一個高性能NoSQL,它不光支持kv,同時還提供了其他的數據結構如hash,list,set,zset供外部使用。

筆者在三年前就開始使用Redis,加之Redis的代碼簡單,很容易就能理解掌控。所以一直到現在,筆者都會優先使用Redis來存儲很多非關系型數據。自然對于kv,筆者也是采用Redis來存放的。

但Redis也有一些不足,最大的莫過于內存限制,Redis存儲的總數據大小最好別超過物理內存,不然性能會有問題。同時,筆者覺得Redis的RDB和AOF機制也比較蛋疼,RDB的時候系統可能會出現卡頓,而AOF在rewrite的時候也可能出現類似的問題。

因為內存的限制,所以Redis不能存儲超大量的數據,為了解決這個問題,我們只能采用cluster的方案,但是Redis官方的cluster仍然處于開發階段,并不能真正在生產環境中使用。所以筆者開發了LedisDB

LedisDB

開發LedisDB,主要就是為了解決Redis內存限制問題,它主要有如下特性:

  • 采用Redis協議,大部分Redis的client都能直接使用。
  • 提供類似Redis的API,支持kv,hash,list,set,zset。
  • 底層采用多種db存儲實際數據,支持rocksdb(推薦),leveldb,goleveldb,boltdb,lmdb,沒有Redis內存限制問題,因為將數據放到硬盤里面了。
  • 高性能,參考benchmark,雖然比Redis略慢,但完全可用于生產環境。

一個簡單地例子:

//start ledis server
ledis-server 

//another shell
ledis-cli -p 6380

ledis> set a 1
OK
ledis> get a
"1"

可以看到,LedisDB非常類似Redis,所以用戶能很方便的從Redis遷移到LedisDB上面。在實際生產環 境中,筆者建議底層選擇rocksdb作為其存儲模塊,它不光性能高,同時提供了很多配置方便用戶根據特定情況進行調優(當然,理解這一堆配置可是一件很 蛋疼的事情)。后續,筆者對于LedisDB的使用說明都會是基于rocksdb的。

數據安全

雖然LedisDB能存儲大量數據,并且易于使用,但是作為一個數據存儲服務,數據的安全性是一個非常需要考慮的問題。

  • LedisDB提供了dump和load工具,我們可以很方便的對其備份。在dump的時候,我們僅僅使用的是rocksdb的snapshot 機制,非常快速,同時不會阻塞當前服務。這點可能是相對于Redis RDB的優勢。雖然Redis的RDB在save的時候也是fork一個子進程進行處理,但如果Redis的數據量巨大,仍然可能造成Redis的卡頓。
  • LedisDB提供類似MySQL的binlog支持,任何操作都是寫入binlog之后再最終提交到底層db的。如果服務崩潰,我們能通過 binlog進行數據恢復。binlog文件有大小限制,當超過閥值之后,LedisDB會寫入一個新的binlog中,而不是像Redis的AOF一樣 進行rewrite處理。
  • LedisDB支持同步或者異步replication,同步復制能保證數據的強一致,但是會犧牲系統的性能,而異步復制雖然高效,但可能會面對 數據丟失問題。這其實就是一個CAP選擇問題,在P(partition tolerance)鐵定存在的情況下,選擇C(consistency)還是選擇A(availability)?通常情況下,筆者會選擇A。

故障轉移

在生產環境中,為了保證數據安全,一個master我們會通常配備一個或者多個slave(筆者喜歡將其稱為replication topology),當master當掉的時候,監控系統會選擇一個最優的slave(也就是擁有master數據最多的那個),將其提升為新的 master,并且將其他slave指向該new master。這套流程也就是我們通常說的failover。

Redis提供了sentinel機制來實現整個replication topology的failover。但sentinel是跟redis綁定的,所以不能直接在LedisDB上面使用,所以筆者開發了redis-failover,一個能支持redis,或者LedisDB failover的sentinel。

redis-failover通過定期向master發送role命令來獲知當前replication topology,主要是slaves的信息。當master當掉之后,redis-failover就會從先前獲取的slaves里面選擇一個最優的slave,提升為master,選擇最優的算法很簡單,通過info命令得到”slave_priority”和”slave_repl_offset”,如果哪個slave的priority最大,就選擇那個,如果priority都一樣,則選擇replication offset最大的那個。

redis-failover會存在單點問題,所以redis-failover自身需要支持cluster。redis-failover的 cluster在內部選舉一個leader用來進行實際的monitor以及failover處理,當leader當掉之后,則進行重新選舉。

現階段,redis-failover可以通過外部的zookeeper進行leader選舉,同時也支持內部自身通過raft算法進行leader選舉。

分布式集群

隨著數據量的持續增大,單臺機器最終無法存儲所有數據,我們不得不考慮通過cluster的方式來解決,也就是將數據放到不同的機器上面去。

要構建LedisDB的cluster,筆者考慮了如下三種方案,這里,我們不說啥hash取模或者consistency hash了,如果cluster真能通過這兩種技術簡單搞定,那還要這么費力干啥。

  • Redis cluster。

    redis cluster是redis官方提供的cluster解決方案,性能高,并且能支持resharding。可是直到現在,redis cluster仍處于開發階段,至少筆者是不敢將其用于生產環境中。另外,筆者覺得它真的很復雜,還是別浪費腦細胞去搞定這套架構了。

  • 定制client。

    通過定制client,我們可以知道不同key的路由規則,自然就能找到實際的數據了。這方面的工作我的一位盆友正在進行,但定制client有一個很嚴重的問題在于所有的client都必須自己實現,其實不算是一個通用的解決方案。

  • Proxy

    記得有人說過,計算機科學領域的任何問題, 都可以通過添加一個中間層來解決。而proxy則是用來解決cluster問題的一個中間層。

    Twemproxy是一個很不錯的選擇,但是它不能支持resharding,而且貌似推ter內部也沒在使用了,所以筆者并不考慮使用。

    本來筆者打算自己寫一個proxy,但這時候,codis橫空出世,它是一個分布式的proxy,同時支持resharding,并且在豌豆莢的生產環境中得到驗證,筆者立刻就決定使用codis了。

    但codis并不支持LedisDB,同時為了滿足他們自身的需求,使用的也是一個修改版的redis,鑒于此,筆者實現了xcodis,一個基于codis的,支持LedisDB以及原生redis的proxy。

架構

最終的架構如下。

構建高可用分布式Key-Value存儲服務kv architecture

我們通過使用LedisDB來解決了Redis單機數據容量問題,通過replication機制保證數據安全性,通過redis-failover用來進行failover處理,最后通過xcodis進行集群管理。

當前,這套架構并沒有在生產環境中得到驗證,但我們一直在內部不斷測試,而且國外也有用戶在幫助筆者驗證這套架構,所以筆者對其還是很有信心的,希望能早日上線。如果有哪位童鞋也對這套架構感興趣,想吃螃蟹的,筆者非常愿意提供支持。

來自:http://siddontang.com/2015/03/06/build-ha-distributed-kv-service/

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!