Cassandra + JSON?答案就是Stargate Documents API

DataStax發表於2020-11-23

JSON已經被開發者在很多場景中頻繁使用,但是其實將Cassandra用於JSON或其他面向文件的用例並不容易。

 

為了讓開發者在使用原生的JSON的同時還能享受Cassandra帶來的可靠性和伸縮性,我們開發了Stargate Cassandra Documents API——它使得絕大多數的Cassandra發行版本都能通過一個REST API使用JSON。


 

如果你像我一樣,當你開始編寫一些新應用程式時,你可能會發現你自己在使用JSON。也許你正在使用Node.js或Python或任何其他動態程式語言(dynamic programming language),這些語言原生的資料格式正巧與JSON類似;或者你正使用從REST API中拉取的資料。 

無論是哪種情況,越來越多的情況是所有的新應用程式都會在某個點與JSON匯合。大多數時候這不是個問題,這只是我們如今建構軟體的方式而已。然而有一個問題——Cassandra其實不是特別擅長處理JSON……

如果深入瞭解一下,你會知道問題並不在於JSON這種資料格式本身,雖然Cassandra並沒有讓JSON變得更易於使用;問題在於大多數程式設計師在建構程式時使用JSON的方式。

迭代開發意味著計劃經常會改變。比如使用者登錄檔格需要一些新的條目,前端開發人員就率先加入了這些條目。當我們使用這個API時,就會有一些額外的資料被返回。歡迎來到這個鬆耦合(loosely coupled)的世界,所有的一切都是開心和有趣的——直到我的應用程式需要把這些資料傳送到資料庫。

其實早期的Cassandra版本要做到這件事很容易,但是隨著這個專案逐漸成熟,像是對企業友好的類SQL查詢語言以及更好的索引等功能被加入之後,意味著我們需要Cassandra資料庫要有一個固定的模式(schema)。逐漸地,將Cassandra用於類似JSON的事情或其他面向文件的用例變得越來越困難。

 

進入Stargate(意為“星際之門”)——如果你只需知道一件關於Stargate團隊的事情,那應該就是:我們的個人使命是讓Cassandra變得對於每個開發人員來說都易於使用。想辦法讓Javascript的開發者在使用原生的JSON的同時還能享受Cassandra帶來的可靠性和伸縮性——這是一個我們不能錯過的挑戰。

也正是這個想法催生了Stargate Documents API——它使得絕大多數的Cassandra發行版本(Cassandra 3.11, Cassandra 4.0, and DataStax Enterprise 6.8)都能通過一個REST API使用JSON。

 

01 Stargate Documents API的特點和設計

當我們剛開始構建這個API的框架時,我們意識到Cassandra完全不像是一個文件資料庫。

表達一行一行的資料是非常直觀簡單的,但是表達樹狀結構的JSON資料就沒那麼容易了。另外如果還想做到將JSON資料對映到Stargate管理的資料庫表,並保證讀取和寫入的速度還保持在相當快的水準,這就更是複雜了。

基於這些,為了能夠達成目標,我們詳細計劃了三個主要的設計考量:基於Cassandra的文件建模、高效處理讀寫請求,以及合理處理刪除操作。

接下來,這篇文章將介紹我們是如何構想每一個設計以及解決遇到的一些小問題的。

 

02 基於文件切分(Document Shredding)在Cassandra中進行文件建模

我們要決定的第一件事就是管理基於文件集合(document collection)的資料庫表的模式(schema)。在與幾位Cassandra專家進行了有建設性的討論後,我們決定當使用者建立一個文件,就會用下面的語句建立一個相應的資料庫表:

create table <name> (
  key text,
  p0 text,
  … p[N] text,
  bool_value boolean,
  txt_value text,
  dbl_value double,
  leaf text
)

 

到這裡,我們得要解決長度無上限的資料模型帶來的問題。因為所有有[N]或更少深度的JSON文件都可以被加入到這個表中,JSON中的每一個值將會在表中儲存為一行。所以如果我想表示一個叫做“x”的含有JSON的文件:

{"a": { "b": 1 }, "c": 2}

 

這個文件將會被“切分”成像這樣的行:

  

對於包含陣列(array)的資料,比如:

{"a": { "b": 1 }, "c": [{"d": 2}]}

 

就會被拆分成這樣的兩行:

 

陣列元素在儲存時,會在一列中被方括號括起來。

 

03 高效處理讀寫請求

下一個出現的問題是:想要更新一個文件,自然而然就要先從資料庫中讀取已有的文件,看看要對其做些什麼改變,然後再寫入更新的資料。 

對大多數資料儲存來說,這個“寫前讀(read-before-write)”的過程是臭名昭著的影響效能和一致性的問題的根源。因此,我們決心要不惜一切避免任何“寫前讀”的操作。

有一個具體的實現細節很有意思——當你向文件寫入一些資料時,這個寫入操作其實只是一個包含了一些插入操作和刪除操作的簡單的批處理。在某些情況下,這會導致文件在資料庫中對應的行鍼對同一個JSON資料域,可能會顯示出兩種不同的狀態。

然後,當這些行都被讀取出來之後,Stargate的Cassandra Documents API就會選取寫入時間戳比較新的資料,從而調和衝突的資訊(這和Cassandra本身的原理很類似)。

這讓我們的寫入操作變得很快,同時不必在讀取操作方面犧牲太多——因為前面所說的這種需要衝突調和的情況並不多見,即使出現,也會很快被解決。對於我們基礎的讀寫操作,這也奠定了非常重要的核心原則:

  • 每一個向一個單獨的文件所做的寫入操作,都是一個單獨的、含有多個語句的批處理。

  • 每一個向一個單獨的文件所做的讀取操作,都是一個單獨的SELECT語句。 

讀操作和寫操作都準備就緒了,那刪除操作呢?

 

04 合理處理刪除

由於Cassandra資料庫本身的分散式屬性,在Cassandra中做刪除操作其實與插入操作很類似。不過刪除操作所做的,是在特定的寫入時間通過寫入“tombstone(墓碑)”來標誌一行資料的死亡。

入土為安吧……等等,事情其實還沒結束——

Cassandra會週期性地做壓實(compaction)操作(這個頻率取決於你的壓實策略和/或叢集負載),從而刪除墓碑並釋放壓力。所以避免讓Cassandra過載的唯一方法就是確保刪除操作的頻率足夠的低。

由於陣列的存在,刪除操作給Stargate的Cassandra Documents API帶來了一個問題。現在讓我們來談談這點。

想象一下,如果你有一個行,其中的某個單元是一個長度為十萬個元素的陣列。然後如果你進行一個更新操作(通過HTTP中的PUT方法)並決定給這個單元賦值為一個新的陣列。結果就是整個十萬行的資料都要被刪除,也就是說整整十萬個墓碑要被寫入系統。 

這麼多墓碑在一次操作中被寫入,其數量相當之大。如果你再多這麼操作幾次,Cassandra很可能會變得非常之慢。所以,我們還需要對每個表的資料結構再做一次大的調整。

 

我們前面提到過,存在資料庫中的陣列訪問路徑(array path)是帶方括號的。比如陣列序列為0的元素會被儲存為[0]。這將意味著像下面這樣刪除100,000個元素:

DELETE FROM <name> where p0 in ('[0]', '[1]', '[2]', …, '[99999]')

這就導致了100,000個墓碑將被寫入。

 

相比這麼做,我們決定向所有的陣列元素的開頭都充填多個0——也就是說索引序號為0的元素會被記作[000000],而索引序號為99999的元素會被表示記作[099999]。通過這種方式,我們可以將刪除語句改成這樣:

DELETE FROM <name> where p0 >= '[000000]' and p0 <= '[999999]'

相比100,000個單元的墓碑,這種方式會使得只有一個所謂的“區間”墓碑(range tombstone)會被寫入(提示:在Cassandra中,大於號和小於號可以依照字典順序作用於字串型別的資料)。這也將陣列長度限制放寬至一百萬個元素,這可真是相當巧妙!

 

下方的時間序列圖顯示了在你以每週一次的頻率做壓實操作的前提下,舊方法和新方法的效果對比:

 

05 簡單瞭解此API的效能

⚠️在開始這個部分之前,我們想先提一下:雖然基準測試是非常好的工具,但並非能絕對說明一個系統在自然條件和真實負載下的表現。另外,我們也還沒在同一硬體上,將擁有此API加成的Cassandra與其它文件資料庫進行對比……是的,還沒有。好吧,讓我們現在就來看看它的效能!

為了測試我們的Cassandra Documents API是否足夠的快,我們用一個單獨的Cassandra儲存節點和一個單獨的Stargate節點進行了一次基準測試(Stargate是包含了Cassandra Documents API的API)。

然後我們進行了兩個不同的基準測試,一個用HTTP GET重複地在一個文件中隨機獲取路徑,另一個用HTTP POST重複地建立新的文件。這兩個操作都分別進行了100,000次,下面的圖中展示了這次測試的結果。

 

簡單來說,由於現在並沒有可以用於對比的基準資料,這次基準測試中分為這樣三種情況:一次只有一個使用者、稍微多一些併發操作(一次10個使用者)、更多的併發操作(一次100個使用者)。

 

需注意,此處我們在後端只有一個節點,更多的併發操作會引起效能的退化(degradation in performance)。如果想要承載更高程度的併發請求,你應該使用多個節點。

下面的是讀操作的測試結果:

下面的是寫操作的測試結果:

 

從上面的結果中,我們可以看到在我們設定的一定程度上的併發操作下,Stargate的Cassandra Documents API表現相當不錯。

 

06 結束語

我們希望你享受這篇對於Stargate Cassandra Documents API的快速介紹。如果你對使用這個API有興趣,不妨點選文末“閱讀原文”前往Stargate.io檢視更多相關資訊,看看如何將這個API用在你自己的Cassandra系統中。

 

來加入我們的Discord Community,你可以在此獲取Stargate的最新動態,並且最先使用Stargate的新功能

https://discord.com/invite/5gY8GDB

 

如果你有任何問題或是想要提交任何程式碼,來看看我們的GitHub倉庫吧

https://github.com/stargate/stargate

 

Stargate的Cassandra Documents API正在被積極開發中,我們盼望著很快帶給你有關更多提升改進的新訊息!

 

相關文章