MongoDB??數(shù)據(jù)模型的設(shè)計(jì)模式及優(yōu)缺點(diǎn)
在實(shí)際開發(fā)中,大多數(shù)性能問題都可以追溯到糟糕的模型設(shè)計(jì)。官方也提供分享過文檔模型設(shè)計(jì)的進(jìn)階技巧,這里簡(jiǎn)單翻譯記錄一下。
簡(jiǎn)介
官方文章的地址是 Building with Patterns: A Summary,其中匯總了 12 種設(shè)計(jì)模式及使用場(chǎng)景。
上述的圖表列舉了 12 種設(shè)計(jì)模式及應(yīng)用場(chǎng)景,主要是以下這些:
- 近似值模式(Approximation Pattern)
- 屬性模式(Attribute Pattern)
- 桶模式(Bucket Pattern)
- 計(jì)算模式(Computed Pattern)
- 文檔版本控制模式(Document Versioning Pattern)
- 擴(kuò)展引用模式(Extended Peference Pattern)
- 異常值模式(Outlier Pattern)
- 預(yù)分配模式(Preallocated Pattern)
- 多態(tài)模式(Polymorphic Pattern)
- 模式版本控制模式(Schema Versioning Pattern)
- 子集模式(Subset Pattern)
- 樹型模式(Tree and Graph Pattern)
近似值模式
示例描述
淘寶在往年“雙十一”都會(huì)有一個(gè)銷售額大屏展示,當(dāng)銷售額小于 1 億時(shí),可能展示的是實(shí)際的數(shù)量,當(dāng)銷售額超過 1 億時(shí),單位立即變成以“億”為單位,對(duì)于展示的大屏而言,”億“以下的單位這個(gè)時(shí)候并不是很重要了。
對(duì)于上述的場(chǎng)景,如果每次幾十、幾百都直接去更新數(shù)據(jù)庫中的實(shí)際值,則更新數(shù)據(jù)庫會(huì)變得非常頻繁,對(duì)于數(shù)據(jù)庫的壓力是非常大的。
實(shí)際上,并不需要每次都去更新數(shù)據(jù)庫,我們只需要將這個(gè)實(shí)際的精確值存儲(chǔ)在內(nèi)存中,使用 1 億作為一個(gè)閾值,一旦超過這個(gè)閾值就精確值更新進(jìn)數(shù)據(jù)庫中。
對(duì)于精度不是首要考慮因素時(shí),那么就可以使用近似值模式,尤其是消耗資源(時(shí)間、內(nèi)存、CPU 周期)非常昂貴時(shí)效果會(huì)更佳。
近似值模式就是通過減少數(shù)據(jù)的寫入頻率,從而降低了架構(gòu)的復(fù)雜度和資源開銷,進(jìn)而提升了整體的性能與效率。
優(yōu)缺點(diǎn)
近似值模式的優(yōu)點(diǎn)如下:
- 由于是近似的數(shù)據(jù),不必時(shí)時(shí)刻刻寫入,數(shù)據(jù)庫的寫操作量級(jí)更小
近似值模式的缺點(diǎn)如下:
- 存儲(chǔ)的是近似的數(shù)據(jù),無法應(yīng)對(duì)需要展示精確數(shù)據(jù)的場(chǎng)景
- 此模式需要在應(yīng)用層實(shí)現(xiàn)
屬性模式
示例描述
屬性模式運(yùn)用到了 MongoDB 多鍵索引的概念,支持對(duì)數(shù)組中的嵌套子文檔中的某個(gè)屬性進(jìn)行索引。
假設(shè)現(xiàn)在有一個(gè)關(guān)于電影的集合,其中文檔中會(huì)包含標(biāo)題、導(dǎo)演、制片人、演員、上映時(shí)間等等信息,對(duì)于跨地區(qū)上映的電影,有可能不同地區(qū)的上映時(shí)間是不一樣的。
如下展示是一條電影的文檔數(shù)據(jù):
{ "title": "Star Wars", "director": "George Lucas", // 不同地區(qū)有不同的上映時(shí)間 "release_US": ISODate("1977-05-20T01:00:00+01:00"), "release_France": ISODate("1977-10-19T01:00:00+01:00"), "release_Italy": ISODate("1977-10-20T01:00:00+01:00"), "release_UK": ISODate("1977-12-27T01:00:00+01:00"), }
為了支持對(duì)所有上映時(shí)間做一個(gè)快速搜索,也許我們需要將所有的上映時(shí)間設(shè)置為單一索引,這個(gè)時(shí)候,索引的數(shù)量就會(huì)變得顯而易見的多。
使用屬性模式,我們通過將這些上映時(shí)間信息移動(dòng)到一個(gè)數(shù)組中,然后再對(duì)這個(gè)數(shù)組建立一個(gè)多鍵索引索引,以實(shí)現(xiàn)使用一個(gè)索引替代多個(gè)類似索引的功能。
如下是修改結(jié)構(gòu)后的文檔數(shù)據(jù):
{ "title": "Star Wars", "director": "George Lucas", // 所有的地區(qū)的上映時(shí)間都放在同一個(gè)屬性內(nèi)部 "releaseList": [ { "region": "US", "date": ISODate("1977-05-20T01:00:00+01:00") }, { "region": "France", "date": ISODate("1977-10-19T01:00:00+01:00") }, { "region": "Italy", "date": ISODate("1977-10-20T01:00:00+01:00") }, { "region": "UK", "date": ISODate("1977-12-27T01:00:00+01:00") } ] }
優(yōu)缺點(diǎn)
屬性模式的優(yōu)點(diǎn)如下:
- 需要更少的索引
- 查詢變得更容易編寫,而且通常更快
桶模式
示例描述
桶模式有點(diǎn)類似于水平分庫,常見的水平分庫是將一個(gè)集合按照某一個(gè)規(guī)則分布到不同的數(shù)據(jù)庫上,桶模式是將一個(gè)集合中的文檔按照某一個(gè)規(guī)則合并起來。
假設(shè)現(xiàn)在有一個(gè)需要記錄用戶日志的需求,對(duì)于用戶的每一個(gè)動(dòng)作,都需要將其更新到 MongoDB 當(dāng)中,并且是記錄其動(dòng)作、時(shí)間。
對(duì)于這樣的日志數(shù)據(jù)來說,如果將每一個(gè)動(dòng)作都存儲(chǔ)成一個(gè)文檔,則將會(huì)占用極大的存儲(chǔ)空間和內(nèi)存。
使用桶模式的解決辦法就是,將一段時(shí)間的日志數(shù)據(jù)存儲(chǔ)成一個(gè)文檔,再將每一個(gè)動(dòng)作日志的數(shù)據(jù)存儲(chǔ)到子文檔數(shù)據(jù)中。
當(dāng)需要管理流式數(shù)據(jù)的時(shí)候,如時(shí)間序列、實(shí)時(shí)分析或物聯(lián)網(wǎng)應(yīng)用程序,桶模式就是一個(gè)很好的解決方案。
優(yōu)缺點(diǎn)
桶模式的優(yōu)點(diǎn)如下:
- 減少了集合中的文檔總數(shù)
- 提高了索引性能
- 可以通過預(yù)聚合簡(jiǎn)化數(shù)據(jù)的訪問
計(jì)算模式
示例描述
對(duì)于大型數(shù)據(jù)集,每一次計(jì)算都可能會(huì)占用極大的 CPU、磁盤、內(nèi)存等相關(guān)資源,甚至是影響到服務(wù)器上的其他計(jì)算。
而對(duì)于需要重復(fù)計(jì)算、讀取比寫入多的場(chǎng)景,計(jì)算模式提供了一種優(yōu)化的思路,以便降低服務(wù)器資源的占用。
拿一個(gè)電影觀看總?cè)藬?shù)的例子來說明:假設(shè)現(xiàn)在頁面上需要展示觀看電影的實(shí)際總?cè)藬?shù),而且這個(gè)頁面會(huì)有成千上萬的人訪問。
雖然,我們可以對(duì)電影的每一次放映都記錄起觀看人數(shù),但是要獲取總?cè)藬?shù),則需要拿出所有的放映場(chǎng)次的觀看人數(shù)之后計(jì)算其總和,這個(gè)計(jì)算就非常耗費(fèi)資源和時(shí)間。
對(duì)于只有放映場(chǎng)次變化之后,總?cè)藬?shù)才會(huì)更新的情況,實(shí)際數(shù)據(jù)庫讀取的次數(shù)遠(yuǎn)遠(yuǎn)大于寫入的次數(shù)。
在這個(gè)場(chǎng)景中,計(jì)算模式的思路是:每一次更新放映場(chǎng)次數(shù)據(jù)的時(shí)候,將這個(gè)放映場(chǎng)次的人數(shù)匯總到一個(gè)文檔當(dāng)中,這個(gè)文檔直接面向用戶的查詢。
優(yōu)缺點(diǎn)
計(jì)算模式的優(yōu)點(diǎn)如下:
- 對(duì)于頻繁的計(jì)算可以減少 CPU 的負(fù)載
- 查詢變得更容易編寫,而且通常更快
計(jì)算模式的缺點(diǎn)如下:
- 識(shí)別出需要使用此模式的的場(chǎng)景可能比較困難
- 除非必要,請(qǐng)勿過度使用此模式
文檔版本控制模式
示例描述
文檔版本控制模式在高度規(guī)范化的行業(yè)中非常有用,這些行業(yè)會(huì)要求數(shù)據(jù)的特定時(shí)間點(diǎn)版本。
假設(shè)現(xiàn)在有一個(gè)博客系統(tǒng),其中有一個(gè)記錄每次編輯博客文章歷史的功能,這樣的功能就能應(yīng)用文檔版本控制模式。
假設(shè)我們將所有的文章歷史都存儲(chǔ)在同一個(gè)集合當(dāng)中,則需要考慮大部分與文章相關(guān)的功能都要過濾掉歷史版本、版本越多則集合文檔數(shù)量越多等等問題。
文檔版本控制模式的想法是:文檔中需要記錄一個(gè)文檔的版本,將最新的文檔保存在一個(gè) current 集合中,而那些舊版本的文檔保存在 history 集合中。
為了最大化利用文檔版本控制模式的優(yōu)勢(shì),通常會(huì)假設(shè)數(shù)據(jù)訪問模式盡量符合以下要求:
- 每個(gè)文檔不會(huì)有太多的修訂版本
- 需要做版本控制的文檔不會(huì)太多
- 大多數(shù)的查詢都是基于文檔的最新版本
優(yōu)缺點(diǎn)
文檔版本控制模式的優(yōu)點(diǎn)如下:
- 容易實(shí)現(xiàn),對(duì)現(xiàn)有系統(tǒng)的影響小
- 在最新版本上進(jìn)行請(qǐng)求時(shí),沒有性能上的影響
文檔版本控制模式的缺點(diǎn)如下:
- 寫操作的數(shù)量會(huì)翻倍
- 請(qǐng)求需要被定位到正確的集合
擴(kuò)展引用模式
示例描述
MongoDB 是一個(gè)不需要提前建模的 NoSQL,當(dāng)不同文檔、不同集合之間存在關(guān)系的時(shí)候,通常會(huì)有嵌入和引用兩種方式。
嵌入就是將文檔數(shù)據(jù)嵌入到引用此數(shù)據(jù)的文檔中,訪問時(shí)直接訪問這一次文檔即可;引用就是只在文檔中引用另一個(gè)文檔的標(biāo)識(shí),訪問時(shí)需要訪問兩次數(shù)據(jù)庫才能拿到完整的數(shù)據(jù)。
擴(kuò)展引用模式是指僅復(fù)制經(jīng)常訪問并且不經(jīng)常更改的字段,而不是復(fù)制所有的數(shù)據(jù),減少信息的連接以提高性能。
這張圖的場(chǎng)景是:客戶和訂單是 1 對(duì) N 的關(guān)系,通常查詢訂單列表的時(shí)候需要展示客戶的一些信息,我們就需要考慮是否將客戶的信息冗余進(jìn)訂單信息中。
擴(kuò)展引用模式認(rèn)為,客戶的名稱和地址是不常做更新的,可以直接將這些信息冗余進(jìn)訂單表中,以達(dá)到減少兩個(gè)集合連接查詢的要求。
優(yōu)缺點(diǎn)
擴(kuò)展引用模式的優(yōu)點(diǎn)如下:
- 當(dāng)有大量的 JOIN 操作時(shí)可以提升性能
- 讀操作會(huì)更快,并且可以減少 JOIN 操作的數(shù)量
擴(kuò)展引用模式的缺點(diǎn)如下:
- 修改冗余的這部分?jǐn)?shù)據(jù)會(huì)比較復(fù)雜
異常值模式
示例描述
顧名思義,異常值模式主要用以解決超出應(yīng)用程序正常模式的少數(shù)異常查詢情況。
假設(shè)你正在搭建一個(gè)出售圖書的電子商務(wù)網(wǎng)站,現(xiàn)在需要記錄一本書都有哪些用戶購買過,一個(gè)常見的做法的是將購買的用戶標(biāo)識(shí)存儲(chǔ)在圖書文檔中,如下展示:
{ "_id": ObjectId('6392cecd4dd9624424ad025d'), "title": "三國(guó)演義", "author": "羅貫中", "purchase_customers": [ "user0", "user1", "user3", // ... ] }
對(duì)于上述的文檔結(jié)構(gòu),大部分情況下是適用的。但是,對(duì)于銷量特別高的圖書,極可能導(dǎo)致圖書文檔的大小超過 16MB 的限制。
使用異常值模式的方式是:在圖書文檔中添加一個(gè)字段來將其標(biāo)記為異常值,超過一定大小的內(nèi)容可以存儲(chǔ)在另一個(gè)文檔當(dāng)中,在應(yīng)用程序中對(duì)異常文檔做擴(kuò)展查詢處理,減少異常文檔對(duì)正常文檔的影響。
優(yōu)缺點(diǎn)
異常值模式的優(yōu)點(diǎn)如下:
- 防止整個(gè)應(yīng)用被某些異常的文檔或請(qǐng)求所影響
- 請(qǐng)求會(huì)針對(duì)那些典型的用例進(jìn)行優(yōu)化,而異常值仍將得到處理
異常值模式的缺點(diǎn)如下:
- 通常會(huì)為特定的查詢而進(jìn)行定制,因此一些臨時(shí)產(chǎn)生的查詢可能性能不太理想
- 此模式的大部分工作是在應(yīng)用程序代碼中完成的
預(yù)分配模式
示例描述
在使用 MMAPv1 存儲(chǔ)引擎時(shí),MongoDB 的一個(gè)常見優(yōu)化是提前分配所需的內(nèi)存,以滿足不斷增長(zhǎng)的文檔未來會(huì)達(dá)到的大小。
MMAPv1 中不斷增長(zhǎng)的文檔需要由服務(wù)端以相當(dāng)昂貴的成本進(jìn)行位置的遷移,而 WiredTiger 的無鎖機(jī)制(lock-free)和重寫(rewrite)更新算法不需要這種處理。
一個(gè)相對(duì)應(yīng)的例子就是,直接存儲(chǔ)一個(gè)二維數(shù)據(jù)可以做到預(yù)分配內(nèi)存,而存儲(chǔ)二維數(shù)組轉(zhuǎn)換后的稀疏數(shù)組則無法做到預(yù)分配內(nèi)存。
因此,在 MMAPv1 中,更推薦使用預(yù)分配模式直接存儲(chǔ)原始的二維數(shù)組。
優(yōu)缺點(diǎn)
預(yù)分配模式的優(yōu)點(diǎn)如下:
- 當(dāng)預(yù)先知道文檔結(jié)構(gòu)時(shí),可以簡(jiǎn)化設(shè)計(jì)
預(yù)分配模式的缺點(diǎn)如下:
- 簡(jiǎn)單和性能之間的權(quán)衡
多態(tài)模式
示例描述
在面向?qū)ο笾校鄳B(tài)指的是為不同數(shù)據(jù)類型的實(shí)體提供統(tǒng)一的接口,或使用一個(gè)單一的符號(hào)來表示多個(gè)不同的類型。
而 MongoDB 不強(qiáng)制要求集合的文檔擁有特定的結(jié)構(gòu),這里的多態(tài)模式指的是,集合中的文檔具有更多的相似性而不是差異性,文檔結(jié)構(gòu)都類似但又不完全相同。
其一種實(shí)現(xiàn)方案是將文檔分組在一起做查詢,而不是將其分散到多個(gè)集合中;另一種實(shí)現(xiàn)方案是使用嵌入式子文檔的模式匯總。
多態(tài)模式的一個(gè)典型用例是單一視圖應(yīng)用程序:假設(shè)現(xiàn)在一家較大的公司收購了其他公司,這些公司的業(yè)務(wù)都是類似的,數(shù)據(jù)庫都以類似的方式存儲(chǔ)了數(shù)據(jù)。
這個(gè)時(shí)候就可以利用 MongoDB 和多態(tài)模式在短時(shí)間內(nèi)構(gòu)建好單一視圖應(yīng)用程序。
除了單一視圖應(yīng)用程序外,多態(tài)模式的其他典型用例還有以下幾種:
- 內(nèi)容管理
- 移動(dòng)應(yīng)用程序
- 產(chǎn)品目錄
優(yōu)缺點(diǎn)
多態(tài)模式的優(yōu)點(diǎn)如下:
- 實(shí)現(xiàn)簡(jiǎn)單
- 查詢可以在單個(gè)集合中運(yùn)行
模式版本控制模式
示例描述
幾乎每個(gè)數(shù)據(jù)庫在其生命周期中的某個(gè)時(shí)刻都會(huì)產(chǎn)生變更,一旦數(shù)據(jù)庫中的數(shù)據(jù)模型發(fā)生變化,通常需要停止應(yīng)用程序,遷移數(shù)據(jù)庫以支持新模式,然后重新啟動(dòng)。
這種停機(jī)更新會(huì)導(dǎo)致糟糕的用戶體驗(yàn),而模式版本控制模式允許歷史版本和當(dāng)前版本的文檔在集合中同時(shí)存在,以此保障用戶體驗(yàn)。
通過使用 schema_version
字段定義模式的版本,并將其保存到數(shù)據(jù)庫中,每個(gè)新的模式版本都會(huì)增加 schema_version
字段的值。
在應(yīng)用程序內(nèi)部,為每個(gè)模式版本創(chuàng)建相應(yīng)的處理函數(shù),這樣即可適應(yīng)不同版本的數(shù)據(jù)。
優(yōu)缺點(diǎn)
模式版本控制模式的優(yōu)點(diǎn)如下:
- 不需要停機(jī)時(shí)間
- 模式遷移可控
- 減少未來的技術(shù)債務(wù)
模式版本控制模式的缺點(diǎn)如下:
- 在遷移過程中,對(duì)相同的字段可能需要兩個(gè)索引
子集模式
示例描述
MongoDB 將頻繁訪問的數(shù)據(jù)保存在 RAM 中,當(dāng)數(shù)據(jù)和索引的工作集超過分配的物理 RAM 時(shí),隨著磁盤訪問的發(fā)生以及數(shù)據(jù)從 RAM 中轉(zhuǎn)出,性能會(huì)開始下降。
為解決這個(gè)問題,一個(gè)方案是向服務(wù)器添加更多的 RAM,不過擴(kuò)展會(huì)有上限,而且非常昂貴;或者考慮對(duì)集合進(jìn)行分片,但這會(huì)帶來額外的成本和復(fù)雜性。
子集模式就解決了有大量數(shù)據(jù)的大文檔沒有被應(yīng)用程序使用而導(dǎo)致的工作集超過 RAM 容量的問題,比如說一個(gè)電商產(chǎn)品的評(píng)論可能有成千上萬條,但大部分情況下都只會(huì)訪問最近 10 個(gè)評(píng)論。
其實(shí)現(xiàn)方法就是,將一個(gè)存儲(chǔ)大文檔的集合拆分成多個(gè)子集,每一個(gè)子集都能為單獨(dú)的功能提供資源,減少了工作集的總體大小。
優(yōu)缺點(diǎn)
子集模式的優(yōu)點(diǎn)如下:
- 在總體上減小了工作集的大小
- 縮短了最常用數(shù)據(jù)的磁盤訪問時(shí)間
子集模式的缺點(diǎn)如下:
- 必須管理子集
- 請(qǐng)求附加的數(shù)據(jù)需要額外的數(shù)據(jù)庫訪問
樹形模式
示例描述
對(duì)于 SQL 來說,可以通過外鏈子結(jié)點(diǎn)或父結(jié)點(diǎn)的方式表示樹形結(jié)構(gòu)。
對(duì)于 MongoDB 而言,比較方便的就是通過存儲(chǔ)子結(jié)點(diǎn)數(shù)組的方式實(shí)現(xiàn),但是其缺點(diǎn)就是每次更新時(shí)都需要操作整個(gè)結(jié)構(gòu),不合適做頻繁更新,比如說家譜。
因此,當(dāng)數(shù)據(jù)是分層結(jié)構(gòu)并且經(jīng)常被查詢時(shí),樹形模式是比較優(yōu)的一個(gè)選擇。并且可以通過給數(shù)組中的屬性創(chuàng)建多鍵索引提高查詢效率。
優(yōu)缺點(diǎn)
樹形模式的優(yōu)點(diǎn)如下:
- 通過避免多次 JOIN 操作提高了性能
樹形模式的缺點(diǎn)如下:
- 需要在應(yīng)用程序中管理樹結(jié)構(gòu)的更新
到此這篇關(guān)于MongoDB 數(shù)據(jù)模型的設(shè)計(jì)模式的文章就介紹到這了,更多相關(guān)MongoDB 數(shù)據(jù)模型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mongodb基礎(chǔ)入門_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了mongodb基礎(chǔ)入門的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08詳解MongoDB數(shù)據(jù)庫基礎(chǔ)操作及實(shí)例
這篇文章主要介紹了詳解MongoDB數(shù)據(jù)庫基礎(chǔ)操作及實(shí)例的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下2017-09-09MongoDB詭異問題之sh.stopBalancer卡住的解決方法
這篇文章主要給大家介紹了關(guān)于MongoDB詭異問題之sh.stopBalancer卡住解決的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-03-03在MongoDB中實(shí)現(xiàn)時(shí)間范圍查詢的代碼詳解
MongoDB作為一個(gè)高性能、開源、無模式的文檔型數(shù)據(jù)庫,廣泛應(yīng)用于各種需要靈活數(shù)據(jù)模型的應(yīng)用場(chǎng)景中,在處理與時(shí)間相關(guān)的數(shù)據(jù)時(shí),MongoDB提供了強(qiáng)大的查詢能力,本文將詳細(xì)介紹如何在MongoDB中執(zhí)行時(shí)間范圍查詢,需要的朋友可以參考下2024-08-08MongoDB排序時(shí)內(nèi)存大小限制與創(chuàng)建索引的注意事項(xiàng)詳解
在數(shù)據(jù)量超大的情形下,任何數(shù)據(jù)庫系統(tǒng)在創(chuàng)建索引時(shí)都是一個(gè)耗時(shí)的大工程,下面這篇文章主要給大家介紹了關(guān)于MongoDB排序時(shí)內(nèi)存大小限制與創(chuàng)建索引的注意事項(xiàng)的相關(guān)資料,需要的朋友可以參考下2022-05-05