淘寶開源Key/Value結構數據存儲系統Tair技術剖析
Tair是由淘寶網自主開發的Key/Value結構數據存儲系統,在淘寶網有著大規模的應用。您在登錄淘寶、查看商品詳情頁面或者在淘江湖和好友“搗漿糊”的時候,都在直接或間接地和Tair交互。
Tair于2010年6月30號在淘寶開源平臺上正式對外開源,本文較詳細地介紹了Tair提供的功能及其實現的細節,希望對大家進一步了解Tair有所幫助。
Tair的功能
Tair是一個Key/Value結構數據的解決方案,它默認支持基于內存和文件的兩種存儲方式,分別和我們通常所說的緩存和持久化存儲對應。
Tair除了普通Key/Value系統提供的功能,比如get、put、delete以及批量接口外,還有一些附加的實用功能,使得其有更廣的適用場景,包括:
- Version支持>
- 原子計數器
- Item支持
Version支持
Tair中的每個數據都包含版本號,版本號在每次更新后都會遞增。這個特性有助于防止由于數據的并發更新導致的問題。
比如,系統有一個value為“a,b,c”,A和B同時get到這個value。A執行操作,在后面添加一個d,value為“a,b,c,d”。B執行操作添加一個e,value為”a,b,c,e”。如果不加控制,無論A和B誰先更新成功,它的更新都會被后到的更新覆蓋。
Tair無法解決這個問題,但是引入了version機制避免這樣的問題。還是拿剛才的例子,A和B取到數據,假設版本號為10,A先更新,更新成功后,value為”a,b,c,d”,與此同時,版本號會變為11。當B更新時,由于其基于的版本號是10,服務器會拒絕更新,從而避免A的更新被覆蓋。B可以選擇get新版本的value,然后在其基礎上修改,也可以選擇強行更新。
原子計數器
Tair從服務器端支持原子的計數器操作,這使得Tair成為一個簡單易用的分布式計數器。
Item支持
Tair還支持將value視為一個item數組,對value中的部分item進行操作。比如有個key的value為[1,2,3,4,5],我們可以只獲取前兩個item,返回[1,2],也可以刪除第一個item,還支持將數據刪除,并返回被刪除的數據,通過這個接口可以實現一個原子的分布式FIFO的隊列。
Tair的內部結構
![](https://imageproxy.pixnet.cc/imgproxy?url=https://pic001.cnblogs.com/img/wangfengmadking/201009/2010090819393635.jpg)
圖 1 Tair整體架構圖
一個Tair集群主要包括client、configserver和dataserver 3個模塊。Configserver通過和dataserver的心跳(HeartBeat)維護集群中可用的節點,并根據可用的節點,構建數據的在集群中的分布信息(見下文的對照表)。Client在初始化時,從configserver處獲取數據的分布信息,根據分布信息和相應的dataserver交互完成用戶的請求。Dataserver負責數據的存儲,并按照configserver的指示完成數據的復制和遷移工作。
數據的分布
分布式系統需要解決的一個重要問題便是決定數據在集群中的分布策略,好的分布策略應該能將數據均衡地分布到所有節點上,并且還應該能適應集群節點的變化。Tair采用的對照表方式較好地滿足了這兩點。
對照表的行數是一個固定值,這個固定值應該遠大于一個集群的物理機器數,由于對照表是需要和每個使用Tair的客戶端同步的,所以不能太大,不然同步將帶來較大的開銷。我們在生產環境中的行數一般為1023 。
對照表簡介
下面我們看對照表是怎么完成數據的分布功能的,為了方便,我們這里假設對照表的行數為6。最簡單的對照表包含兩列,第一列為hash值,第二列為負責該hash值對應數據的dataserver節點信息。比如我們有兩個節點192.168.10.1和192.168.10.2,那么對照表類似:
0 |
192.168.10.1 |
1 |
192.168.10.2 |
2 |
192.168.10.1 |
3 |
192.168.10.2 |
4 |
192.168.10.1 |
5 |
192.168.10.2 |
當客戶端接收到請求后,將key的hash值和6取模,然后根據取模后的結果查找對照表。比如取模后的值為3,客戶端將和192.168.10.2通信。
對照表如何適應節點數量的變化
我們假設新增了一個節點——192.168.10.3,當configserver發現新增的節點后,會重新構建對照表。構建依據以下兩個原則:
- 數據在新表中均衡地分布到所有節點上。
- 盡可能地保持現有的對照關系。
更新之后的對照表如下所示:
0 |
192.168.10.1 |
1 |
192.168.10.2 |
2 |
192.168.10.1 |
3 |
192.168.10.2 |
4 |
192.168.10.3 |
5 |
192.168.10.3 |
這里將原本由192.168.10.1負責的4和192.168.10.2負責的5交由新加入的節點192.168.10.3負責。
如果是節點不可用,則相當于上述過程反過來,道理是一樣的。
多備份的支持
Tair支持自定義的備份數,比如你可以設置數據備份為2,以提高數據的可靠性。對照表可以很方便地支持這個特性。我們以行數為6,兩個節點為例,2個備份的對照表類似:
0 |
192.168.10.1 |
192.168.10.2 |
1 |
192.168.10.2 |
192.168.10.1 |
2 |
192.168.10.1 |
192.168.10.2 |
3 |
192.168.10.2 |
192.168.10.1 |
4 |
192.168.10.1 |
192.168.10.2 |
5 |
192.168.10.2 |
192.168.10.1 |
第二列為主節點的信息,第三列為輔節點信息。在Tair中,客戶端的讀寫請求都是和主節點交互,所以如果一個節點不做主節點,那么它就退化成單純的備份節點。因此,多備份的對照表在構建時需要盡可能保證各個節點作為主節點的個數相近。
當有節點不可用時,如果是輔節點,那么configserver會重新為其指定一個輔節點,如果是持久化存儲,還將復制數據到新的輔節點上。如果是主節點,那么configserver首先將輔節點提升為主節點,對外提供服務,并指定一個新的輔節點,確保數據的備份數。
多機架和多數據中心的支持
對照表在構建時,可以配置將數據的備份分散到不同機架或數據中心的節點上。Tair當前通過設置一個IP掩碼來判斷機器所屬的機架和數據中心信息。
比如你配置備份數為3,集群的節點分布在兩個不同的數據中心A和B,則Tair會確保每個機房至少有一份數據。假設A數據中心包含兩份數據時,Tair會盡可能將這兩份數據分布在不同機架的節點上。這可以減少整個數據中心或某個機架發生故障是數據丟失的風險。
輕量級的configserver
從Tair的整體架構圖上看,configserver很類似傳統分布式集群中的中心節點。整個集群服務都依賴于configserver的正常工作。
但Tair的configserver卻是一個輕量級的中心節點,在大部分時候,configserver不可用對集群的服務是不造成影響的。
Tair用戶和configserver的交互主要是為了獲取數據分布的對照表,當client獲取到對照表后,會cache這張表,然后通過查這張表決定數據存儲的節點,所以請求不需要和configserver交互,這使得Tair對外的服務不依賴configserver,所以它不是傳統意義上的中心節點。
configserver維護的對照表有一個版本號,每次新生成表,該版本號都會增加。當有數據節點狀態發生變化(比如新增節點或者有節點不可用了)時,configserver會根據當前可用的節點重新生成對照表,并通過數據節點的心跳,將新表同步給數據節點。
當客戶端請求數據節點時,數據節點每次都會將自己的對照表的版本號放入response中返回給客戶端,客戶端接收到response后,會將數據節點返回的版本號和自己的版本號比較,如果不相同,則主動和configserver通信,請求新的對照表。
所以客戶端也不需要和configserver保持心跳,以便及時地更新對照表。這使得在正常的情況下,客戶端不需要和configserver通信,即使configserver不可用了,也不會對整個集群的服務造成大的影響。
僅有當configserver不可用,此時有客戶端需要初始化,那么客戶端將取不到對照表信息,這將使得客戶端無法正常工作。
DataServer內部結構
DataServer負責數據的物理存儲,并根據configserver構建的對照表完成數據的復制和遷移工作。DataServer具備抽象的存儲引擎層,可以很方便地添加新存儲引擎。DataServer還有一個插件容器,可以動態地加載/卸載插件。
圖 2 DataServer的內部結構示意圖
抽象的存儲引擎層
Tair的存儲引擎有一個抽象層,只要滿足存儲引擎需要的接口,便可以很方便地替換Tair底層的存儲引擎。比如你可以很方便地將bdb、tc甚至MySQL作為Tair的存儲引擎,而同時使用Tair的分布方式、同步等特性。
Tair默認包含兩個存儲引擎:mdb和fdb。
mdb是一個高效的緩存存儲引擎,它有著和memcached類似的內存管理方式。mdb支持使用share memory,這使得我們在重啟Tair數據節點的進程時不會導致數據的丟失,從而使升級對應用來說更平滑,不會導致命中率的較大波動。
fdb是一個簡單高效的持久化存儲引擎,使用樹的方式根據數據key的hash值索引數據,加快查找速度。索引文件和數據文件分離,盡量保持索引文件在內存中,以便減小IO開銷。使用空閑空間池管理被刪除的空間。
自動的復制和遷移
為了增強數據的安全性,Tair支持配置數據的備份數。比如你可以配置備份數為3,則每個數據都會寫在不同的3臺機器上。得益于抽象的存儲引擎層,無論是作為cache的mdb,還是持久化的fdb,都支持可配的備份數。
當數據寫入一個節點(通常我們稱其為主節點)后,主節點會根據對照表自動將數據寫入到其他備份節點,整個過程對用戶是透明的。
當有新節點加入或者有節點不可用時,configserver會根據當前可用的節點,重新build一張對照表。數據節點同步到新的對照表時,會自動將在新表中不由自己負責的數據遷移到新的目標節點。遷移完成后,客戶端可以從configserver同步到新的對照表,完成擴容或者容災過程。整個過程對用戶是透明的,服務不中斷。
插件容器
Tair還內置了一個插件容器,可以支持熱插拔插件。
插件由configserver配置,configserver會將插件配置同步給各個數據節點,數據節點會負責加載/卸載相應的插件。
插件分為request和response兩類,可以分別在request和response時執行相應的操作,比如在put前檢查用戶的quota信息等。
插件容器也讓Tair在功能方便具有更好的靈活性。
Tair的未來
我們將Tair開源,希望有更多的用戶能從我們開發的產品中受益,更希望依托社區的力量,使Tair有更廣闊的發展空間。
Tair開源后,有很多用戶關心我們是否會持續維護這個項目。我們將Tair開源后,淘寶內部已經不再有私有的Tair分支,所有的開發和應用都基于開源分支。Tair在淘寶有非常廣的應用,我們內部有一個團隊,專門負責Tair的開發和維護,相信我們會和社區一起,將Tair越做越好。
有很多用戶在淘寶開源平臺上申請加入Tair項目,加入項目在我們的開源平臺上意味著成為項目的提交者,可以向代碼庫直接提交代碼。所以我們暫時還沒有批準外部用戶加入,我們將在大家對Tair有更深入的了解后和社區一起決定是否批準加入項目的申請,在此之前,如果你有對代碼的改進,歡迎使用patch的方式提交給我們,我們將在review后決定是否合并到代碼庫。
希望我們能和社區一起,將Tair做成一個真正對大家都有幫助的項目。
關于作者
若海,真名余剛,淘寶網核心系統研發部工程師,Tair的主要作者之一,當前的方向是大型分布式緩存和存儲解決方案。