5大架構:細數數據平臺的組成與擴展

【譯者介紹】

蔡延亮,北京大學計算機碩士畢業,明略數據技術合夥人。專注於大數據解決方案的研發和實施,擁有豐富的大數據分析平臺建設實施經驗。熟悉商務智能(BI)系統的設計、架構和演進規劃,擅長其在電信運營商的應用;在數據ETL處理、模型設計、數據備份、生命週期管理、安全管理等領域有豐富的實踐經驗;熟悉數據挖掘、機器學習等分析算法和工程應用;熟悉軟件項目管理。


導讀:One size does not fit all! 數據處理平臺已不集中於傳統關係型數據庫,各種其他平臺層出不窮,也各有其適用範圍。


從哪些角度去理解各種數據處理平臺的設計思想及發展演進呢?下面我們從幾個角度討論一下:


一、單機存儲引擎設計(數據的位置)


從某種意義上說,當我們處理數據的時候,實際上是在管理數據的位置,管理數據在CPU的位置,數據相對其他數據的位置。CPU特別適合處理順序性操作數據指令,這樣他可以進行數據預取。但是隨機讀取操作使得預取功能幾乎失效,好多預取到緩存、前端總線的數據都是無效的。


傳統意義上說,磁盤的存取性能要弱於內存,但是要分隨機存取及順序存取不同的場景下討論。在流式順序處理場景,磁盤及SSD的讀取速度已經超過內存隨機讀取速度。


我們如何儘量實現數據的順序存取呢?讓我們設計一個很簡單的數據庫開始,存取一個文件。


1、數據存儲和更新


追加寫可以讓我們儘量保持順序存儲文件。但是當數據要進行更新的時候,有兩種選擇,一種是在數據原地進行更新操作,這樣我們就有了隨機IO操作。另一種是把更新都放到文件末尾,然後需要讀取更新數據的時候進行替換。


2、數據讀取


一下子讀取整個文件,也是很耗費時間的事情,例如數據庫中的全表掃描。當我們讀取文件中某一個字段時候,我們需要索引。索引的方式有多種,我們可以用一種簡單的固定數值大小的有序數組來做索引,數組裏存的是當前數據在文件中的存儲偏移量。還有其他索引技術,如hash索引,位圖索引等。


索引相當於在數據之上又加了一層樹狀結構,可以迅速的讀取數據。但是打破了我們前面講的數據的追加寫,這些數據都是根據索引隨機寫入的。在數據庫上建立索引的時候都會遇到這個問題,在傳統的機械式磁盤上,這個問題會造成1000倍的性能差異。


有三種方法可以解決上述問題:


1)把索引放到內存中,可以隨機存儲和讀取,把數據順序存儲到硬盤上。MongoDB,Cassandra都是採取這種方式。這種方式有一個弊端是存儲的數據量受限於內存的大小,數據量一大,索引也增大,數據就飽和了。


2)第二種方式是把大的索引結構,拆成很多小的索引來存儲。在內存中批量進來的數據,當積累到一個預定的量,就排序然後順序寫到磁盤上,本身就是一個小的索引,數據存儲完,最後加一塊小的全局索引數據即可。這樣讀取數據的時候,要遍歷一些小的索引,會有隨機讀取。本質是用部分小的隨機讀換取了整體的數據順序存儲。我們通過在內存中保存一個元索引或者Bloom filter來實現處理那些小索引的低延遲。


日誌結構的歸併樹(log structed merge tree, 簡稱LSM tree)是一種典型的實現,有三個特徵:

a)一組小的、不變的索引集。

b)只能追加寫 ,合併重複的文件。

c)少量的內存索引消耗換來讀取的性能提升。這是一種寫優化索引結構。


HBase、Cassandra、Bigtable都是通過這種比較小的內存開銷來實現讀取和存儲的平衡


3)列式存儲或者面向列的存儲(暴力方式)。

純列式存儲和谷歌bigtable那種列式存儲還是有所不同的,大家最好分開來看,雖然佔用了同一個名字。列式存儲很好理解,就是把數據按照列順序存儲到文件中,讀取的時候只讀需要的列。列式存儲需要保持每一列數據都有相同的順序,即行N在每一列都有相同的偏移。這很重要,因爲同一查詢中可能要返回多個列的數據,同時可能我們要對多列直接進行連接。每一列保持同樣的順序我們可以用非常簡單的循環實現上述操作,且都是高效的CPU和緩存操作。


列式存儲的缺點是更新數據的時候需要更新每一個列文件中的相應數據,一個常用的方法就是類似LSM那種批量內存寫的方式。


當查詢只是返回某幾列數據,列式存儲可以大規模減少磁盤IO。除此之外,列式存儲的數據往往屬於同一類型,可以進行高效的壓縮,一些低延遲,高壓縮率的掃描寬度、位填充算法都試用。即使對於未壓縮的數據流,同時可以進行鍼對其編碼格式的預取。


列式存儲尤其適用於大表掃描,求均值、最大最小值、分組等聚合查詢場景。


列式存儲天然的保持了一列中數據的順序性,方便兩列數據進行關聯,而heap-file index結構關聯時候,一份數據可以按順序讀取,則另一份數據就會有隨機讀取了。


典型優勢總結:

  • 列式壓縮,低IO

  • 列中每行數據保持順序,可以按照行id進行關聯合並

  • 壓縮後的數據依然可以進行預取

  • 數據延遲序列化


上面討論的數據順序存取的幾種方案,在很多數據處理平臺的最優技術方案中大都有參考。


通過heap-file結構把索引存儲在內存,是很多NoSQL數據庫及一些關係型數據庫的首選,例如Riak,CouchBase和MongoDB,模型簡單並且運行良好。


要處理更大量的數據,LSM技術應用更爲廣泛,提供了同時滿足高效存儲和讀取效率的基於磁盤的存取結構。HBase、Cassandra、RocksDB, LevelDB,甚至MongoDB最新版也支持這種技術。


列式存儲在MPP數據庫裏面應用廣泛,例如RedShift、Vertica及hadoop上的Parquet等。這種結構適合需要大表掃描的數據處理問題,數據聚合類操作(最大最小值)更是他的主戰場。


Kafka 通過追加式的文件或者預定義的offset集來存儲消息隊列。你來消費消息,或者重新消費消息都是很高效的順序IO操作。這和其他的消息中間件架構上有所不同,JMS和AMQP都需要上面提到過的額外的索引來管理選擇器和session信息。他們最終性能表現像一個數據庫而非文件。


爲滿足讀和寫不同業務場景的優化,以上這些技術多少都有某些方面的折中,或者把問題簡化,或者需要硬件支持,作爲一種拓展的方法。


二、分佈式集羣存儲設計(並行化)


把數據放到分佈式集羣中運算,有兩點最爲重要:分區(partition)和副本(replication)。


分區又被稱爲sharding,在隨機訪問和暴力掃描任務下都表現不錯。


通過hash函數把數據分佈到多個機器上,很像單機上使用的hashtable,只不過這兒每一個數據桶都被放到了不同的機器上。


這樣可以通過hash函數直接去存儲數據的機器上把數據取出來,這種模式有很強的擴展性,也是唯一可以根據客戶端請求數線性擴展的模式。請求會被獨立分發到某一機器上單獨處理。


我們通過分區可以實現批量任務的並行化,例如聚合函數或者更復雜的聚類或者其他機器學習算法,我們通過廣播的方式在所有機器上使任務同時執行。我們還可以運行分治策略來使得高計算的任務在一個更短的時間內解決。


這種批處理系統在處理大型的計算問題時有不錯的效果,但只能提供有限併發,,因爲執行任務時會非常消耗集羣的資源。


所以分區方式在兩個極端情況非常簡單:

  • 直接hash訪問

  • 廣播,然後分而治之。在這兩種情況之間還有中間地帶,那就是在NoSQL數據庫中常用的二級索引技術。


二級索引是指不是構建在主鍵上的索引,意味着數據不會因爲索引的值而進行分區。不能直接通過hash函數去路由到數據本身。我們必須把請求廣播到所有節點上,這樣會限制了併發性,每一個請求都會捲入所有的節點。


因此好多基於key-value的數據庫拒絕引入二級索引,雖然它很有價值,例如Hbase和Voldemort。 也有些數據庫系統包含它了,因爲它有用,例如Cassandra、MongoDB、Riak等。重要的是我們要理解好他的效益及他對併發性所造成的影響。


解決上述併發性瓶頸的一個途徑是數據副本,例如異步從數據庫和Cassandra、MongoDB中的數據副本。


實際上副本數據可以是透明的(只是數據恢復時候使用)、只讀的(增加讀的併發性),可讀寫的(增加分區容錯性)。這些選擇都會對系統的一致性造成影響,這是CAP理論中的一個研究課題(也許CAP理論不像你想象中的那麼簡單)。


這些對一致性的折中,給我們帶來一個值得思考的問題?一致性到底有什麼用?


實現一致性的代價非常昂貴。在數據庫中是用串行化來保證ACID的。他的基本保證是所有操作都是順序排列的。這樣實現起來的代價非常昂貴,所以好多關係型數據庫也不把他當成默認選項。


所以說要想在包含分佈式寫操作的系統上實現強一致性,如同墜入深淵。(補充說明,Consistency, 在ACID和CAP中同時出現,但是意義不一樣,我這兒說的是在CAP中的定義:所有的節點在同一時間看到的是同樣的數據)


解決一致性問題的方案也很簡單,避免他。假如不能避免它把他隔離到儘可能少的寫入和儘可能少的機器上。


避免一致性問題其實很簡單,尤其是你的數據是一串不再改變的事實描述。web日誌就是一個很好的例子,不用擔心一致性問題,因爲日誌存下來後就是不變的事實描述。


當然有些業務場景是必須要保證數據一致性的,例如銀行轉賬時候。有些業務場景感覺上是必須保持一致性的,但實際上不是,例如標記一個交易是否有潛在的欺詐,我們可以先把它更新到一個新的字段裏面,另外再用一條單獨的記錄數據去關聯最開始的那個交易。


所以對一個數據平臺來說有效的方式是去避免或者孤立需要一致性的請求,一種孤立的方法是採取單一寫入者的策略,Datamic就是典型的例子。另一種物理隔離的方法就是去區分請求中可變和不可變的字段,分別查詢。


Bloom/CALM把這種理念走的更遠,默認的配置選項就是無序執行的策略,只有在必要的時候才啓用順序執行讀寫語句。


前面是我們必須考慮的一些點,現在思考如何把這些設計組裝在一起做成一個數據處理平臺?





三、架構


1、命令查詢職責分離架構(CQRS)


最常用的架構就是用傳統關係型數據庫存取數據,上層承接各種應用。這種架構會遇到一些瓶頸,比如當數據吞吐量大到一定程度,就會遇到消息傳遞、負載均衡、擴容、併發性能降低等問題。爲保持ACID特性,擴容問題尤其嚴峻。


一種解決方案是CQRS(Command Query Responsibility Segregation),命令查詢職責分離)架構,該模式從業務上分離修改 (Command,增,刪,改,會對系統狀態進行修改)和查詢(Query,查,不會對系統狀態進行修改)的行爲。從而使得邏輯更加清晰,便於對不同部分進行針對性的優化。


還有一種簡單的方式是把讀和寫的請求進行分離,寫數據側進行寫優化處理,類似於日誌文件結構。讀數據側進行讀優化處理。比較代表性的實現如Oracle的GoldenGate和MongoDB的Replica Sets .



還有一些數據庫,採用增加一層引擎的方式來實現上述思想。Druid就是一個很典型的例子,他是一個開源的、分佈式的、實時的、列式存儲的分析引擎。列式存儲特別適合需要加載大的數據塊,且數據塊分到多個文件中的場景。Druid把一些近線實時數據放到寫優化的存儲中,然後隨着時間的推移逐步把這些數據遷移到讀優化的存儲中。當Druid接收到請求,會同時把請求轉發給讀、寫優化的存儲,然後把返回的查詢結果根據時間標記進行排序反饋給用戶。像Druid這 種類似的系統,通過一層抽象實現了CQRS的優點。




2、操作/分析橋(Operational/Analytic Bridge)架構


另一種相似的處理方式是操作/分析橋(Operational/Analytic Bridge),讀和寫優化視圖被事件流所區分,數據流的狀態是被永久保存的,所以異步視圖可以通過後來的更新被重組或增強。


這樣前端模塊可以提供同步的讀和寫,這樣可以簡單高效的讀取剛被寫入的數據,或者保持複雜的ACID事物管理。


後端模塊利用異步性、狀態不變性、去擴展離線處理進程,具體方式可以採用副本、異化、或者完全使用不同的存儲引擎。信息橋,連接前端與後端,允許上層應用使用訪問數據處理平臺的數據。


這種模試比較適合中級數量的部署,尤其是至少包含部分的、不可

避免的動態視圖請求。



3、批處理架構(Hadoop)


如果我們的數據是一次寫入,多次讀,不在改變的場景,上面可以部署各種複雜的分析型應用。採取批處理模式的hadoop無疑是這種平臺最廣用和出色的代表了。


Hadoop平臺提供快速的讀寫訪問,廉價的存儲,批處理流程,高吞吐信息流,和其他抽取、分析、處理數據的工具。


批處理平臺可以主拉取或者推進來多種數據源的數據,存儲進HDFS,後續可以處理成多種優化的數據格式。數據可以壓縮,清洗結構化,聚合,處理爲一種讀優化的格式例如Parquet,或者直接加載到數據服務層或者數據集市。通過這些過程,數據可以被查詢或者處理。


這種結構在大批量的、數據不再改變的場景表現良好,一般可以到100TB以上,這種結構的進化是緩慢的,數據處理速度一般也是以小時爲單位的




4、lambda架構


有時候我們並不想等待小時後纔得到結果,這是該架構的一個缺陷。一種解決方法就是加一個流處理層,就是常說的lambda架構。


lambda架構在批處理的架構上增加了一個流處理層,如同在一個擁擠城鎮新建一條高架橋。流處理層可以用主流的Storm或者Samza實現。lambda架構的本質是可以快速的返回一個近似的結果,精確的結果在後續返回。


所以流處理旁路提供一個流處理窗口期內最好的結果,可以先被上層應用所使用,後續批處理流程計算出精確結果在覆蓋掉前面的近似結果。這種架構是對精準度和反饋時間做了一個聰明的平衡,作爲後續發展,Spark平臺同時提供了批處理和流處理模塊(雖然流處理實際上市用微型批處理來實現的)。這種架構也可以滿足 100TB以上數據的處理。


這種架構的另一種代表叫kappa架構,但是本文作者沒看中那種架構,覺得叫kappa屬於吃飽了撐的。




5、流式處理架構


不像是批處理架構,把數據存儲到HDFS上,然後在上面執行各種跑批任務。流處理架構把數據存儲到可擴展的消息或者日誌隊列,例如kafka,這樣數據就可以被實時的處理成三級視圖、索引, 被數據服務層或者數據集市供上層應用使用。


這和去掉批處理層的lambda架構很相似,在消息層可以存儲處理海量的數據,有足夠強大的流處理引擎可以hold住這些數據處理進程。


流處理結構可以用來解決“應用集成”問題,這是個頭疼複雜的問題,IT傳統大佬:Oracle,Tibco,Informatica都曾經試圖想解決,一些部分結果是有用的,但不是真的解決,始終在尋找一套真正可用的解決方案。


流式處理平臺提供了一種解決該問題的可能性,他繼承了O/A橋平臺的優點:多樣化的異步存儲形式和重新計算視圖的能力,把一致性請求給隔離。系統保存的數據是日誌的話,很天然的擁有不變性。Kafka可以保存高容量和吞吐量的歷史記錄,意味着可以重新計算數據狀態,而不是持續的設置檢查點。


類似流處理架構的工具還有Goldengate,用來向大型數據倉庫同步數據,不過他在數據副本層缺乏高吞吐量支持,在數據模型管理層過於複雜。




四、小結:


我們開始於數據的位置,用來讀寫數據的順序地址,從而說明了我們用到組件對該問題的折衷。我們討論了對一些組件的拓展,通過分區和副本構建分佈式的數據處理平臺。最後我們闡述了觀點:儘量在數據處理平臺中把一致性的請求隔離。


數據處理平臺自身也是一個動態調整變化的平臺,依據業務需求,會把寫優化轉爲讀優化,把強一致性依賴轉爲開放的流式、異步、不變的狀態。


有些東西我們必須留在思想中,順序的結構化模式是一種,時序、分佈式、異步是另一種。


我們要堅信:經過認真的解決,這些問題都是可控的。


附(知識補充):


簡單介紹一下heap-file結構(和鏈表結構很相似):

  • 支持追加數據(append)

  • 支持大規模順序掃描

  • 不支持隨機訪問




下面是Heap file自有的一些特性:

  • 數據保存在二級存儲體(disk)中:Heapfile主要被設計用來高效存儲大數據量,數據量的大小隻受存儲體容量限制;

  • Heapfile可以跨越多個磁盤空間或機器:heapfile可以用大地址結構去標識多個磁盤,甚至於多個網絡;

  • 數據被組織成頁;

  • 頁可以部分爲空(並不要求每個page必須裝滿);


頁面可以被分割在某個存儲體的不同的物理區域,也可以分佈在不同的存儲體上,甚至是不同的網絡節點中。我們可以簡單假設每一個page都有一個唯一的地址標識符PageAddress,並且操作系統可以根據PageAddress爲我們定位該Page。


一般情況下,使用page在其所在文件中的偏移量就可以表示了。


轉自:大數據文摘


發佈了1 篇原創文章 · 獲贊 29 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章