Netflix如何設計一個能滿足5倍增長量的時序數據存儲新架構?

2016年1月,Netflix在全球範圍內擴展業務。越來越多的會員、越來越多的語言和越來越多的視頻回放將時間序列數據存儲架構擴展到了它的臨界點(詳見第1部分文章《Netflix實戰指南:規模化時序數據存儲》)。在第2部分中,我們將探索該架構的侷限性,並介紹我們如何爲下一個演進階段重新構建架構。

2016年1月,Netflix在全球範圍內擴展業務,向另外130個國家開放服務,總共支持20種語言。2016年晚些時候,在瀏覽體驗中加入了視頻預覽。越來越多的會員、越來越多的語言和越來越多的視頻回放讓Netflix的時間序列數據存儲架構達到了臨界點。在這篇文章中,我們將探索該架構的侷限性,並介紹我們如何爲下一個演進階段重新構建架構。

臨界點

之前的架構將所有的觀看數據同等對待,而不考慮類型(完整觀看或視頻預覽)或時間(多久之前觀看過)。隨着該功能擴展到更多設備,預覽與完整觀看的比例正迅速增長。到2016年底,數據存儲在一個季度內增長了30%;由於對數據存儲的潛在影響,視頻預覽功能被推遲。最簡單的解決方案是擴展底層的Cassandra集羣以適應這種增長,但是,它已經是正在使用中的一個最大的集羣,並且已接近集羣規模限制。我們必須做點什麼,而且要快。

重新思考我們的設計

我們開始重新思考我們的方法,並設計出一個至少能滿足5倍增長量的方法。我們可以重用之前架構中的模式,但這還不夠,我們需要新的模式和技術。

分析

我們首先分析數據集的訪問模式。我們分析出三種不同類型的數據:

  • 完整視頻播放
  • 視頻預覽播放
  • 語言首選項(即播放哪種字幕/配音)

對於每一類數據,我們發現了另外一種模式——大多數數據訪問都是針對最近的數據。數據越舊,其詳細級別就應該降得越低。把這些見解與我們同數據消費者的交流相結合,我們就可以判定哪些數據需要在什麼樣的詳細級別上存儲多長時間。

存儲效率問題

對於增長最快的數據集——視頻預覽和語言信息,我們的合作伙伴只需要最近的數據。非常短的視頻預覽被我們的合作伙伴過濾掉,因爲它們不能反映出會員對內容的觀看意向。此外,我們發現,對於他們觀看的完整視頻,大多數會員選擇了相同的字幕/配音語言。爲每個觀看記錄存儲相同的語言首選項會導致大量的數據重複。

客戶端複雜性

我們研究的另一個限制因素是觀看數據服務的客戶端庫如何滿足調用者在特定時間段內對特定數據的特殊需求。調用者可以通過指定以下條件來檢索觀看數據:

  • 視頻類型 — 完整視頻或視頻預覽;
  • 時間範圍— 過去X天/X月/X年,其中X會隨不同使用場景而變化;
  • 詳細級別 —完整或摘要;
  • 是否包含字幕/配音信息。

對於大多數場景,這些過濾器會在從後端服務獲取到完整數據後將其應用於客戶端。你可能想到了,這會導致大量不必要的數據傳輸。此外,對於較大的觀看數據集,性能會迅速下降,導致99百分位讀取延遲出現巨大變化。

重新設計

我們的目標是設計一個能夠擴展到5倍增長量的解決方案,並且具有合理的成本效率以及更可預測的延遲。通過對上述問題的分析和了解,我們進行了此次重大的重新設計。以下是我們的設計準則:

數據類別

  • 按數據類型分片;

  • 減少數據字段,僅保留基本元素。

數據年齡

  • 按數據年齡分片;

  • 對於最近數據,在設定好的TTL之後
    過期;

  • 對於歷史數據,彙總並轉入歸檔集羣。

性能

  • 並行化讀取爲近期數據和歷史數據提供了統一的抽象。

集羣分片

之前,我們將所有數據組合到一個集羣中,並使用一個客戶端庫根據類型/年齡/詳細級別過濾數據。現在,我們顛倒了這種方法,我們按照類型/年齡/詳細級別對集羣進行分片,每個數據集的增長率就可以互相分離,簡化了客戶端,並改善了讀取延遲。

存儲效率

對於增長最快的數據集——視頻預覽和語言信息,我們能夠與合作伙伴保持一致,只保留最近的數據。我們不存儲非常短的預覽視頻播放數據,因爲它們不能很好地說明會員對內容感興趣程度。此外,我們現在也存儲了初始語言首選項,然後只存儲後續播放的增量。這意味着對於大多數會員,我們只存儲一個語言首選項記錄,從而節省了大量存儲空間。我們降低了預覽視頻和語言首選項數據的TTL,因而,與完整視頻數據相比,它們的過期策略會更激進。

在必要時,我們將應用“動態(live)”和壓縮技術,其中可配置數量的近期觀看記錄以未壓縮的形式存儲,其餘的記錄以壓縮的形式存儲在單獨的表中。對於存儲較舊數據的集羣,我們完全以壓縮形式存儲數據,在訪問時在較低的存儲成本和較高的計算成本之間做出權衡。

最後,我們使用較少的幾列把摘要視圖存儲在一個單獨的表中,而不是存儲完整視頻播放的所有細節。這個摘要視圖也被壓縮,以進一步優化存儲成本。

總體上,我們的新架構看起來像下面這樣:

image

如上所示,觀看數據是按類型進行分片的——有單獨的集羣用於完整播放數據、預覽播放數據和語言首選項信息。對於完整播放數據,存儲是按年齡進行分片的。對於最近的觀看數據(過去幾天)、過去的觀看數據(幾天到幾年)和歷史查看數據,都有單獨的集羣。最後,對於歷史觀看數據,只有摘要視圖,而沒有詳細記錄。

image

數據流

寫入

數據寫入到最近的集羣中。在輸入之前會應用過濾器,例如不存儲非常短的視頻預覽播放,或將所播放的字幕/配音與以前的首選項進行比較,只在與以前的行爲相比存在變化時才存儲。

讀取

對最新數據的請求直接發送到最近的集羣。當請求更多的數據時,並行讀取可以更有效地獲取數據。

最近幾天的觀看數據:對於大多數需要幾天完整播放記錄的場景,只從“最近(Recent)”集羣中讀取數據。在集羣中並行讀取LIVE表和COMPRESSED表。繼續利用動態和壓縮數據集模式,當從LIVE讀取的記錄數量超出配置的閾值時,記錄會被彙總,並在壓縮後寫入COMPRESSED表,作爲一個具有相同row key的新版本。

此外,如果需要語言首選項信息,就並行讀取“語言首選項”集羣。類似地,如果需要預覽播放信息,那麼就並行讀取“預覽播放”集羣中的LIVE表和COMPRESSED表。與完整觀看數據類似,如果LIVE表中的記錄數超過了配置的閾值,那麼這些記錄將作爲具有相同row key的新版本彙總、壓縮並寫入COMPRESSED表中。

過去幾個月的完整播放數據需要並行讀取“最近的”和“過去的”集羣。

彙總觀看數據通過並行讀取“最近”、“過去”和“歷史”集羣獲得。然後,將數據整合在一起,得到完整的彙總視圖。爲了減少存儲大小和成本,“歷史”集羣中的彙總視圖不包含最近幾年會員觀看的更新數據,因此,需要通過彙總來自“最近”和“過去”集羣的觀看數據來進行增強。

數據流轉

對於完整播放記錄,不同年齡數據的集羣之間的數據移動是異步進行的。在從“最近”集羣中讀取會員的觀看數據時,如果確定有比配置的天數更長的記錄,則會排隊等待將該會員的相關記錄從“最近”集羣移動到“過去”集羣。在執行任務時,相關記錄與“過去”集羣中COMPRESSED表中的現有記錄相組合。然後,將組合的記錄集壓縮並作爲新版本存儲在COMPRESSED表中。一旦新版本寫入成功,以前的版本記錄將被刪除。

如果壓縮後的新版本記錄集的大小大於配置的閾值,則將記錄集分塊,並並行寫入這些塊。這些從一個集羣到另一個集羣的記錄傳輸是成批進行的,這樣,它們就不會在每次讀取時都觸發。

image

在從“過去”集羣讀取數據時,也完成了記錄到“歷史”集羣的移動。相關記錄會被重新處理,使用現有彙總記錄創建新的彙總記錄。然後,將它們壓縮並作爲新版本寫入“歷史”集羣中的COMPRESSED表。新版本寫入成功後,刪除先前的版本記錄。

性能優化

與之前的架構類似,LIVE記錄和COMPRESSED記錄存儲在不同的表中,並進行不同的調優以獲得更好的性能。由於LIVE表會頻繁更新,而且僅包含少量的觀看記錄,所以需要經常進行壓縮,並設置較小的gc_grace_seconds,以便減少SSTable的數量。爲了提高數據一致性,會頻繁進行讀修復和“全列族修復(full column family repair)”。由於COMPRESSED表的更新頻率較低,所以通過低頻的手動全量壓縮足以減少SSTable的數量。在極少的更新執行期間檢查數據的一致性,從而避免進行讀修復和全列族修復。

緩存層的變化

由於我們對Cassandra的大數據塊進行大量的並行讀取,使用緩存層會帶來很大的好處。爲了模擬後端存儲架構,EVCache緩存層架構也做了修改,如下圖所示。所有的緩存都有接近99%的命中率,並且在減少對Cassandra層的讀請求數量方面非常有效。

image

緩存和存儲架構之間的一個區別是,“彙總”緩存集羣存儲完整播放觀看數據的壓縮彙總。在大約99%的緩存命中率下,只有一小部分請求到達了Cassandra層,這一層會並行讀取3個表,把記錄拼接在一起,然後創建整個觀看記錄的彙總。

遷移:初步結果

這些變更已經完成了一半以上。能夠從按照數據類型進行分片的集羣中受益的場景已經遷移完畢。因此,雖然我們還不能分享完整的成果,但以下是我們獲得的初步結果和經驗教訓:

  • 僅基於按數據類型分片的集羣就獲得了Cassandra操作特性(壓縮、GC壓力和延遲)的巨大改進。
  • 巨大的完整觀看數據Cassandra集羣動態餘量可以應對至少5倍的規模增長。
  • 得益於更激進的數據壓縮和數據TTL,節省了大量成本。
  • 架構重構是向後兼容的。現有的API將繼續有效,預計會有更好、更可預測的延遲。爲訪問數據子集而新創建的API將帶來額外的延遲優勢,但需要修改客戶端。這使得獨立於客戶端推出服務器端變更變得更加容易,並且可以根據客戶的使用帶寬在不同的時間使用不同的客戶端。

小結

在過去幾年裏,觀看數據存儲架構已經取得了長足的進步。我們逐步演變成使用一種動態和壓縮數據的模式,可以並行讀取觀看數據,並將這種模式重新用於團隊內其他的時間序列數據存儲上。最近,我們對存儲集羣進行了分片,以滿足不同場景的獨特需求,並對一些集羣使用了動態和壓縮數據模式。我們擴展了動態和壓縮數據的流轉模式,以便在按年齡分片的集羣之間移動數據。

設計這些可擴展的構建塊可以簡單而高效地擴展我們的存儲層。當我們以5倍增長規模爲目標進行重新設計時,我們知道,Netflix的產品體驗在持續發生變化和改進。我們正密切關注可能需要進一步演進的地方。

查看英文原文:https://medium.com/netflix-techblog/scaling-time-series-data-storage-part-ii-d67939655586?br=ro&

image

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章