MongoDB权威指南(7)- Administration
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进行错误恢复,进行常规备份。