Tair ldb(leveldb存儲引擎)實現介紹

簡介

tair 是淘寶本身開發的一個分佈式 key/value 存儲引擎. tair 分爲持久化和非持久化兩種使用方式. 非持久化的 tair 能夠當作是一個分佈式緩存. 持久化的 tair 將數據存放於磁盤中. 爲了解決磁盤損壞致使數據丟失, tair 能夠配置數據的備份數目, tair 自動將一份數據的不一樣備份放到不一樣的主機上, 當有主機發生異常, 沒法正常提供服務的時候, 其於的備份會繼續提供服務.java

tair 的整體結構

tair 做爲一個分佈式系統, 是由一箇中心控制節點和一系列的服務節點組成. 咱們稱中心控制節點爲config server. 服務節點是data server. config server 負責管理全部的data server, 維護data server的狀態信息. data server 對外提供各類數據服務, 並以心跳的形式將自身情況彙報給config server. config server是控制點, 並且是單點, 目前採用一主一備的形式來保證其可靠性. 全部的 data server 地位都是等價的.redis

tair 的負載均衡算法是什麼

tair 的分佈採用的是一致性哈希算法, 對於全部的key, 分到Q個桶中, 桶是負載均衡和數據遷移的基本單位. config server 根據必定的策略把每一個桶指派到不一樣的data server上. 由於數據按照key作hash算法, 因此能夠認爲每一個桶中的數據基本是平衡的. 保證了桶分佈的均衡性, 就保證了數據分佈的均衡性.算法

增長或者減小data server的時候會發生什麼

當有某臺data server故障不可用的時候, config server會發現這個狀況, config server負責從新計算一張新的桶在data server上的分佈表, 將原來由故障機器服務的桶的訪問從新指派到其它的data server中. 這個時候, 可能會發生數據的遷移. 好比原來由data server A負責的桶, 在新表中須要由 B負責. 而B上並無該桶的數據, 那麼就將數據遷移到B上來. 同時config server會發現哪些桶的備份數目減小了, 而後根據負載狀況在負載較低的data server上增長這些桶的備份. 當系統增長data server的時候, config server根據負載, 協調data server將他們控制的部分桶遷移到新的data server上. 遷移完成後調整路由. 固然, 系統中可能出現減小了某些data server 同時增長另外的一些data server. 處理原理同上. 每次路由的變動, config server都會將新的配置信息推給data server. 在客戶端訪問data server的時候, 會發送客戶端緩存的路由表的版本號. 若是data server發現客戶端的版本號過舊, 則會通知客戶端去config server取一次新的路由表. 若是客戶端訪問某臺data server 發生了不可達的狀況(該 data server可能宕機了), 客戶端會主動去config server取新的路由表.緩存

發生遷移的時候data server如何對外提供服務

當遷移發生的時候, 咱們舉個例子, 假設data server A 要把 桶 3,4,5 遷移給data server B. 由於遷移完成前, 客戶端的路由表沒有變化, 客戶端對 3, 4, 5 的訪問請求都會路由到A. 如今假設 3還沒遷移, 4 正在遷移中, 5已經遷移完成. 那麼若是是對3的訪問, 則沒什麼特別, 跟之前同樣. 若是是對5的訪問, 則A會把該請求轉發給B,而且將B的返回結果返回給客戶, 若是是對4的訪問, 在A處理, 同時若是是對4的修改操做, 會記錄修改log.當桶4遷移完成的時候, 還要把log發送到B, 在B上應用這些log. 最終A B上對於桶4來講, 數據徹底一致纔是真正的遷移完成. 固然, 若是是由於某data server宕機而引起的遷移, 客戶端會收到一張中間臨時狀態的分配表. 這張表中, 把宕機的data server所負責的桶臨時指派給有其備份data server來處理. 這個時候, 服務是可用的, 可是負載可能不均衡. 當遷移完成以後, 才能從新達到一個新的負載均衡的狀態.安全

桶在data server上分佈時候的策略

程序提供了兩種生成分配表的策略, 一種叫作負載均衡優先, 一種叫作位置安全優先: 咱們先看負載優先策略. 當採用負載優先策略的時候, config server會盡可能的把桶均勻的分佈到各個data server上. 所謂儘可能是指在不違背下面的原則的條件下儘可能負載均衡. 1 每一個桶必須有COPY_COUNT份數據 2 一個桶的各份數據不能在同一臺主機上; 位置安全優先原則是說, 在不違背上面兩個原則的條件下, 還要知足位置安全條件, 而後再考慮負載均衡. 位置信息的獲取是經過 _pos_mask(參見安裝部署文檔中關於配置項的解釋) 計算獲得. 通常咱們經過控制 _pos_mask 來使得不一樣的機房具備不一樣的位置信息. 那麼在位置安全優先的時候, 必須被知足的條件要增長一條, 一個桶的各份數據不能都位於相同的一個位置(不在同一個機房). 這裏有一個問題, 假如只有兩個機房, 機房1中有100臺data server, 機房2中只有1臺data server. 這個時候, 機房2中data server的壓力必然會很是大. 因而這裏產生了一個控制參數 _build_diff_ratio(參見安裝部署文檔). 當機房差別比率大於這個配置值時, config server也再也不build新表. 機房差別比率是如何計出來的呢? 首先找到機器最多的機房, 不妨設使RA, data server數量是SA. 那麼其他的data server的數量記作SB. 則機房差別比率=|SA – SB|/SA. 由於通常咱們線上系統配置的COPY_COUNT是3. 在這個狀況下, 不妨設只有兩個機房RA和RB, 那麼兩個機房什麼樣的data server數量是均衡的範圍呢? 當差別比率小於 0.5的時候是能夠作到各臺data server負載都徹底均衡的.這裏有一點要注意, 假設RA機房有機器6臺,RB有機器3臺. 那麼差別比率 = 6 – 3 / 6 = 0.5. 這個時候若是進行擴容, 在機房A增長一臺data server, 擴容後的差別比率 = 7 – 3 / 7 = 0.57. 也就是說, 只在機器數多的機房增長data server會擴大差別比率. 若是咱們的_build_diff_ratio配置值是0.5. 那麼進行這種擴容後, config server會拒絕再繼續build新表.網絡

tair 的一致性和可靠性問題

分佈式系統中的可靠性和一致性是沒法同時保證的, 由於咱們必須容許網絡錯誤的發生. tair 採用複製技術來提升可靠性, 而且爲了提升效率作了一些優化, 事實上在沒有錯誤發生的時候, tair 提供的是一種強一致性. 可是在有data server發生故障的時候, 客戶有可能在必定時間窗口內讀不到最新的數據. 甚至發生最新數據丟失的狀況.app

tair提供的客戶端

tair 的server端是C++寫的, 由於server和客戶端之間使用socket通訊, 理論上只要能夠實現socket操做的語言均可以直接實現成tair客戶端. 目前實際提供的客戶端有java 和 C++. 客戶端只須要知道config server的位置信息就能夠享受tair集羣提供的服務了.負載均衡

主要的性能數據

  待補充框架

 

一. 修改配置

leveldb中有一系列參數會與讀寫的效率有關,將相關的配置以及編譯常量統一修改爲可運行時配置參數,測試選取最佳配置值。socket

二. 實例個數控制

首先肯定在一個tair server上要啓幾個leveldb實例?
Tair中以桶來組織數據,若是按照一個桶一個leveldb實例,在作遷移複製的時候會很方便,但考慮若是在一塊磁盤上起多個實例,那麼總體看來,多個順序寫變成了隨機寫,每一個實例的compact進程會加重整個磁盤的隨機IO,因此並不採用每一個桶一個實例,而是針對磁盤的數量由相關配置控制實例的個數。

三. 內部key的格式

爲實現Tair中的功能邏輯,ldb傳入leveldb的user-key格式以下:

1)    Tair中的數據能夠設置過時時間,過時時間保存能夠保存在在value的meta中,但考慮能在leveldb內部提早檢查,省去解析value的消耗,將過時時間保存在key中,但並不參與排序。
2)    Tair中的數據組織以及遷移複製都是以桶(bucket)單位,爲得到一個桶的數據,添加桶號前綴,保證一個桶的數據都存儲在一塊兒。leveldb內部對key作前綴壓縮,桶號基本均可以被壓縮掉。
3)    Tair中的數據區分namespace,會將namespace做爲客戶端傳入key的前綴存儲。

四. 自定義comparator

爲了實現Tair中的功能邏輯,實現自定義的comparator傳入leveldb,實現自定義的排序邏輯(傳入leveldb的user-key中表示過時時間的前四個字節不參與排序),併爲comparator添加兩個判斷數據是否有效的邏輯接口(ShouldDrop()/ShouldDropMaybe()),修改leveldb內部作遍歷以及compact時判斷數據是否有效(kTypeValue/kTypeDeletion)的邏輯。
1)    ShouldDropMaybe(): 用來判斷數據是否已通過期。解析key中的expired_time便可。
2)    ShouldDrop(): 用來判斷數據是否屬於已經清理掉的數據(bucket已經遷移或者namespace已經被清理)。
3)    區分過時數據與清理數據的判斷,是由於丟棄過時數據必須保證該key是數據的最後出現,不然刪除該數據會讓該key失去最後的更新狀態,而清理數據有gc信息保證,不須要關心數據的狀態。

五. 清理數據/自定義的內部compact邏輯

這裏把清理數據的操做稱爲gc。
1.    遷移的bucket以及清理的namespace中的數據
一旦發生bucket遷移或者清理namespace,會把相應的信息保存下來(GcNode)

 struct GcNode
 {
   // 遷移走的bucket或者清掉的namespace
   int32_t key_;
   // 清理髮生時的SequnceNumber,用來判斷數據的時間點
   uint64_t sequence_;
   // 清理髮生時的FileNumber,用來縮小compact的範圍
   uint64_t file_number_;
   // 清理髮生時的時間
   uint32_t when_;
 };

1)    Tair中會有桶遷移或者namespace清理的操做,廢棄的數據並不會馬上清理,依靠後續的compact進程清理。但同時桶又可能被遷移回來,namespace也可能繼續使用。leveldb中的SequnceNumber剛好能夠標識數據的時間點,因此在作數據清理的時候,記錄下當時的SequnceNumber(sequnce_),在作判斷的時候,只有小於sequnce_的數據才認爲能夠被丟棄。
2)    filenumber_是爲了縮小主動compact的範圍。
leveldb自身進行compact的過程當中,加入自實現comparator的ShouldDropMaybe()/ShouldDrop()判斷邏輯,會將對應的gc數據清理。但leveldb自身的compact進程並不能保證將全部的數據清理掉,因此添加compact的定時線程,主動觸發compact作數據清理。對於清理的bucket或者namespace,根據key的格式,能夠構造出須要compact的key-range,但直接使用leveldb提供的CompactRange有如下問題:
1)    若是某個sstable在記錄gc以後已經被compact過,所要清理的數據就已經丟棄掉了,並不須要再作compact。
2)    主動觸發的compact並非基於均衡db狀態,因此形成的level遷移可能會有副作用。
對於1)2)的問題,作如下策略:
1)    FileNumber全局遞增能夠用來標識時間點,gc時,記錄下當時的FileNumber,主動compact時,只須要選取符合key-range而且FileNumber小於記錄的filenumber_的sstable便可,縮小須要compact的sstable範圍。
2)    主動觸發的compact只選取level-n中的sstable,compact後,也只生成level-n中的sstable,level之間的均衡,仍由內部compact進程負責。
3)    基於上述1) 2),爲leveldb添加CompactRangeSelfLevel()邏輯,實現1) 2)中的策略,主動觸發compact以清理數據,整個db的均衡仍由內部的機制保證。
gc信息會同時記binlog,在server重啓時replay。

2.    過時的數據
由於過時是一個持續的時間狀態,若是要徹底回收過時的數據,只能對全db作compact,這樣作的性能比不合算,因此目前對過時數據不作特別的主動清理,依靠內部以及外部觸發的compact進程中回收空間。

六. 上層cache

leveldb內部有sstable元信息和block數據的cache,優化讀的效率,在leveldb上層再嵌入Tair自帶的mdb作KV層面的cache,並添加cache的信息統計。當前統計看到,熱點數據的讀基本落在mdb中。

七. 後續的優化

1)    leveldb自己的一些優化(參見leveldb的實現解析)。
2)    優化網絡的使用,使用新的網絡框架。
3)    ssd使用優化。leveldb內部作的一些細節優化針對於sas盤的IO性能,當前使用的ssd IO能也未充分利用。後續針對提高ssd使用性能作相應的IO優化(dio)。
4)     內存的優化使用。大內存的使用有很大的優化餘地,避免swap的使用,pagecache的相應回寫策略配置。
5)    對不存在數據查找的優化,採用mock value處理或者添加bloomilter。
6)    range 以及數據dump功能的實現。

歡迎你們對leveldb的使用優化提出寶貴建議。

廣告時間: Tair如今已經將Redis的存儲部分抽離出來,做爲非持久化的存儲引擎rdb,即將上線使用,歡迎你們強勢圍觀。

相關文章
相關標籤/搜索