關于存儲引擎 WiredTiger storage engine
WiredTiger 在3.2版本成為mongodb的默認存儲引擎。所以這里講的就是WiredTiger了。
Document Level Concurrency
WiredTiger提供了document-level concurrency control 的寫操作,這么說,多個client可以在同一時間內修改同一collection的不同文檔。對于大多數讀寫操作,WiredTiger都會使用最佳的并發控制,在global、database、collection等級別上只使用了意向鎖(intent lock)。如果存儲引擎檢測到兩個操作沖突了,則導致mongodb將其中一個操作重新執行。
Snapshots and Checkpoints
MongoDB配置WiredTiger在每60秒或者2GB日志數據就創建一個checkpoint(快照)。在寫入一個checkpoint時,前一個checkpoint仍然是有效的,所以在此時MongoDB如果崩潰了,還是可以回退到最新的有效checkpoint,并依靠日志來恢復最近的改變。在新的checkpoint成功變成有效的,之前的舊checkpoint pages就會被釋放。
Journal
MongoDB在沒有新的checkpoint生成之前,會持續地打日志,使用的是snappy compression library來壓縮日志,但是也可以指定其他的壓縮算法,這可通過storage.wiredTiger.engineConfig.journalConpressor
來設置。甚至設置storage.journal.enabled=false
來關閉日志系統,這樣可以減少花費,但是所做的修改也就很危險。
MongoDB配置了WiredTiger在內存指定緩沖區中進行記錄,等到達 128 kb 時再寫到磁盤中去。將內存中的記錄寫入磁盤有下面一些條件:
- 每經過100ms。
- 新的checkpoint出現,或者日志數據到達2GB。
- 如果設置了write concern 的
j:true
選項,存儲引擎立刻寫入log file。
最小的log記錄是128字節,如果等于小于128字節則不使用壓縮。log file的大小規定為100M,所以存儲引擎大約每100M的日志數據就創建一個log file,一般會預先創建一些log file(通常是在首次啟動mongod實例之前,此功能可以關閉)。除非寫操作特別多,否則一般情況下只會有兩三日志文件。正常關閉mongod時會清空所有的日志文件,而非正常關閉則會留下一些文件。
如果在日志信息仍在內存緩沖區的時候mongd突然崩潰(如斷電),這些日志數據就會丟失。最有可能丟失的就是崩潰前的100ms再加上寫進日志文件的這一時間段。serverStatus
命令中有log的統計信息。
如果頻繁寫日志文件會導致效率的下降,這時可以將journal目錄轉移到其他文件系統中,就不會影響到數據庫正常的讀寫操作效率。但這會帶來的一個問題是,對數據庫使用snapshot時不能夠對這兩個部分單獨進行操作,而是需要使用fsyncLock()
將數據庫鎖起來,等snapshot操作都完成之后再使用fsyncUnlock()
解鎖。
Compression
MongoDB提供collection壓縮和index壓縮。默認是使用block compression來壓縮collection,和使用prefix compression來壓縮index。
對于collection,可以更改指定壓縮算法或者禁用壓縮,使用storage.wiredTiger.collectionConfig.blockCompressor
設置即可。
對于index,可以使用storage.wiredTiger.indexConfig.prefixCompression
來關閉prefix壓縮。
甚至,針對每個collection和index的壓縮,可以使用具體的壓縮算法,參考create-collection-storage-engine-options
和db.collection.createIndex()
。不過,默認的壓縮算法在大多數平均情況下都有出色的表現,很好平衡了效率和執行需求。
Cache
MongoDB使用了雙Cache,一個是WiredTiger Cache,一個是文件系統Cache。默認情況下,從3.2起WiredTiger將使用60%的RAM減去1GB或者使用1GB,取其中的大者。在3.0中要么使用1GB,要么50%的物理內存,取其中的大者。可以參考storage.wiredTiger.engineConfig.cacheSizeGB
和 --wiredTigerCacheSizeGB進行配置Cache大小,但是要避免超過默認的大小,值得注意的是,storage.wiredTiger.engineConfig.cacheSizeGB
僅僅限制了WiredTiger Cache的大小,這只是mongod的一部分,而不是mongod所使用內存的上限。MongoDB假設了這只有一個instance在一個node上而已,如果有多個的話,更加需要調整一下Cache大小。
本引擎會自主決定哪些數據保存在內存中,哪些數據會刷新到磁盤中。而且,兩種引擎不能混淆使用,即用Wired Tiger創建的數據庫不能用MMAPv1去打開,這是兩種不同的文件存儲方式以及管理方式的。
關于MMAPv1 storage engine
每個文檔中的所有field都位于同一塊連續的內存中,而storage engine在為文檔申請內存塊的時候都是會留出一部分內存供后來填充用的,即為每個文檔申請的實際內存大小都會大于其真實大小(一般是以2的指數增長)。當填充的多余部分內存用光了之后,就會引起重新為該文檔申請新內存的操作。
Document Level Lock
本引擎也提供了鎖的機制,對于document級別的寫操作保證了不會沖突,而是進行有序的執行。
MongoDB 系統的限制與門檻
- BSON文檔
最大BSON文檔的大小是16MB,如果超過這個限制,可以嘗試使用GridFS API來存儲大文件。 - 嵌套深度
MongoDB目前支持的內嵌文檔的嵌套深度是不超過100層。 - Namespace 長度
支持的最大的集合namespace為120 Bytes,其中包括了數據庫名稱,還有dot等等,即<database>.<collection>
。 - Namespace 的數量
在3.0之前使用的默認存儲引擎就會有一些限制,比如Namespace的數量受限制于namespace文件的大小,現在的默認存儲引擎WiredTiget就沒有這些限制了。 - Namespace 文件的大小
之前MMAPv1默認的namespace文件的大小限制為16MB,但是可以通過nsSize來配置它,只要不超過2047字節。但WiredTiger現在沒有這個限制。 - 固定集合Capped Collection。如果是通過max參數來創建的固定集合,那么集合大小不能超過232。而在創建固定集合的時候沒有指定集合大小,那么集合中可以包含任意多個文檔。
- MMAPv1存儲引擎限制了每個數據庫不能夠超過16000個數據文件,即不超過32TB,可通過
storage.mmapv1.smallFiles
來設置為8TB。 - MMAPv1不能夠處理數據大小超過操作系統的虛存空間大小。而WiredTiger沒有此限制。
- MMAPv1限制了數據庫中的文檔數量,與索引數量和namespace文件大小相關。WiredTiger沒有此限制。
- 針對具體情況,可以使用no padding模式的申請內存方式,這樣每次申請的內存大小就剛剛好了。
GridFS 大文檔存儲
如果存儲的文檔超過了16MB,那么就需要選擇這種方式來存儲了。將大文檔分割成小部分或chunk,然后分別作為小文檔存儲起來,分別存儲于兩個集合collection中,其中一個集合存儲的是文件chunk,另一個存儲的是元數據(這里有詳細介紹 GridFS Collections)。默認的chunk大小為255kB,即除了最后一塊,其他都是255kB(自從v 2.4.10改為255kB的)。
在需要查詢大文件的時候,引擎會重組所有的chunk。可以針對文件中的某個范圍進行查詢,比如跳過視頻的片頭。這種存儲方式也支持16MB以下的文檔存儲的,當經常需要查詢文件中的某個范圍的時候就能派上用場了。
一般情況下,GridFS使用兩個分別名為fs.files
和fs.chunks
的文件來保存所有的大文件信息。一對文件稱之為“桶”,可以創建多對,也可以選擇其他名稱。chunks中的每個文檔都是一個chunk,就像下面這樣:
{
"_id" : <ObjectId>,
"files_id" : <ObjectId>,
"n" : <num>, //sequence of this chunk
"data" : <binary>
}
files中的每個文檔代表著GridFS中的一個大文件。除了部分是可選的,文檔格式大致如下:
{
"_id" : <ObjectId>,
"length" : <num>,
"chunkSize" : <num>,
"uploadDate" : <timestamp>,
"md5" : <hash>,
"filename" : <string>,
"contentType" : <string>,
"aliases" : <string array>,
"metadata" : <dataObject>
}
那效率如何呢?GridFS對每個chunks和files集合使用了index,驅動一般會自動創建這些索引,提升速度就只能依靠這些索引了,也可以自定義一些新索引。