JSON已經被開發者在不少場景中頻繁使用,可是其實將Cassandra用於JSON或其餘面向文檔的用例並不容易。前端
爲了讓開發者在使用原生的JSON的同時還能享受Cassandra帶來的可靠性和伸縮性,咱們開發了Stargate Cassandra Documents API——它使得絕大多數的Cassandra發行版本都能經過一個REST API使用JSON。git
若是你像我同樣,當你開始編寫一些新應用程序時,你可能會發現你本身在使用JSON。也許你正在使用Node.js或Python或任何其餘動態編程語言(dynamic programming language),這些語言原生的數據格式正巧與JSON相似;或者你正使用從REST API中拉取的數據。 程序員
不管是哪一種狀況,愈來愈多的狀況是全部的新應用程序都會在某個點與JSON匯合。大多數時候這不是個問題,這只是咱們現在建構軟件的方式而已。然而有一個問題——Cassandra其實不是特別擅長處理JSON……github
若是深刻了解一下,你會知道問題並不在於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正在被積極開發中,咱們盼望着很快帶給你有關更多提高改進的新消息!