MongoDB權威指南(7)- 管理
1.啟動和停止mongoDB
從命令行啟動mongoDB服務器使用可執行文件mongod,mongod有很多的啟動選項,運行mongod --help可以查看這些選項,下邊是常用的重要選項:
--dbpath
指定數據目錄,缺省為/data/db/。每個mongod進程都需要自己的數據目錄,如果你要運行3個mongod的實例,那么就需要3個獨自的目錄。mongod啟動的時候會在數據目錄創建一個mongod.lock文件,阻止其他進程使用此目錄。
--port
指定服務器偵聽的端口號。mongod缺省使用27017端口,如果你要運行多個實例,需要給每個進程指定不同的端口。
--fork
創建服務器子進程,以守護進程的方式運行服務。
--logpath
將輸出寫入指定文件而不是控制臺。如果文件不存在,將會創建此文件,如果文件已存在,文件將會被覆蓋,舊的內容會被清除掉。如果想保留舊的內容,另外需要使用
--logappend選項
--config
從配置文件讀取啟動選項,下邊是一個配置文件的示例,#之后的為注釋
# Start MongoDB as a daemon on port 5586 port = 5586 fork = true # daemonize it! logpath = mongodb.log
停止服務器最基本的方式是向進程發送一個SIGINT或SIGTERM信號。如果服務是作為前臺進程運行的,那么可以按Ctrl-C,否則可以通過命令如kill來發送信號。
另外一個辦法是使用shutdown命令,這個命令必須在admin數據庫上運行。
> use admin switched to db admin> db.shutdownServer(); server should be down...
2.監控
缺省條件下,啟動mongod同時也會啟動一個Http服務器,偵聽比數據庫服務偵聽端口大1000的端口,Http服務器提供一個接口,用戶可以查看 mongoDB服務器的一些基本信息。所有的這些信息都可以通過shell來取得,Http接口提供了更直觀,更容易看的方式。在瀏覽器里打開 http://localhost:28017即可查看。
serverStatus命令是獲取運行中mongoDB服務器的統計數據的基本工具。其輸出如下:
> db.runCommand({"serverStatus" : 1}) { "version" : "1.5.3", "uptime" : 166, "localTime" : "Thu Jun 10 2010 15:47:40 GMT-0400 (EDT)", "globalLock" : { "totalTime" : 165984675, "lockTime" : 91471425, "ratio" : 0.551083556358441 }, "mem" : { "bits" : 64, "resident" : 101, "virtual" : 2824, "supported" : true, "mapped" : 336 }, "connections" : { "current" : 141, "available" : 19859 }, "extra_info" : { "note" : "fields vary by platform" }, "indexCounters" : { "btree" : { "accesses" : 1563, "hits" : 1563, "misses" : 0, "resets" : 0, "missRatio" : 0 } }, "backgroundFlushing" : { "flushes" : 2, "total_ms" : 44, "average_ms" : 22, "last_ms" : 36, "last_finished" : "Thu Jun 10 2010 15:46:54 GMT-0400 (EDT)" }, "opcounters" : { "insert" : 38195, "query" : 8874, "update" : 4058, "delete" : 389, "getmore" : 888, "command" : 17731 }, "asserts" : { "regular" : 0, "warning" : 0, "msg" : 0, "user" : 5054, "rollovers" : 0 }, "ok" : true }
globalLock:顯示了全局寫入鎖持續的時間,單位是微秒。
mem:顯示了有多少數據進行了內存映射,服務器進程駐留內存和虛擬內存的大小,單位mb。
indexCounters:給出了需要到磁盤進行B-Tree查找的次數(miss),以及在內存中進行查找的次數(hit),如果miss/hit開始升高的話,你就需要考慮增加內存了。
background Flushing:顯示了運行了多少后臺fsync,花費了多長時間。ps:fsync是數據庫備份的一種,稍后會提到
opcounters:計算主要操作類型的操作次數
asserts:服務器上斷言發生的次數
所有的計數器都是在服務開始運行的時候就開始跟蹤,如果某個計數器的值太大了就會發生roll over,rollovers的計數就會增加。
相對serverStatus命令,mongostat命令的輸出更加友好一些,mongostate打印來自serverStatus的重要信息,每秒打印一行新記錄,看起來更加實時一些。
很多管理員已經在使用第三方的插件來跟蹤服務器,serverStatus命令使得寫個這類工具非常容易,這些插件包括Nagios, Munin, Ganglia, Cacti等等。
3.安全和認證
mongoDB處理安全的最好方式是在受信任的環境中運行服務器,并且保證連接到服務器的機器是受信任的。也就是說,mongoDB支持的是按連接的認證,這是一種粗粒度的權限模式。mongoDB實例中的每個數據庫都可以有任意數量的自己的用戶,在啟用安全功能之后,只有認證用戶能夠對數據庫執行讀寫操作。admin數據庫是個特例,admin數據庫里邊的用戶被視為超級用戶,認證之后,admin的用戶可以讀寫所有的數據庫并且執行一些僅限于管理員的命令,如listDatabases,shutdown。
在啟用安全功能之前,admin里至少要添加一個用戶。看下下邊的例子,是從shell啟動連接到服務器,并且沒有啟用安全功能。
> use admin switched to db admin> db.addUser("root", "abcd"); { "user" : "root", "readOnly" : false, "pwd" : "1a0f1c3c3aa1d592f490a2addc559383" }> use test switched to db test> db.addUser("test_user", "efgh"); { "user" : "test_user", "readOnly" : false, "pwd" : "6076b96fc3fe6002c810268702646eec" }> db.addUser("read_only", "ijkl", true); { "user" : "read_only", "readOnly" : true, "pwd" : "f497e180c9dc0655292fee5893c162f1" }
我們添加了一個管理員用戶root,還有兩個test數據庫的用戶,其中的read_only只有讀權限。調用addUser函數,你需要對數據庫的寫權限,我們這里能夠對所有數據庫執行此函數是因為我們沒有啟用安全功能。addUser函數不止能用來添加新用戶,還能修改用戶密碼和只讀狀態,只要按用戶名重新調用一次次函數即可。
現在就可以重啟服務器,加上--auth來啟用安全功能,然后通過shell重新連接到服務器
> use test switched to db test> db.test.find(); error: { "$err" : "unauthorized for db [test] lock type: -1 " }> db.auth("read_only", "ijkl");1 > db.test.find(); { "_id" : ObjectId("4bb007f53e8424663ea6848a"), "x" : 1 }> db.test.insert({"x" : 2}); unauthorized> db.auth("test_user", "efgh");1 > db.test.insert({"x": 2});> db.test.find(); { "_id" : ObjectId("4bb007f53e8424663ea6848a"), "x" : 1 } { "_id" : ObjectId("4bb0088cbe17157d7b9cac07"), "x" : 2 }> show dbs assert: assert failed : listDatabases failed:{ "assertion" : "unauthorized for db [admin] lock type: 1","errmsg" : "db assertion failure","ok" : 0 }> use admin switched to db admin> db.auth("root", "abcd");1 > show dbs admin local test
當我們連接之后還不能進行任何操作,認證為read_only之后就可以進行讀操作了,認證為test_user之后就可以進行寫操作了,認證為root就可以進行管理員的操作了。
給定數據庫的用戶都存儲在數據庫里邊的system.users里,用戶數據結構為{"user" : username, "readOnly" : true, "pwd" :password hash}, password hash是一個基于username和password的hash值。知道了這個就可以方便地進行一些操作了,如刪除用戶,只需從 system.users里將其刪除即可。
4.備份和修復
mongoDB將所有的數據存儲在數據目錄,缺省為/data/db,那么我們就可以將所有數據目錄下的文件拷貝出來來創建備份。對于運行中的 mongoDB來說,拷貝文件的方式創建備份并不安全,這樣的備份有可能有損壞,需要修復,除非服務器完成了一個完整的fsync并且不允許寫。關閉服務器然后拷貝數據目錄是一種安全有效的方式,但是并不理想。我們下邊看幾種技術,可以在不關閉服務器的情況下進行備份。
mongodump和mongorestore
mongodump是mongoDB分發包里的一個工具,它是這樣工作的,它對運行中的mongoDB服務器進行查詢,然后將所有的數據寫入到磁盤。由于 mongodump只是一個普通的客戶端,所以它可以對活動狀態的服務器進行備份,即使服務其器在處理其他請求或者在執行寫操作。
Note:由于mongodump執行的是普通的查詢,所以這個備份不一定就是服務器數據在某個時間點上的快照。另外就是,備份過程中,其他客戶都的性能可能會受到影響。
分發包里還包括了一個對應的工具,mongorestore,用來從備份恢復數據。
fsync和Lock
mongodump和mongorestore使我們能在不關閉服務器的情況下進行備份,但是不能獲取某個時間點的數據視圖。
fsync命令會強制mongoDB服務器清空所有的掛起的寫操作,然后,可以指定是否對數據庫加鎖以阻止其他的寫入,直到數據庫解鎖。
> use admin switched to db admin> db.runCommand({"fsync" : 1, "lock" : 1}); {"info" : "now locked against writes, use db.$cmd.sys.unlock.findOne() to unlock","ok" : 1 }
此刻,數據目錄就代表了我們的數據在這個時間點上的一個數據一致的快照。由于現在服務器鎖定了寫操作,我們就可以安全的進行數據備份了。備份完成后,我們需要執行解鎖
> db.$cmd.sys.unlock.findOne(); { "ok" : 1, "info" : "unlock requested" }> db.currentOp(); { "inprog" : [ ] }
我們運行了currentOp命令來確保鎖被釋放了。
slave備份
相比上邊的備份方式,最靈活的還是從slave服務器上備份。主從復制(master-slave replication)是mongoDB里最常用的復制模式,數據會自動從master服務器復制到slave服務器。slave服務器一直保持一份幾乎和master數據庫同步的數據。因為我們不需要依賴slave服務器是否可用或者它的性能,所以上邊提到的3種備份方式我們可以隨便用。
修復
我們備份數據,以備發生災難事件后可以恢復數據,但是不幸情況總是存在的,沒有數據備份。當遇到斷電或者軟件崩潰,機器重啟之后,我們不能保證數據沒有問題,好在mongoDB內建了修復功能,嘗試修復受損的文件。如果mongoDB不正常關閉,啟動服務器備份的時候就會看到一個警告消息
**************
old lock file: /data/db/mongod.lock. probably means unclean shutdown
recommend removing file and running --repair
see: http://dochub.mongodb.org/core/repair for more information
*************
修復所有數據最簡單的辦法就是啟動時加上--repaire。修復的過程很容易理解,首先,數據庫里所有的document除了那些無效數據都會被導出,然后立刻導入,這個過程結束后再重建所有的索引。理解了這個機制,就可以解釋修復的某些特性。大數據集的修復可能會花很長時間,因為所有的數據都是驗證過的而且所有的索引都會重建。修復同時還可能是數據庫里的document減少,因為那些損壞的document被拋棄了。修復的過程同時會執行壓縮,額外的空閑空間會被回收。
修復單個的數據庫可以在shell里運行repaireDatabase方法
> use test switched to db test> db.repairDatabase() { "ok" : 1 }
修復是清楚損壞的最后手段,最有效的方式仍然是安全地停止服務器,使用replication進行錯誤恢復,進行常規備份。