【翻譯】ScyllaDB數據建模的最佳實踐
文章翻譯自Scylla官方文檔://www.scylladb.com/2019/08/20/best-practices-for-data-modeling/
轉載請註明出處://www.cnblogs.com/morningli/p/16202131.html
在我們最新的夏季技術講座系列網路研討會上,ScyllaDB 現場工程師 Juliana Oliveira 指導虛擬與會者了解了一系列有關 ScyllaDB 數據建模的最佳實踐。她將演講分為三個關鍵領域:
- 數據建模在 ScyllaDB 中的工作原理
- 數據存儲如何工作以及如何壓縮數據
- 如何查找和使用(或圍繞)大型分區
Juliana 強調了掌握這些基礎知識的重要性。「因為一旦我們有了正確的數據存儲和分布概念模型,接下來的事情就會變得自然。」
SQL 與 CQL
對於那些具有 SQL 背景的人,Juliana 首先描述了該著名數據模型與ScyllaDB 使用的Cassandra 查詢語言 (CQL)之間的主要區別。

雖然這兩種查詢語言之間存在相似之處,但 Juliana 指出「SQL 數據建模不能完美地應用於 ScyllaDB。」 您沒有相同的關係模型來避免數據重複。相反,在 ScyllaDB 中,所有數據都是非規範化的。您還希望根據您希望執行的查詢來組織數據。例如,您希望將數據均勻地分布在集群中的每個節點上,以便每個節點都擁有大致相同數量的數據。還應該進行平衡以確保您沒有「熱分區」(經常訪問的數據)並且數據均勻分布在集群的節點上。因此,確定您的分區鍵至關重要。
分區和行:獸醫示例

想像一下,您在一家獸醫診所工作。我們創建一個名為 的表heartrate_v1。在這個表中,我們有一個pet_chip_id列、一個時間列和一個heart_rate列。我們將 設置PRIMARY KEY為pet_chip_id。該表負責保存我們寵物患者的數據。想像一下,我們每五秒鐘就會得到一個新的讀數。在這種情況下,由於pet_chip_id是我們的主鍵,是 CQL 中行的唯一標識符,因此它是分區的唯一標識符。
什麼是分區?
分區是節點上數據的子集——排序行的集合,由唯一的主鍵或分區鍵標識。它跨節點複製。
在這種情況下,我們在主鍵欄位中只有一列 - pet_chip_id. 但是由於我們每五秒讀取一次數據,並且主鍵必須是唯一的,這當然不是理想的解決方案,因為如果讀取是相同pet_chip_id的,它將覆蓋現有記錄。
什麼是聚類鍵?
現在我們發現由於我們的數據模型,我們不斷地覆蓋我們的數據,也許我們應該在主鍵中添加一個新的第二列。如果我們將time第二列添加到主鍵欄位,這將用作集群鍵。集群鍵的作用是對分區內的每一行進行物理排序。
正如我們稍後將看到的,分區鍵被散列並分布在集群周圍。這意味著查詢總是需要用相等來指定分區鍵——因為散列會打亂它的自然排序。但是因為集群鍵沒有散列,所以行為是不同的。
編寫查詢時,需要包含分區鍵,但可能會省略集群鍵,在這種情況下,查詢會作用於整個分區,另外,由於集群鍵是在分區內排序的,因此您還可以運行等式查詢和聚類鍵的不等式。

但是,如果您有兩個聚類鍵,例如 byheart_trate和time,您將按聚類鍵順序排序。首先它將按 排序time,然後按 排序heart_rate。我們不能編寫指定心率而不是時間的查詢,因為數據在物理上是按聚類鍵順序排列的。所以這個查詢會失敗,因為它沒有指定time; 它只指定heart_rate:

因此,知道集群鍵是如何工作的,並且知道我們heart_rate每五秒寫入一次,很容易想像分區鍵會變得非常大。由於寵物可以在數周內受到監控,我們可以在同一個分區中有數千行。(例如,每 5 秒對寵物進行一周取樣將導致單個分區中有 120,960 行。)擁有這麼大的分區可能會導致性能問題,尤其是如果您不知道正在發生這種情況。
我的分區鍵會怎樣?
它去哪兒了?分區鍵,即pet_chip_id,將由我們的散列函數進行散列 - 我們使用murmur3,與 Cassandra 相同 - 生成 64 位散列。然後我們將為負責存儲鍵的每個節點分配一個分區鍵範圍。
在此示例中,您有一個三節點集群,該集群放置的令牌範圍為 0 到 1200。如果複製因子為 2,則每個節點都擁有兩個範圍。實際上發生的是 ScyllaDB 將數據拆分為 vNode。如果沒有 vNode,並且複製因子為 2,則環將如下所示,其中每個節點將是一個分區鍵範圍的主節點,並且將是第二個分區鍵範圍的輔助節點:

使用 vNodes,這是 ScyllaDB 拆分數據的方式,其中每個物理節點將是四個範圍的主要複製和其他四個範圍的輔助複製:

pet_chip_id將駐留在這些 vNode 範圍之一(及其複製副本)中。由於散列函數,ScyllaDB 知道它在哪裡。存儲
接下來 Juliana 轉向研究 ScyllaDB 中的底層存儲系統,因為這會影響數據建模。首先,重要的是要知道數據首先寫入記憶體結構,即 memtable。而且,隨著時間的推移,一些 memtable 更改將被刷新到名為 SSTable 的不可變(不更改)文件中的持久存儲中。所以想像一下,在第一個 SSTable 1 中,我們寫了一些「A = 1」的數據。
隨著時間的推移,更多的數據被刷新到額外的 SSTables。想像一下,我們最終刪除了「A」,並且在 memtable 刷新中,這個更新被寫入了 SSTable 3。知道 SSTable 是不可變的,它會發生什麼?我們不能從現有的 SSTable 中刪除「A」。所以在這種情況下,「A」最終會在下一次壓縮中被刪除。

壓縮是讀取一組 SSTable 並將它們的數據組合以寫入僅包含「實時」(當前)數據的新 SSTable 的操作。這是有效的,因為當 SSTable 被排序時,我們只讀取一次,並且當從多個 SSTable(1+2+3)壓縮數據時,會檢查它們的最新值。因此,例如,「A」的最新值是「刪除它」,它會從生成的 SSTable 1+2+3 壓縮中刪除。這就是為什麼當我們刪除數據時,在下一次壓縮之前我們不會看到它反映在磁碟空間上。
有不同的策略,每種策略都與何時以及如何運行壓縮的不同演算法相關聯。讓我們在這裡列出主要的:
- 默認情況下,ScyllaDB 使用size-tiered compaction strategy (STCS),非常高效。它將 SSTable 壓縮到類似大小的桶中,因此所有 SSTable 的大小大致相同。但不利的一面是,如果您的 SSTable 太大,我們需要在寫入之前讀取,這可能會佔用大量磁碟空間。(此壓縮策略需要大約 50% 的可用磁碟空間來寫出大型壓縮。另請參閱此部落格。)
- 時間窗口壓縮策略(TWCS)也使用大小分層壓縮;但有時間窗口桶。它設計用於時間序列數據。
- 分級壓縮策略 (LCS)不需要一半的磁碟空間可用,並通過具有多個級別來分隔數據。然而,這需要更多的寫入 I/O,因此對於寫入繁重的工作負載來說效率很低。

大小分層壓縮策略是 ScyllaDB 使用的默認策略,當系統有足夠的大小相似的 SSTables 時觸發。正如您在此圖中看到的那樣,它具有磁碟使用高峰。它們發生在 ScyllaDB 壓縮所有 SSTable 時。例如,當我們認為記錄佔用了太多空間並且我們想通過運行 nodetool compact 來擺脫它們時,它會壓縮所有 SSTables。因此,在我們完成輸出 SSTable 的寫入之前,無法刪除輸入 SSTable。在壓縮結束之前,我們在磁碟上有兩次數據,在輸入 SSTables 和(壓縮的)輸出 SSTable 中。我們暫時需要磁碟空間是資料庫中可用數據的兩倍。因此,要使這種壓縮策略起作用,我們需要磁碟始終處於半滿狀態。

在這種策略,Leveled Compaction Strategy (LCS) 中,大小均勻的 SSTable 被劃分為級別。每次壓縮都會生成一個小的 SSTable,它最終會進入下一個級別。由於每個小的 SSTable 沒有重疊的鍵範圍,它們可以並行壓縮。而且因為它們很小,而且永遠不必編寫一個巨大的 SSTable,所以你不會遇到與大小分層壓縮相同的空間問題。
該策略的缺點是因為我們需要在每次 SSTable 更改級別時重寫相同的數據,這可能是 I/O 密集型的。
在這裡我們可以看到各有各的優點。只寫工作負載的大小分層和分級工作,但一個將佔用兩倍的磁碟空間,另一個將佔用兩倍的寫入操作。Leveled 最適合覆蓋數據,因為它不會像大小分層一樣保留所有數據。對於許多更新,水平可能是一個問題,因為它也需要許多寫入。
有很多事情需要考慮。如果您對最佳策略有任何疑問,您應該查看我們的文檔。
大分區和熱分區
這些是什麼?正如我們在討論集群鍵時所看到的,我們可以在同一分區中的集群鍵下擁有大量值。導致分區中的行數驚人。這樣的分區稱為大分區,這是一個問題,因為當您讀取它時,查詢可能會更慢,因為 ScyllaDB 不索引分區內的行,並且還因為分區內的查詢無法並行化。
這可能會變得更糟。您可能會遇到大分區問題和熱分區問題。這是當其中一個分區比其他分區更容易訪問時。這種熱分區問題通常發生在數據分布不均時。例如,在寵物診所,我們有一隻總是生病的特定寵物。所以我們為這個寵物生成並標記數據。

我們有辦法避免這種情況。首先,您需要了解您的數據及其行為方式。其次,通過向分區鍵添加更多列來增加粒度是很常見的。在這個例子中,我們有一個心率表,我們在主鍵中添加了日期。所以我們的分區不僅是 by pet_chip_id也是 by date。這使得分區更小,更易於管理。

為了跟蹤大分區,我們保留了一個名為system.large_partitions. 每次將一個大分區寫入磁碟時——這意味著,在它被從 memtable 中刷新之後——我們都會在這個表中添加一個條目。可以檢測隨著時間的推移生成了多少大分區,以便了解數據的行為方式並根據需要改進數據分布。請注意,此數據僅在數據寫入磁碟後可用,之前不可用。
大分區檢測閾值可以在scylla.yaml文件中用compaction_large_partion_warning_threshold_mb參數設置,默認為100M。您可以使用與您的用例相關的任何內容。每個大於此閾值的分區都將在大分區表中報告,並在日誌中寫入警告,因此您還可以在日誌系統上設置警報。

