【轉載】OpenStack Swift學習筆記



免責聲明:
    本文轉自網絡文章,轉載此文章僅爲我的收藏,分享知識,若有侵權,請聯繫 博主進行刪除。
    原文做者: 崔炳華 
    原文地址: http://blog.csdn.net/i_chips/article/details/17787017

1       概述

OpenStack Object Storage(Swift)OpenStack開源雲計算項目的子項目之一。Swift的目的是使用普通硬件來構建冗餘的、可擴展的分佈式對象存儲集羣,存儲容量可達PB級。

Swift並非文件系統或者實時的數據存儲系統,它是對象存儲,用於永久類型的靜態數據的長期存儲,這些數據能夠檢索、調整,必要時進行更新。最適合存儲的數據類型的例子是虛擬機鏡像、圖片存儲、郵件存儲和存檔備份。html

Swift無需採用RAID(磁盤冗餘陣列),也沒有中心單元或主控結點。Swift經過在軟件層面引入一致性哈希技術和數據冗餘性,犧牲必定程度的數據一致性來達到高可用性(High Availability,簡稱HA)和可伸縮性,支持多租戶模式、容器和對象讀寫操做,適合解決互聯網的應用場景下非結構化數據存儲問題。node

2       技術特性

2.1        Swift的主要特徵

Swift的主要特性以下:python

  • 極高的數據持久性(Durability)。
  • 徹底對稱的系統架構:「對稱」意味着Swift中各節點能夠徹底對等,能極大地下降系統維護成本。
  • 無限的可擴展性:一是數據存儲容量無限可擴展;二是Swift性能(如QPS、吞吐量等)可線性提高。
  • 無單點故障:Swift的元數據存儲是徹底均勻隨機分佈的,而且與對象文件存儲同樣,元數據也會存儲多份。整個Swift集羣中,也沒有一個角色是單點的,而且在架構和設計上保證無單點業務是有效的。
  • 簡單、可依賴。

2.2        Swift和HDFS的技術差別

Swift和Hadoop分佈式文件系統(HDFS)都有着類似的目的:實現冗餘、快速、聯網的存儲,它們的技術差別以下:算法

  • 在Swift中,元數據呈分佈式,跨集羣複製。而在HDFS使用了中央系統來維護文件元數據(Namenode,名稱節點),這對HDFS來講無異於單一故障點,於是擴展到規模很是大的環境顯得更困難。
  • Swift在設計時考慮到了多租戶架構,而HDFS沒有多租戶架構這個概念。
  • 在Swift中,文件能夠寫入屢次;在併發操做環境下,以最近一次操做爲準。而在HDFS中,文件寫入一次,並且每次只能有一個文件寫入。
  • Swift用Python來編寫,而HDFS用Java來編寫。
  • Swift被設計成了一種比較通用的存儲解決方案,可以可靠地存儲數量很是多的大小不一的文件;而HDFS被設計成能夠存儲數量中等的大文件(HDFS針對更龐大的文件做了優化),以支持數據處理。

3       關鍵技術

3.1        一致性哈希(ConsistentHashing)

在分佈式對象存儲中,一個關鍵問題是數據該如何存放。Swift是基於一致性哈希技術,經過計算可將對象均勻分佈到虛擬空間的虛擬節點上,在增長或刪除節點時可大大減小需移動的數據量;虛擬空間大小一般採用2的n次冪,便於進行高效的移位操做;而後經過獨特的數據結構 Ring(環)再將虛擬節點映射到實際的物理存儲設備上,完成尋址過程。sql

圖1 一致性哈希環結構數據庫

衡量一致性哈希的4個指標:swift

  • 平衡性(Balance):平衡性是指Hash的結果可以儘量分佈均勻,充分利用全部緩存空間。
  • 單調性(Monotonicity):單調性是指若是已經有一些內容經過哈希分派到了相應的緩衝中,又有新的緩衝加入到系統中。哈希的結果應可以保證原有已分配的內容能夠被映射到新的緩衝中去,而不會被映射到舊的緩衝集合中的其餘緩衝區。
  • 分散性(Spread):分散性定義了分佈式環境中,不一樣終端經過Hash過程將內容映射至緩存上時,因可見緩存不一樣,Hash結果不一致,相同的內容被映射至不一樣的緩衝區。
  • 負載(Load):負載是對分散性要求的另外一個緯度。既然不一樣的終端能夠將相同的內容映射到不一樣的緩衝區中,那麼對於一個特定的緩衝區而言,也可能被不一樣的用戶映射爲不一樣的內容。

Swift使用該算法的主要目的是在改變集羣的node數量時(增長/刪除服務器),可以儘量少地改變已存在key和node的映射關係,以知足單調性。數組

考慮到哈希算法在node較少的狀況下,改變node數會帶來巨大的數據遷移。爲了解決這種狀況,一致性哈希引入了「虛擬節點」(vnode,也稱爲partition)的概念: 「虛擬節點」是實際節點在環形空間的複製品,一個實際節點對應了若干個「虛擬節點」,「虛擬節點」在哈希空間中以哈希值排列。緩存

總的來講,Swift中存在兩種映射關係,對於一個文件,經過哈希算法(MD5)找到對應的虛節點(一對一的映射關係),虛節點再經過映射關係(ring文件中二維數組)找到對應的設備(多對多的映射關係),這樣就完成了一個文件存儲在設備上的映射。服務器

圖2 對象、虛結點、節點間的映射關係

在設置虛結點數的時候,須要對系統預期的規模作充分考慮,假如集羣的規模不會超過6000個結點,那麼能夠將虛結點數設置爲結點數的100倍。這樣,變更任意一個結點的負載僅影響1%的數據項。此時有6百萬個vnode數,使用2bytes來存儲結點數(0~65535)。基本的內存佔用是6*(10^6)*2bytes=12Mb,對於服務器來講徹底能夠承受。

假設有65536(2^16)個node,有128(2^7)倍的partition數(2^23,則PARTITION_POWER=23)。因爲MD5碼是32位的,使用PARTITION_SHIFT(等於32- PARTITION_POWER)將數據項的MD5哈希值映射到partition的2^23的空間中。

3.2        數據一致性模型(ConsistencyModel)

按照Eric Brewer的CAP(Consistency,Availability,PartitionTolerance)理論,沒法同時知足3個方面,Swift放棄嚴格一致性(知足ACID事務級別),而採用最終一致性模型(Eventual Consistency),來達到高可用性和無限水平擴展能力。

爲了實現這一目標,Swift採用Quorum仲裁協議(Quorum有法定投票人數的含義):

  • 定義:N:數據的副本總數;W:寫操做被確認接受的副本數量;R:讀操做的副本數量。
  • 強一致性:R+W>N,以保證對副本的讀寫操做會產生交集,從而保證能夠讀取到最新版本;若是 W=N,R=1,則須要所有更新,適合大量讀少許寫操做場景下的強一致性;若是 R=N,W=1,則只更新一個副本,經過讀取所有副原本獲得最新版本,適合大量寫少許讀場景下的強一致性。
  • 弱一致性:R+W<=N,若是讀寫操做的副本集合不產生交集,就可能會讀到髒數據;適合對一致性要求比較低的場景。

Swift針對的是讀寫都比較頻繁的場景,因此採用了比較折中的策略,即寫操做須要知足至少一半以上成功W>N/2,再保證讀操做與寫操做的副本集合至少產生一個交集,即R+W>N。

在分佈式系統中,數據的單點是不容許存在的。線上正常存在的replica數量是1的話將很是危險的,由於一旦這個replica再次錯誤,就可能發生數據的永久性錯誤。假如咱們把N設置成爲2,那麼,只要有一個存儲節點發生損壞,就會有單點的存在。因此N必須大於2。但N越高,系統的維護和總體成本就越高。因此,工業界一般把N設置爲3。

Swift默認配置是N=3,W=2>N/2,R=1或2,即每一個對象會存在3個副本,這些副本會被儘可能存儲在不一樣區域的節點上;W=2表示至少須要更新2個副本纔算寫成功。

當R=1時,意味着某一個讀操做成功便馬上返回,此種狀況下可能會讀取到舊版本(弱一致性模型)。

當R=2時,須要經過在讀操做請求頭中增長x-newest=true參數來同時讀取2個副本的元數據信息,而後比較時間戳來肯定哪一個是最新版本(強一致性模型)。

若是數據出現了不一致,後臺服務進程會在必定時間窗口內經過檢測和複製協議來完成數據同步,從而保證達到最終一致性。

圖3 Quorum協議示例

3.3        環(Ring)

Ring是Swift中最重要的組件,用於記錄存儲對象與物理位置間的映射關係。在涉及查詢Account、Container、Object信息時就須要查詢集羣的Ring信息。

環是爲了將虛擬節點(partition,分區)均衡地映射到一組物理存儲設備上,並提供必定的冗餘度而設計的,其數據結構由如下信息組成:

存儲設備列表、設備信息包括惟一標識號(id)、區域號(zone)、權重(weight)、IP 地址(ip)、端口(port)、設備名稱(device)、元數據(meta)。

Swift爲帳戶、容器和對象分別定義了的Ring,其查找過程是相同的。Ring中每一個partition在集羣中都默認有3個replica。每一個partition的位置由ring來維護,並存儲在映射中。

Ring使用zone來保證數據的物理隔離。每一個partition的replica都確保放在了不一樣的zone中。Zone只是個抽象概念,它能夠是一個磁盤(disk drive),一個服務器(server),一個機架(cabinet),一個交換機(switch),甚至是一個數據中心(datacenter),以提供最高級別的冗餘性,建議至少部署5個zone。

權重參數是個相對值,能夠來根據磁盤的大小來調節,權重越大表示可分配的空間越多,可部署更多的分區。

當集羣中發生存儲節點宕機、新增(刪)存儲節點、新增(刪)zone等必須改變partition和node間的映射關係時,還能夠對Ring文件經過從新平衡(rebalance)來進行更新。當虛節點須要移動時,環會確保一次移動最少數量的虛節點數,而且一次只移動一個虛節點的一個副本。

總的來講,Ring引入一致性哈希的緣由是爲了減小因爲增長結點致使數據項移動的數量來提升單調性;引入partition的緣由是爲了減小因爲節點數過少致使移動過多的數據項;引入replica的緣由是防止數據單點、提升冗餘性;引入zone的緣由是爲了保證分區容忍性;引入weight的緣由是爲了保證partition分配的均衡。

4       架構設計

4.1        Swift數據模型

Swift採用層次數據模型,共設三層邏輯結構:Account/Container/Object(帳戶/容器/對象)。每層節點數均沒有限制,能夠任意擴展。這裏的帳戶和我的帳戶不是一個概念,可理解爲租戶,用來作頂層的隔離機制,能夠被多個我的帳戶所共同使用;容器相似文件夾,表明封裝一組對象;對象由元數據和數據兩部分組成。

4.2        Swift系統架構

Swift採用徹底對稱、面向資源的分佈式系統架構設計,全部組件均可擴展,避免因單點失效而擴散並影響整個系統運轉;通訊方式採用非阻塞式 I/O 模式,提升了系統吞吐和響應能力。

Swift組件包括:

  • 代理服務(ProxyServer):Swift經過Proxy Server向外提供基於HTTP的REST服務接口,會根據環的信息來查找服務地址並轉發用戶請求至相應的帳戶、容器或者對象,進行CRUD(增刪改查)等操做。因爲採用無狀態的REST請求協議,能夠進行橫向擴展來均衡負載。在訪問Swift服務以前,須要先經過認證服務獲取訪問令牌,而後在發送的請求中加入頭部信息 X-Auth-Token。代理服務器負責Swift架構的其他組件間的相互通訊。代理服務器也處理大量的失敗請求。例如,若是對於某個對象PUT請求時,某個存儲節點不可用,它將會查詢環可傳送的服務器並轉發請求。對象以流的形式到達(來自) 對象服務器,它們直接從代理服務器傳送到(來自)用戶—代理服務器並不緩衝它們。
  • 認證服務(AuthenticationServer):驗證訪問用戶的身份信息,並得到一個對象訪問令牌(Token),在必定的時間內會一直有效;驗證訪問令牌的有效性並緩存下來直至過時時間。
  • 緩存服務(CacheServer):緩存的內容包括對象服務令牌,帳戶和容器的存在信息,但不會緩存對象自己的數據;緩存服務可採用Memcached集羣,Swift會使用一致性哈希算法來分配緩存地址。
  • 帳戶服務(AccountServer):提供帳戶元數據和統計信息,並維護所含容器列表的服務,每一個帳戶的信息被存儲在一個SQLite數據庫中。
  • 容器服務(ContainerServer):提供容器元數據和統計信息(好比對象的總數,容器的使用狀況等),並維護所含對象列表的服務。容器服務並不知道對象存在哪,只知道指定容器裏存的哪些對象。 這些對象信息以SQLite數據庫文件的形式存儲,和對象同樣在集羣上作相似的備份。 
  • 對象服務(ObjectServer):提供對象元數據和內容服務,能夠用來存儲、檢索和刪除本地設備上的對象。在文件系統中,對象以二進制文件的形式存儲,它的元數據存儲在文件系統的擴展屬性(xattr)中,建議採用默認支持擴展屬性(xattr)的XFS文件系統。每一個對象使用對象名稱的哈希值和操做的時間戳組成的路徑來存儲。最後一次寫操做總能夠成功,並確保最新一次的對象版本將會被處理。刪除也被視爲文件的一個版本(一個以".ts"結尾的0字節文件,ts表示墓碑)。
  • 複製服務(Replicator):會檢測本地分區副本和遠程副本是否一致,具體是經過對比哈希文件和高級水印來完成,發現不一致時會採用推式(Push)更新遠程副本:對於對象的複製,更新只是使用rsync同步文件到對等節點。賬號和容器的複製經過HTTP或rsync來推送整個數據庫文件上丟失的記錄;另一個任務是確保被標記刪除的對象從文件系統中移除:當有一項(對象、容器、或者賬號)被刪除,則一個墓碑文件被設置做爲該項的最新版本。複製器將會檢測到該墓碑文件並確保將它從整個系統中移除。
  • 更新服務(Updater):當對象因爲高負載或者系統故障等緣由而沒法當即更新時,任務將會被序列化到在本地文件系統中進行排隊,以便服務恢復後進行異步更新;例如成功建立對象後容器服務器沒有及時更新對象列表,這個時候容器的更新操做就會進入排隊中,更新服務會在系統恢復正常後掃描隊列並進行相應的更新處理。
  • 審計服務(Auditor):在本地服務器上會反覆地爬取來檢查對象,容器和帳戶的完整性,若是發現比特級的錯誤,文件將被隔離,並複製其餘的副本以覆蓋本地損壞的副本;其餘類型的錯誤(好比在任何一個容器服務器中都找不到所需的對象列表)會被記錄到日誌中。
  • 帳戶清理服務(AccountReaper):移除被標記爲刪除的帳戶,刪除其所包含的全部容器和對象。刪除帳號的過程是至關直接的。對於每一個帳號中的容器,每一個對象先被刪除而後容器被刪除。任何失敗的刪除請求將不會阻止整個過程,可是將會致使整個過程最終失敗(例如,若是一個對象的刪除超時,容器將不能被刪除,所以帳號也不能被刪除)。整個處理過程即便遭遇失敗也繼續執行,這樣它不會由於一個麻煩的問題而停止恢復集羣空間。帳號收割器將會繼續不斷地嘗試刪除帳號直到它最終變爲空,此時數據庫在db_replicator中回收處理,最終移除這個數據庫文件。

圖4 Swift系統架構

Swift支持的全部操做能夠總結爲下表:

表1 SwiftRESTful API總結

image

4.3        Ring的數據結構

Ring 的數據結構由三個頂層域構成,其中:

  • List of Devices,表示集羣中設備的列表,在Ring類內部被稱爲devs;
  • Partition Assignment List,用於存放每一個replica與device間映射關係,在Ring類內部被稱爲_replica2part2dev_id;
  • Partition Shift Value,表示計算數據hash的移位量,在Ring類內部稱爲_part_shift。

使用python讀取/etc/swift/object.ring.gz存放的數據,能夠得到以devs、 part_shift、 replica2part2dev_id 爲key的dict類數據。

4.4        Swift存儲結構

在Storage Node上運行着Linux系統並使用了XFS文件系統,邏輯上使用一致性哈希算法將固定總數的partition映射到每一個Storage Node上,每一個data也使用一樣的哈希算法映射到partition上。

存儲內容通常放在/srv/node/sdb1之類的路徑下,其目錄結構以下所示:accounts、async_pending、containers、objects、quarantined和tmp。其中accounts、containers、objects分別是帳號、容器、對象的存儲目錄,async_pending是異步待更新目錄,quarantined是隔離目錄,tmp是臨時目錄。

  • objects:在objects目錄下存放的是各個partition目錄,其中每一個partition目錄是由若干個suffix_path名的目錄和一個hashes.pkl文件組成,suffix_path目錄下是由object的hash_path名構成的目錄,在hash_path目錄下存放了關於object的數據和元數據;object的數據存放在後綴爲.data的文件中,它的metadata存放在之後綴爲.meta的文件中,將被刪除的Object以一個0字節後綴爲.ts的文件存放。
  • accounts:在accounts目錄下存放的是各個partition,而每一個partition目錄是由若干個suffix_path目錄組成,suffix_path目錄下是由account的hsh名構成的目錄,在hsh目錄下存放了關於account的sqlite db;在account的db文件中,包含了account_stat、container、incoming_sync 、outgoing_sync 4張表;其中,表account_stat是記錄關於account的信息,如名稱、建立時間、container數統計等等;表container記錄關於container的信息;表incoming_sync記錄到來的同步數據項;表outgoing_sync表示推送出的同步數據項。
  • containers:containers目錄結構和生成過程與accounts相似,containers的db中共有5張表,其中incoming_sync和outgoing_sync的schema與accounts中的相同。其餘3張表分別爲container_stat、object、sqlite_sequence;表container_stat與表account_stat類似,其區別是container_stat存放的是關於container信息。
  • tmp:tmp目錄做爲account/container/object server向partition目錄內寫入數據前的臨時目錄。例如,client向server上傳某一文件,object server調用DiskFile類的mkstemp方法建立在路徑爲path/device/tmp的目錄。在數據上傳完成以後,再調用put()方法,將數據移動到相應路徑。
  • async_pending:async_pending存放未能及時更新而被加入更新隊列的數據。本地server在與remote server創建HTTP鏈接或者發送數據時超時致使更新失敗時,將把文件放入async_pending目錄。這種狀況常常發生在系統故障或者是高負荷的狀況下。若是更新失敗,本次更新被加入隊列,而後由Updater繼續處理這些失敗的更新工做;account與container的db和object二者的pending文件處理方式有所不一樣:db的pending文件在更新完其中的一項數據以後,刪除pending文件中的相應的數據項,而object的數據在更新完成以後,移動pending文件到目標目錄。
  • quarantined:quarantined路徑用於隔離發生損壞的數據。Auditor進程會在本地服務器上每隔一段時間就掃描一次磁盤來檢測account、container、object的完整性。一旦發現不完整的數據,該文件就會被隔離,該目錄就稱爲quarantined目錄。爲了限制Auditor消耗過多的系統資源,其默認掃描間隔是30秒,每秒最大的掃描文件數爲20,最高速率爲10Mb/s。account和container的Auditor的掃描間隔比object要長得多。

圖5 隔離對象的處理流程

5       小結

Swift犧牲必定程度的數據一致性,來達到高可用性和可伸縮性,支持多租戶模式、容器和對象讀寫操做,適合解決互聯網的應用場景下非結構化數據存儲問題。

有理由相信,由於其徹底的開放性、普遍的用戶羣和社區貢獻者,Swift可能會成爲雲存儲的開放標準,從而打破Amazon S3在市場上的壟斷地位,推進雲計算在朝着更加開放和可互操做的方向前進。

6       參考資料

1) 《Openstack Swift 原理、架構與 API 介紹》,http://www.kankanews.com/ICkengine/archives/66411.shtml

2) 《深刻雲存儲系統Swift核心組件:Ring實現原理剖析》,http://www.cnblogs.com/yuxc/archive/2012/06/22/2558312.html

3) 《深刻雲存儲系統Swift核心組件:Ring數據結構及構建、重平衡操做》,http://www.cnblogs.com/yuxc/archive/2012/06/28/2568584.html

4) 《深刻雲存儲系統Swift存儲節點:存儲實現分析》,http://www.cnblogs.com/yuxc/archive/2012/07/04/2575536.html

5) 《OpenStack對象存儲——Swift開源雲計算》,http://dev.yesky.com/244/33228744.shtml

6) 《討論:HDFS和OpenStack對象存儲的技術差別》,http://os.51cto.com/art/201202/314254.htm

相關文章
相關標籤/搜索