發一個分布式存儲的架構設計,看有沒有感興趣的同學一起討論下,也非常歡迎高手指教。
最底層類似Ceph(個人認為Ceph實在是太復雜),為一個統一的分布式存儲引擎。上層提供對象存儲服務,文件系統服務,塊存儲服務。
(本帖只講底層的分布式存儲引擎。)
分布式存儲的典型問題:
1) 海量文件(數量可達萬億級)的管理,
2) 數據高可靠,
3) 服務高可用,
4) 存儲機器擴容縮容,
5) 故障自動處理,
6) 高性能。
首先:
整體上采用集中式模式,由管理節點(在內存中)統一管理元數據,管理節點為新數據分配存儲節點,管理節點還負責存儲節點健康監控,故障恢復調度等。數據存放在存儲節點上。
這種方式比Ceph的方式優點更多:數據分布任意,擴容縮容極為方便(擴容時可以不遷移數據),磁盤利用率高。至於性能,讀數據可在客戶端緩存數據位置,定位數據的總體性能不會比Ceph差多少。
Ceph的crush算法會導致一個非常嚴重的問題,在機器故障或者擴容縮容時,會導致大量數據重新平衡,而數據平衡期間服務不可用(不能讀寫),服務不可用時間可能會長達幾十個小時。所有基於算法來確定數據位置的方式都會有類似的問題,例如S3的基於一致性哈希來定位數據也會遇到數據再平衡的問題。
Ceph的元數據持久化方式(集中的存放到RocksDB中)也會導致一個嚴重的問題,即單個集群文件(此處文件指的是各個對象數據)數量會非常有限,基本只能到億級。遠不能滿足互聯網的應用場景。一般只能創建多個集群來滿足應用的需要,但這極大的增加了運維工作量,以及浪費機器資源。所有集中式持久化元數據的方案都有類似的問題,就算元數據存放到數據庫,同樣會占用一個規模不小的數據庫集群(試想,這里是可能有千億萬億個文件的元數據要集中持久化)。再補充一點,各個文件的元數據沒必要集中持久化,這沒有意義。文件(此處的文件指的是posix接口的文件)存儲中的路徑管理,可以考慮集中持久化到RocksDB或者MySQL等,一般來說互聯網場景下,文件存儲的應用規模不大(網盤除外),絕大多數都是對象存儲。
總的來說,分布式存儲中中心式管理遠好於無中心式管理。
典型問題的處理思路:
“1)海量文件(數量萬億級)的管理”的解決方式:
小文件(用戶的文件或對象,或塊)合並為大文件管理和存儲。各個小文件在大文件內部依次緊密存放(這樣小文件的修改就不方便,后面有講),且每個小文件有一個固定長度的元數據區,元數據區包含該小文件的id,size,delete_flag,crc,user_id等。例如:大文件大小為1GB(其實集群中大文件大小也可有多種規格,以適應對象存儲,文件存儲和塊存儲),小文件大小不定(通常是幾十KB)。這樣整個集群大文件數據最多億級,小文件數據可以過萬億。
大文件以物理文件方式直接存放到磁盤上。每個大文件3副本(或其他副本數,或EC)。
小文件名(很重要)由大文件名(一般是UUID,20多位長)及小文件在大文件內部的id組成,具體格式為:大文件名小文件ID。
大文件的元數據(主要是大文件所在的存儲節點ip,version等)不專門持久化(這是一個創新點,持久化會帶來一些列問題,增加了系統的復雜性),當然元數據自然的持久化在其各個副本的數據中。存儲節點啟動時向管理節點匯報各自所擁有的大文件名稱及version等,這樣管理節點就能匯總到所有的大文件及其副本的位置(包括副本缺失也能知道)。大文件元數據直接存放在管理節點內存中(管理節點內存中不存放小文件元數據,內存不夠)。
小文件從存儲節點的磁盤加載后元數據存放在存儲節點內存中(單個存儲節點上的小文件數據有限,基本可以全部加載到內存中,或采用LRU算法來淘汰)。
這樣,讀寫小文件時(肯定知道小文件名,也就知道了對應的大文件名),客戶端首先位到管理節點找到小文件對應的大文件所在的存儲節點,然后訪問到主副本的存儲節點(主副本規則,后面講),打開大文件,再根據ID找到小文件的位置,進行讀寫。
“2)數據高可靠”的解決方式:
用多副本(通常是3副本)或EC模式來保證。
一致性協議:用raft(其實raft不適合高並發場景,心跳開銷太大太耗性能,跨機房部署時光纖估計被其耗光,然后寫數據時寫放大一倍),或者,自研一套一致性協議(借助管理節點和客戶端)。
“3)服務高可用”的解決方式:
管理節點采用主備(其實也可以不主備,同時多寫)方式,用raft無日志狀態下的選舉(這個要簡單很多)來確定主節點。
存儲節點每個文件在不同機器上總計有3副本。同時有故障自動處理機制來保證及時發現和補全丟失的副本。
擴容縮容,機器故障(丟失1個副本)時都不影響數據讀寫。
“4)存儲機器擴容縮容”的解決方式:
擴容時直接加存儲節點即可,可以不用平衡數據(可選擇在凌晨時平衡數據)。
縮容時,只涉及本台機器數據的補全(補全的同時依據平衡了)。
其實,我這個方案中,所有的存儲節點,除了要考慮故障域,所有存儲節點之間完全沒有關聯,完全對等。而有些方案中把存儲節點分組或分區,這對擴容縮容以及故障處理很不利,也不能實現熱點數據的隨意遷移。這比Ceph好太多,Ceph數據量大的時候,平衡數據可能需要幾十小時,且期間不能讀寫。
“5)故障自動處理”的解決方式:
管理節點會負責監控存儲節點的健康狀態,在發現存儲節點故障后,按照一定的策略來補全大文件的副本。
管理節點自身通過主備切換(或者分組多寫及分組調整)模式來實現故障自動處理。
我總結下創新點:
1)管理節點不持久化文件的元數據(自然的持久化在存儲節點的各個副本數據中)。這能很大程度的減低系統的復雜度,提高性能。
2)元數據分散持久化(自然的持久化在存儲節點的各個副本數據中,和文件的內容數據放到一起),這能保證集群基本無容量上限(萬億級文件,數千台存儲節點)。
3)管理節點多寫。這也是集群無容量上限的一個保證。
4)一致性協議不采用raft(raft真不適合高並發場景)。
其他問題:
1)修改文件的問題。由於各個小文件在大文件內部一次緊密存放,因此修改文件成為一個難題(如果增加了文件大小,則沒有空余空間可用)。可以采用鏈接的方式來解決,即修改時重新創建一個全新的文件(可能被存放到其他大文件中),然后在當前小文件的元數據上存放一個新小文件名稱作為鏈接,后續讀寫該小文件時,通過該鏈接轉到新的地方去。這樣性能上會差一些,但是客觀的講,互聯網場景下一般修改操作很少(至於塊存儲,因為對應的小文件大小固定,就可以直接修改)。
2)自定義一致性協議,性能和簡單性都比raft好很多。專門寫了一篇:https://www.cnblogs.com/feiyufrank/articles/12843965.html
有興趣的同學可以討論下,這個大方案有沒有什么問題。歡迎指出。