2014年6月16日 星期一

OpenStack Swift 的儲存結構最佳化

當需要使用大型雲端基礎建設時,在國外有蠻多企業選擇 OpenStack 這個開源雲端專案。
OpenStack 裡面又有 Swift(不是 Apple Swift 程式語言 XD)這個 Object Storage 服務,可以提供關於物件儲存的解決方案。
Swift 主要提供的是高可用性且易於擴展的儲存服務~
它的儲存結構中區分為 account、container 以及 object 三個階層
因此想要建構在 OpenStack 之上的應用服務時,也需要稍微思考一下要如何去建構 Swift 的結構。

Swift 在概念上,account 就是一個系統的使用者、container 有點類似電腦上的資料夾、object 則表示一個檔案。
實際存放時,account 和 container 都會被 Swift 使用 SQLite 資料庫存起來(維持在 OpenStack 中稱為 Ring 的架構)
理論上當開發應用程式時,應用程式是可以把所有檔案都放進同一個 account 下的同一個 container
也可以把一個應用程式當做一個 account,依據應用程式內的資料類型區分,不同資料類型放在不同的 container
或者甚至再往上一層,依據資料類型放在不同 account 之內。
不過實務上,目前查了一些資料顯示,Swift 存在一些設計上的議題,以下稍微整理一些目前看到的資料。

註:需要注意的是,OpenStack 從官方的釋出時間來看,變化地相當快速。本文中引用的連結大多都不是最近的文章,也許這些問題已經解決了。只是目前我還沒找到更新的資料去驗證文章中提到的問題是不是已經在新版獲得解決 Orz

1. Object 和 Container 之間的關係
這是我在搜尋時想到,相當直覺的第一個問題:可以把所有的 objects 都放在同一個 container 裡嗎?關於這個問題,[1] 中有一個基本的效能測試,節錄圖如下:


從圖中可以看出當同時持續上傳檔案給 Swift 時,把檔案全部放在同一個 container 的效率是比較差的
而原因在 [1] 中也有描述到,主要的瓶頸在於 Swift 使用 SQLite 資料庫來存放 container ring 的資料,其中每個 container 會建立一個 SQLite 資料庫
因此當所有檔案都放在同一個 container,並且用大量的 concurrent access 去存取時,會使得 container 所屬的 SQLite 成為效能瓶頸。

此外,[2-3] 的討論中探討到在一個 container 中可以放多少 objects,[3] 有以下這段話:

You can store as many objects in a container as you wish. However, your per-object upload latency will increase considerably one you reach a certain point. I found the optimal number of objects per container to be just under one million. This number will vary depending on your equipment, and how heavy of a workload it’s subjected to.

也就是實務上沒辦法明確說明到底數量超過多少會造成效率下降,因為那是根據實際佈署的硬體環境來決定的
不過一般狀況大概可以看成 100 萬的 object 是一個警訊,超過這個數字有可能就要面對效率下降的問題了。

2. 資料複製
根據 [4] 的描述,在 Swift 的設計中,因為要確保高可用性,因此 Swift 定義了 zone 這個單位。
每個 zone 是各自獨立的,而 zone 之內會存放 account、container 或者 object 的資料。
不過基於高可用性,因此會有好幾個 zone 同時存放同樣的一部分資料,而這也表示了資料在存放時必須寫到好幾個 zone 裡
但不一定所有的 zone 在資料寫入的當下都是正常有辦法寫入的狀態,如下圖所示:


圖中當 zone 3 正好處在無法存取的狀態時,object 寫入就不會寫入 zone 3,因此導致 zone 3 的資料不一致。
在 Swift 上這就是 swift-proxy daemon 的工作,負責檢查每個 zone 的資料都是一致的,並把不一致的 zone 更新成一致。

實作上,Swift 是根據 account 和 container 去一個一個掃對應的 zone
因此當 account 或 container 越來越多時,Swift 會花更多的 CPU 時間去做這些一致性的比對
根據 [4] 的 Replication process bottlenecks 小節中有提到,Swift 預設每 30 秒會檢查一次 account 和 container 的一致性
但大約有 12,000 個 container 以上時,這個一致性的比對就可能耗時超過 30 秒,因此導致系統會不停地忙碌運轉。

關於這個問題,[4] 的建議如下:
decreasing the number of partitions
decreasing the number of replicas
increasing the number of node

另外 [4] 還提到一個問題是在 Swift 上呼叫 API 刪除 container 時,container 所屬的 SQLite 並不會真的被刪除,只會被標註為要刪除而已
因此在大量地刪除 container 時,這些不會再被使用的 container 依然會消耗 swift-proxy daemon 的時間去做一致性的比對。
真正要刪除 container 的方法是必須把 container 所屬的 account 整個刪除掉,這時 swift-account-reaper 才會真正去把 account 和它底下的 container 一併刪除。
這個部分我還沒查到更確切的資訊,所以不確定是否在新版本中有被解決。(同時我也還不清楚為什麼要這樣設計....)



以上是目前收集到已知的 Swift 問題,如果還有找到新的資料會再補充進來。

參考資料:
1、Multi-Container Sharding: Making OpenStack Swift Swifter
2、What is maximum recommended objects per container?
3、OpenStack Object Storage is Great For…
4、OpenStack Swift eventual consistency analysis & bottlenecks

沒有留言: