把 MongoDB 當成是純記憶體資料庫來使用(Redis 風格)
基本思想
將MongoDB用作記憶體資料庫(in-memory database),也即,根本就不讓MongoDB把資料儲存到磁碟中的這種用法,引起了越來越多的人的興趣。這種用法對於以下應用場合來講,超實用: 置於慢速RDBMS系統之前的寫操作密集型快取 嵌入式系統 無需持久化資料的PCI相容系統 需要輕量級資料庫而且庫中資料可以很容易清除掉的單元測試(unit testing)
如果這一切可以實現就真是太優雅了:我們就能夠巧妙地在不涉及磁碟操作的情況下利用MongoDB的查詢/檢索功能。可能你也知道,在99%的情況下,磁碟IO(特別是隨機IO)是系統的瓶頸,而且,如果你要寫入資料的話,磁碟操作是無法避免的。
MongoDB有一個非常酷的設計決策,就是她可以使用記憶體影射檔案(memory-mapped file)來處理對磁碟檔案中資料的讀寫請求。這也就是說,MongoDB並不對RAM和磁碟這兩者進行區別對待,只是將檔案看作一個巨大的數組,然後按照位元組為單位訪問其中的資料,剩下的都交由作業系統(OS)去處理。就是這個設計決策,才使得MongoDB可以無需任何修改就能夠運行於RAM之中。 實現方法
這一切都是通過使用一種叫做tmpfs的特殊類型檔案系統實現的。在Linux中它看上去同常規的檔案系統(FS)一樣,只是它完全位於RAM中(除非其大小超過了RAM的大小,此時它還可以進行swap,這個非常有用。)。我的伺服器中有32GB的RAM,下面讓我們建立一個16GB的 tmpfs:
# mkdir /ramdata# mount -t tmpfs -o size=16000M tmpfs /ramdata/# dfFilesystem 1K-blocks Used Available Use% Mounted on/dev/xvde1 5905712 4973924 871792 86% /none 15344936 0 15344936 0% /dev/shmtmpfs 16384000 0 16384000 0% /ramdata
接下來要用適當的設定啟動MongoDB。為了減小浪費的RAM數量,應該把smallfiles和noprealloc設定為true。既然現在是基於RAM的,這麼做完全不會降低效能。此時再使用journal就毫無意義了,所以應該把nojournal設定為true。
dbpath=/ramdatanojournal = truesmallFiles = truenoprealloc = true
MongoDB啟動之後,你會發現她運行得非常好,檔案系統中的檔案也正如期待的那樣出現了:
# mongoMongoDB shell version: 2.3.2connecting to: test> db.test.insert({a:1})> db.test.find(){ "_id" : ObjectId("51802115eafa5d80b5d2c145"), "a" : 1 }# ls -l /ramdata/total 65684-rw-------. 1 root root 16777216 Apr 30 15:52 local.0-rw-------. 1 root root 16777216 Apr 30 15:52 local.ns-rwxr-xr-x. 1 root root 5 Apr 30 15:52 mongod.lock-rw-------. 1 root root 16777216 Apr 30 15:52 test.0-rw-------. 1 root root 16777216 Apr 30 15:52 test.nsdrwxr-xr-x. 2 root root 40 Apr 30 15:52 _tmp
現在讓我們添加一些資料,證實一下其運行完全正常。我們先建立一個1KB的document,然後將它添加到MongoDB中4百萬次:
> str = ""> aaa = "aaaaaaaaaa"aaaaaaaaaa> for (var i = 0; i < 100; ++i) { str += aaa; }> for (var i = 0; i < 4000000; ++i) { db.foo.insert({a: Math.random(), s: str});}> db.foo.stats(){ "ns" : "test.foo", "count" : 4000000, "size" : 4544000160, "avgObjSize" : 1136.00004, "storageSize" : 5030768544, "numExtents" : 26, "nindexes" : 1, "lastExtentSize" : 536600560, "paddingFactor" : 1, "systemFlags" : 1, "userFlags" : 0, "totalIndexSize" : 129794000, "indexSizes" : { "_id_" : 129794000 }, "ok" : 1}
可以看出,其中的document平均大小為1136位元組,資料總共佔用了5GB的空間。_id之上的索引大小為130MB。現在我們需要驗證一件
非常重要的事情:RAM中的資料有沒有重複,是不是在MongoDB和檔案系統中各儲存了一份。還記得MongoDB並不會在她自己的進程內緩衝任何資料,她的資料只會緩衝到檔案系統的緩衝之中。那我們來清除一下檔案系統的緩衝,然後看看RAM中還有有什麼資料:
# echo 3 > /proc/sys/vm/drop_caches # free total used free shared buffers cachedMem: 30689876 6292780 24397096 0 1044 5817368-/+ buffers/cache: 474368 30215508Swap: 0 0 0
可以看到,在已使用的6.3GB的RAM中,有5.8GB用於了檔案系統的緩衝(緩衝區,buffer)。為什麼即使在清除所有緩衝之後,系統中仍然還有5.8GB的檔案系統快取。。其原因是,Linux非常聰明,她不會在tmpfs和緩衝中儲存重複的資料。太棒了。這就意味著,你在RAM只有一份資料。下面我們訪問一下所有的document,並驗證一下,RAM的使用方式不會發生變化:
> db.foo.find().itcount()4000000# free total used free shared buffers cachedMem: 30689876 6327988 24361888 0 1324 5818012-/+ buffers/cache: 508652 30181224Swap: 0 0 0# ls -l /ramdata/total 5808780-rw-------. 1 root root 16777216 Apr 30 15:52 local.0-rw-------. 1 root root 16777216 Apr 30 15:52 local.ns-rwxr-xr-x. 1 root root 5 Apr 30 15:52 mongod.lock-rw-------. 1 root root 16777216 Apr 30 16:00 test.0-rw-------. 1 root root 33554432 Apr 30 16:00 test.1-rw-------. 1 root root 536608768 Apr 30 16:02 test.10-rw-------. 1 root root 536608768 Apr 30 16:03 test.11-rw-------. 1 root root 536608768 Apr 30 16:03 test.12-rw-------. 1 root root 536608768 Apr 30 16:04 test.13-rw-------. 1 root root 536608768 Apr 30 16:04 test.14-rw-------. 1 root root 67108864 Apr 30 16:00 test.2-rw-------. 1 root root 134217728 Apr 30 16:00 test.3-rw-------. 1 root root 268435456 Apr 30 16:00 test.4-rw-------. 1 root root 536608768 Apr 30 16:01 test.5-rw-------. 1 root root 536608768 Apr 30 16:01 test.6-rw-------. 1 root root 536608768 Apr 30 16:04 test.7-rw-------. 1 root root 536608768 Apr 30 16:03 test.8-rw-------. 1 root root 536608768 Apr 30 16:02 test.9-rw-------. 1 root root 16777216 Apr 30 15:52 test.nsdrwxr-xr-x. 2 root root 40 Apr 30 16:04 _tmp# dfFilesystem 1K-blocks Used Available Use% Mounted on/dev/xvde1 5905712 4973960 871756 86% /none 15344936 0 15344936 0% /dev/shmtmpfs 16384000 5808780 10575220 36% /ramdata
果不其然。 :) 複製(replication)呢。
既然伺服器在重啟時RAM中的資料都會丟失,所以你可能會想使用複製。採用標準的複本集(replica set)就能夠獲得自動容錯移轉(failover),還能夠提高資料讀取能力(read capacity)。如果有伺服器重啟了,它就可以從同一個複本集中另外一個伺服器中讀取資料從而重建自己的資料(重新同步,resync)。即使在大量資料和索引的情況下,這個過程也會足夠快,因為索引操作都是在RAM中進行的 :)
有一點很重要,就是寫操作會寫入一個特殊的叫做oplog的collection,它位於local資料庫之中。預設情況下,它的大小是總資料量的5%。在我這種情況下,oplog會佔有16GB的5%,也就是800MB的空間。在拿不準的情況下,比較安全的做法是,可以使用oplogSize這個選項為oplog選擇一個固定的大小。如果備選伺服器宕機時間超過了oplog的容量,它就必須要進行重新同步了。要把它的大小設定為1GB,可以這樣:
oplogSize = 1000
分區(sharding)呢。
既然擁有了MongoDB所有的查詢功能,那麼用它來實現一個大型的服務要怎麼弄。你可以隨心所欲地使用分區來實現一個大型可擴充的記憶體資料庫。設定管理員(儲存著資料區塊分配情況)還還是用過採用基於磁碟的方案,因為這些伺服器的活動數量不大,老從頭重建叢集可不好玩。 注意事項
RAM屬稀缺資源,而且在這種情況下你一定想讓整個資料集都能放到RAM中。儘管tmpfs具有藉助於磁碟交換(swapping)的能力,但其效能下降將非常顯著。為了充分利用RAM,你應該考慮: 使用usePowerOf2Sizes選項對儲存bucket進行正常化 定期運行compact命令或者對節點進行重新同步(resync) schema的設計要相當正常化(以避免出現大量比較大的document) 結論
寶貝,你現在就能夠將MongoDB用作記憶體資料庫了,而且還能使用她的所有功能。效能嘛,應該會相當驚人:我在單線程/核的情況下進行測試,可以達到每秒20K個寫入的速度,而且增加多少個核就會再增加多少倍的寫入速度。
本文地址:http://www.oschina.net/translate/how-to-use-mongodb-as-a-pure-in-memory-db-redis-style
原文地址:http://edgystuff.tumblr.com/post/49304254688/how-to-use-mongodb-as-a-pure-in-memory-db-redis-style