用于 JavaScript 應(yīng)用程序的五大嵌入式數(shù)據(jù)庫
我們習(xí)慣于將數(shù)據(jù)庫視為巨大的存儲平臺,我們可以在其中存儲我們需要的所有數(shù)據(jù),然后通過某種形式的查詢語言檢索它。擴(kuò)展這些數(shù)據(jù)庫,保持信息的一致性和容錯性本身就是一個挑戰(zhàn)。但是,當(dāng)我們的數(shù)據(jù)需求非常小時會發(fā)生什么?
當(dāng)RedShift、BigQuery、甚至MySQL對我們微小的數(shù)據(jù)需求來說是一個太大的解決方案時,會發(fā)生什么?好吧,事實(shí)證明,有一個應(yīng)用程序可以解決這個問題。事實(shí)上,有很多選擇,所以在這里,我將介紹5大嵌入式數(shù)據(jù)庫,以滿足你的微小數(shù)據(jù)需求。
什么是嵌入式數(shù)據(jù)庫?
當(dāng)我們讀到“嵌入式”這個詞時,90% 的人會得出結(jié)論,我說的是物聯(lián)網(wǎng)或移動設(shè)備。但這種情況并非如此。
反正不是唯一的情況。誠然,這些系統(tǒng)的資源非常有限,這使得大多數(shù)傳統(tǒng)的數(shù)據(jù)庫系統(tǒng)很難配置和安裝在那里。
但是對于小型數(shù)據(jù)庫還有其他的用例,也就是將它們嵌入到軟件產(chǎn)品中。例如,想象一下通過您的IDE在一個大型代碼存儲庫上進(jìn)行搜索。IDE可以嵌入一個反向索引數(shù)據(jù)庫,允許您搜索關(guān)鍵字并快速獲取相關(guān)文件的參考。或者在您最喜歡的電子郵件桌面客戶端上執(zhí)行搜索時,該客戶端很可能也有一個嵌入式數(shù)據(jù)庫。所有的電子郵件都存儲在那里并被編入索引,所以你可以快速輕松地訪問這些信息。
到目前為止,您可能已經(jīng)了解了嵌入式數(shù)據(jù)庫的另一個巨大好處,即它們不需要與網(wǎng)絡(luò)調(diào)用進(jìn)行交互。與標(biāo)準(zhǔn)數(shù)據(jù)庫相比,這是一個巨大的性能提升。從本質(zhì)上講,在正常的開發(fā)中,你希望把數(shù)據(jù)庫放在自己的服務(wù)器(或服務(wù)器集群)上,這樣它的資源消耗就不會影響到你架構(gòu)中的其他組件,而對于嵌入式數(shù)據(jù)庫,你希望它們盡可能地靠近客戶端代碼。這就減少了它們之間的延遲,避免了對通信渠道(即網(wǎng)絡(luò))的依賴。
現(xiàn)在,這個想法可以有多種形式,從使用 JSON 文件作為主存儲的快速內(nèi)存數(shù)據(jù)庫,到可以使用類似 SQL 的語言進(jìn)行查詢的高效微型關(guān)系數(shù)據(jù)庫。
讓我們來看看5種選擇。
LowDB
讓我們從簡單的開始,LowDB 是一個小型的內(nèi)存數(shù)據(jù)庫。這是一個非?;镜慕鉀Q方案,但它解決了一個非常簡單的用例:需要從基于 JavaScript 的項(xiàng)目中存儲和訪問類似 JSON 的結(jié)構(gòu)(即文檔)。
LowDB的一個主要好處是它可以從JavaScript中使用,也就是說:它可以用于后端、桌面和瀏覽器代碼。
在后端,你可以將它與 Node.js 一起使用,對于桌面開發(fā),它可以集成到一個 Electron 項(xiàng)目中,最后,它也可以通過其集成的 JS 運(yùn)行時直接在瀏覽器上運(yùn)行。
這個數(shù)據(jù)庫提供的API也相當(dāng)簡單和簡約,它不提供任何開箱即用的搜索功能。它只限于將一個JSON文件的數(shù)據(jù)加載到一個數(shù)組變量中,讓你(用戶)以你認(rèn)為合適的方式找到你要找的東西。
例如,看看下面的代碼:
- import { LowSync, JSONFileSync } from 'lowdb'
- const title = "This is a test"
- const adapter = new JSONFileSync('file.json')
- const db = new LowSync(adapter)
- db.read() //將文件內(nèi)容加載到內(nèi)存中
- db.data ||= { posts: [] } //默認(rèn)值
- db.data.posts.push({ title }) //將數(shù)據(jù)添加到“集合”中
- db.write() //通過將數(shù)據(jù)保存到JSON文件來持久化數(shù)據(jù)
- //任何類似于查找的操作都由用戶自己來完成
- let record = db.data.posts.find( p => p.title == "Hello world")
- if(!record) {
- console.log("No data found!")
- } else {
- console.log("== Record found ==")
- console.log(record)
- }
正如你所看到的,這里有趣的部分不是默認(rèn)的行為,而是我正在使用一個叫做 JSONFileSync 的適配器。我可以很容易地使用一個由我創(chuàng)建的自定義的適配器,這才是這個數(shù)據(jù)庫的真正強(qiáng)項(xiàng)。
它具有高度的可擴(kuò)展性并與 TypeScript 兼容,后者為數(shù)據(jù)存儲提供了類似于模式的行為(即不允許添加不遵循預(yù)設(shè)模式的數(shù)據(jù))。
如果混合使用這兩種選項(xiàng),那么LowDB將成為處理本地類似json的數(shù)據(jù)的有趣選項(xiàng)。
LevelDB
LevelDB 是由 Google 構(gòu)建的開源鍵值數(shù)據(jù)庫。它是一種超快但非常有限的鍵值存儲,其中數(shù)據(jù)按開箱即用的鍵排序存儲。
它只有三個基本操作:Put、Get 和 Delete,沒有別的——如果你仔細(xì)想想,有點(diǎn)像 LowDB。
和LowDB一樣,它沒有一個客戶端-服務(wù)器封裝器,這意味著沒有辦法從任何語言與它通信,如果你想使用它,你必須使用C/C++庫,如果你想要一個類似服務(wù)器的行為,你必須自己封裝它。
就像我們在這里要介紹的大多數(shù)案例一樣,功能非?;?,因?yàn)樗鼈兒w了一個非常簡單但需要的用例:在靠近代碼的地方存儲數(shù)據(jù)并快速訪問。
該數(shù)據(jù)庫的存儲架構(gòu)是圍繞著日志結(jié)構(gòu)的合并樹(LSM),這意味著它被優(yōu)化為大型的連續(xù)寫操作,而不是小型的隨機(jī)操作。
LevelDB 的一個主要限制是,一旦打開,它就會在存儲上獲得系統(tǒng)級鎖,這意味著當(dāng)時只有一個進(jìn)程可以與數(shù)據(jù)庫交互。當(dāng)然,您可以使用多個線程來并行化該進(jìn)程中的某些操作。但這就是它的范圍。
有趣的是,這個數(shù)據(jù)庫被用作Chrome的IndexedDB的后臺數(shù)據(jù)庫,顯然Minecraft Bedrock版也使用它來存儲一些分塊和實(shí)體數(shù)據(jù)(盡管從外觀上看,他們使用的是谷歌實(shí)現(xiàn)的略微修改的版本)。
Raima 數(shù)據(jù)庫管理器
我之前提到過物聯(lián)網(wǎng)不是嗎? Raima 是速度最快的數(shù)據(jù)庫管理器之一,專門針對在資源受限的物聯(lián)網(wǎng)設(shè)備中運(yùn)行進(jìn)行了優(yōu)化。
資源受限的環(huán)境是什么意思? Raima 只需要 350kb 的 RAM 即可運(yùn)行。這就是我可以極簡的資源利用。
該解決方案的主要特點(diǎn)之一是它完全支持 SQL,這一點(diǎn)在之前的任何解決方案中都沒有出現(xiàn)。它提供了一個關(guān)系數(shù)據(jù)模型,并允許您使用 SQL 語言進(jìn)行查詢。
與 LevelDB 不同的是,它還允許通過客戶端-服務(wù)器架構(gòu)對數(shù)據(jù)庫進(jìn)行多進(jìn)程訪問(即這種架構(gòu)允許您比其他架構(gòu)更遠(yuǎn)離源代碼)。如果您決定采用接近源代碼的嵌入式應(yīng)用程序,您還可以使用多線程來支持對多個數(shù)據(jù)庫的并發(fā)訪問。
Raima的靈活性允許你從傳統(tǒng)的客戶-服務(wù)器方法到最有效的(當(dāng)然也是有限的)使用案例,即由單個客戶消費(fèi)的單一內(nèi)存數(shù)據(jù)庫。但是,嘿,這是一個非常有效的嵌入式數(shù)據(jù)庫的使用案例。
這種靈活性使它成為一種非常通用的解決方案。當(dāng)然,每種部署模式都有自己的優(yōu)點(diǎn)和限制,但是也會針對特定的用例進(jìn)行優(yōu)化。因此,請確保您選擇了正確的一個,并從這個數(shù)據(jù)庫中獲得最大的好處。
Apache Derby
如果你正在尋找另一個非常小的、類似SQL的數(shù)據(jù)庫,Apache Derby很可能是你正在尋找的東西。
Derby完全是用JAVA編寫的,當(dāng)它聲稱只有3.5Mb的內(nèi)存占用時,也損失了一點(diǎn)信譽(yù)。畢竟,如果沒有在主機(jī)系統(tǒng)上安裝JVM,您就不能運(yùn)行或使用它。
也就是說,如果您的用例允許使用JVM,那么很好,您可以繼續(xù)考慮Derby,否則您可能希望使用更本地的解決方案,如LevelDb或Raima。
但正如我所說,如果您已經(jīng)在從事 JAVA 項(xiàng)目并且需要集成一個小型、可靠、基于 SQL 的數(shù)據(jù)庫,那么 Derby 絕對是一個潛在的候選者。
它帶有一個集成的 JDBC 驅(qū)動程序,因此不需要額外的依賴項(xiàng)。它既可以在 JAVA 應(yīng)用程序內(nèi)的嵌入式模式下工作,也可以作為獨(dú)立服務(wù)器運(yùn)行,允許多個應(yīng)用程序同時與其交互(類似于 Raima 的工作方式,但沒有許多變體)。
說實(shí)話,這個項(xiàng)目最大的缺點(diǎn)是它的文檔。它可能是JAVA社區(qū)的一個標(biāo)準(zhǔn),但它對用戶并不友好,大部分的官方鏈接都把讀者送到一個私人的匯合頁面。這里的許多其他解決方案在涉及到文檔時提供了更順暢的體驗(yàn),這也有助于他們產(chǎn)品的采用。
solidDB
最后,solidDB提供了一個非常有趣的內(nèi)存關(guān)系數(shù)據(jù)庫,它同時還可以增強(qiáng)一個持久性模型。聲稱它可以保持兩個數(shù)據(jù)存儲選項(xiàng)實(shí)時同步。這可不是個小要求。
本質(zhì)上就像這里列出的其他解決方案一樣,solidDB 可以通過 ODBC 或 JDBC 訪問,這允許 JAVA 和 C 應(yīng)用程序通過 SQL 與其交互。
也像這里列出的一些解決方案一樣,它可以以多種模式部署:
- 高可用性模式。這涉及具有重復(fù)數(shù)據(jù)的多個服務(wù)器。當(dāng)然,這種模式在我們考慮的用例中并不多。
- 共享內(nèi)存訪問。這個方案非常有趣,因?yàn)樗粌H將數(shù)據(jù)保存在內(nèi)存中(就像已經(jīng)列出的其他解決方案一樣),而且還允許多個應(yīng)用程序訪問該內(nèi)存(因此有共享內(nèi)存部分)。當(dāng)然,對共享內(nèi)存的直接訪問需要由同一節(jié)點(diǎn)內(nèi)的應(yīng)用程序完成,但是,它還允許基于 JDBC/ODBC 從外部節(jié)點(diǎn)訪問相同的數(shù)據(jù)。將共享內(nèi)存轉(zhuǎn)變?yōu)榫哂型獠吭L問權(quán)限的內(nèi)存數(shù)據(jù)庫。
由于訪問數(shù)據(jù)的速度快如閃電,思科、阿爾卡特、諾基亞和西門子等多家知名企業(yè)聲稱將使用該數(shù)據(jù)庫進(jìn)行關(guān)鍵任務(wù)操作。
鑒于其所有的部署模式、廣泛的文檔和高需求的客戶名單,我可以看到這是這個名單上最可靠、最穩(wěn)定、最快速的嵌入式數(shù)據(jù)庫之一。
嵌入式數(shù)據(jù)庫是為了處理一個非常具體的用例,或者通過提供快速可靠的數(shù)據(jù)存儲和最小的延遲,或者通過允許快速安全地訪問數(shù)據(jù)。這里列出的解決方案通過不同的手段實(shí)現(xiàn)這些目標(biāo),這取決于你和你的特定環(huán)境,以決定哪一個是適合你的。