MongoDB是面向文檔的數據庫管理系統DBMS(顯然mongodb不是oracle那樣的RDBMS,而僅僅是DBMS)。 想想一下MySQL中沒有任何關系型數據庫的表,而由JSON類型的對象組成數據模型的樣子是如何的?
值得注意的是,MongoDB既不支持JOIN(連接)也不支持transaction(事務)。Significantly, MongoDB supports neither joins nor transactions.
但是請注意MongDB有着大量其他優良的特性,如二級索引、功能豐富的查詢語言以及對每一個單個文檔文件的原子寫保證以及完全一致性的讀取。
MongoDB
一、存儲引擎(Storage)
mongodb 3.0默認存儲引擎為MMAPV1,還有一個新引擎wiredTiger可選,或許可以提高一定的性能。
mongodb中有多個databases,每個database可以創建多個collections,collection是底層數據分區(partition)的單位,每個collection都有多個底層的數據文件組成。(參見下文data files存儲原理)
wiredTiger引擎:3.0新增引擎,官方宣稱在read、insert和復雜的update下具有更高的性能。所以后續版本,我們建議使用wiredTiger。所有的write請求都基於“文檔級別”的lock,因此多個客戶端可以同時更新一個colleciton中的不同文檔,這種更細顆粒度的lock,可以支撐更高的讀寫負載和並發量。因為對於production環境,更多的CPU可以有效提升wireTiger的性能,因為它是的IO是多線程的。wiredTiger不像MMAPV1引擎那樣盡可能的耗盡內存,它可以通過在配置文件中指定“cacheSizeGB”參數設定引擎使用的內存量,此內存用於緩存工作集數據(索引、namespace,未提交的write,query緩沖等)。
MMAPv1引擎:mongodb原生的存儲引擎,比較簡單,直接使用系統級的內存映射文件機制(memory mapped files),一直是mongodb的默認存儲引擎,對於insert、read和in-place update(update不導致文檔的size變大)性能較高;不過MMAPV1在lock的並發級別上,支持到collection級別,所以對於同一個collection同時只能有一個write操作執行,這一點相對於wiredTiger而言,在write並發性上就稍弱一些。對於production環境而言,較大的內存可以使此引擎更加高效,有效減少“page fault”頻率,但是因為其並發級別的限制,多核CPU並不能使其受益。此引擎將不會使用到swap空間,但是對於wiredTiger而言需要一定的swap空間。(核心:對於大文件MAP操作,比較忌諱的就是在文件的中間修改數據,而且導致文件長度增長,這會涉及到索引引用的大面積調整)
為了確保數據的安全性,mongodb將所有的變更操作寫入journal並間歇性的持久到磁盤上,對於實際數據文件將延遲寫入,和wiredTiger一樣journal也是用於數據恢復。所有的記錄在磁盤上連續存儲,當一個document尺寸變大時,mongodb需要重新分配一個新的記錄(舊的record標記刪除,新的記record在文件尾部重新分配空間),這意味着mongodb同時還需要更新此文檔的索引(指向新的record的offset),與in-place update相比,將消耗更多的時間和存儲開支。由此可見,如果你的mongodb的使用場景中有大量的這種update,那么或許MMAPv1引擎並不太適合,同時也反映出如果document沒有索引,是無法保證document在read中的順序(即自然順序)。3.0之后,mongodb默認采用“Power of 2 Sized Allocations”,所以每個document對應的record將有實際數據和一些padding組成,這padding可以允許document的尺寸在update時適度的增長,以最小化重新分配record的可能性。此外重新分配空間,也會導致磁盤碎片(舊的record空間)。
Power of 2 Sized Allocations:默認情況下,MMAPv1中空間分配使用此策略,每個document的size是2的次冪,比如32、64、128、256...2MB,如果文檔尺寸大於2MB,則空間為2MB的倍數(2M,4M,6M等)。這種策略有2種優勢,首先那些刪除或者update變大而產生的磁盤碎片空間(尺寸變大,意味着開辟新空間存儲此document,舊的空間被mark為deleted)可以被其他insert重用,再者padding可以允許文檔尺寸有限度的增長,而無需每次update變大都重新分配空間。此外,mongodb還提供了一個可選的“No padding Allocation”策略(即按照實際數據尺寸分配空間),如果你確信數據絕大多數情況下都是insert、in-place update,極少的delete,此策略將可以有效的節約磁盤空間,看起來數據更加緊湊,磁盤利用率也更高。
內存映射存儲引擎:MongoDB目前支持的存儲引擎為內存映射引擎。當MongoDB啟動的時候,會將所有的數據文件映射到內存中,然后操作系統會托管所有的磁盤操作。
備注:mongodb 3.2+之后,默認的存儲引擎為“wiredTiger”,大量優化了存儲性能,建議升級到3.2+版本。
摘自:http://shift-alt-ctrl.iteye.com/blog/2255580
數據文件
在MongoDB的數據文件夾中(默認路徑是/data/db)由構成數據庫的所有文件。每一個數據庫都包含一個.ns文件和一些數據文件,其中數據文件會隨着數據量的增加而變多。所以如果有一個數據庫名字叫做foo,那么構成foo這個數據庫的文件就會由foo.ns,foo.0,foo.1,foo.2等等組成。數據文件每新增一次,大小都會是上一個數據文件的2倍,每個數據文件最大2G。這樣的設計有利於防止數據量較小的數據庫浪費過多的空間,同時又能保證數據量較大的數據庫有相應的空間使用。
MongoDB會使用預分配方式來保證寫入性能的穩定(這種方式可以使用–noprealloc關閉)。預分配在后台進行,並且每個預分配的文件都用0進行填充。這會讓MongoDB始終保持額外的空間和空余的數據文件,從而避免了數據增長過快而帶來的分配磁盤空間引起的阻塞。
數據存儲及索引——更多可以看 http://lib.csdn.net/article/mongodb/53951 recordID本質上是位置信息是『文件id + 文件內offset 』
插入新文檔時,MongoDB 會調用底層KV引擎存儲文檔內容,並生成一個 RecordId 的作為文檔的位置信息標識,通過 RecordId 就能在底層KV引擎讀取到文檔的內容。
如果插入的集合包含索引(MongoDB的集合默認會有_id索引),針對每項索引,還會往底層KV引擎插入一個新的 key-value,key 是索引的字段內容,value 為插入文檔時生成的 RecordId,這樣就能快速根據索引找到文檔的位置信息。
如上圖所示,集合包含{_id: 1}, {name: 1} 2個索引
- 用戶插入文檔時,底層引擎將文檔內容存儲,返回對應的位置信息,即 RecordId1
- 集合包含2個索引
- 插入 {_id: ObjectId1} ==> RecordId1 的索引
- 插入 {name: "rose"} ==> RecordId1 的索引
有了上述的數據,在根據_id訪問時文檔時 (根據其他索引字段類似)
- 根據文檔的 _id 字段從底層KV引擎讀取 RecordId
- 根據 RecordId 從底層KV引擎讀取文檔內容
摘自:https://toutiao.io/posts/9oxdop/preview