当向数据库写入时,mongodb默认不等待响应消息。使用getLastError命令来确保操作已经正确执行。
在很多驱动中当你使用"安全"模式(一些驱动中称为“set write concern”)进行保存和更新时,getLastError命令会自动调用。但是当这个特别的被称为getlasterror的命令被调用时,实际上又发生什么了呢。这里,我们介绍它怎么工作。
在shell中运行
getlasterror命令检查本连接的上一次数据库操作的错误。由于它是一个命令,这里有一些方法可以使用它:
> db.$cmd.findOne({getlasterror:1})
或者在shell中:
> db.runCommand("getlasterror")
或者使用shell助手:
> db.getLastError()
在mongo shell,db.getLastError()返回上一次错误-如果没有错误则为空。使用db.getLastErrorObj()查看完整的错误结果。
在没有错误的情况下,db.getLastErrorObj().err应该为空。
什么时候用
getlasterror主要对写操作有用(虽然它也在一个命令或者查询后面设置了)。写操作默认不会有返结果:这样就节约了客户端在写操作时等待客户端/服务器之间往返时间。如果有人需要一个返回结果,那么他可以始终调用getLastError.
如果你在多个连接上面向mongodb写数据,那么有时候在一个连接上调用getlasterror来确认数据已经提交到了数据库是很重要的。举个例子,如果你向连接#1写入数据并希望这些写入可以反映到连接#2上面的读操作,你可以在向连接#1写入后调用getlasterror来确保这一点。
为了最大化的速度,对于不重要的写入跳过使用getLastError(或者"safe mode")。在需要向用户确认写操作成功时使用它。
选项
fsync
当没有使用日志功能运行mongod(--nojournal)时,同步的选项强制数据库在同步完所有文件后才返回。
当使用日志功能运行mongod,同步选项等待下一次组提交后才返回。
> db.runCommand({getlasterror:1,fsync:true})
{ "err" : null, "n" : 0, "fsyncFiles" : 2, "ok" : 1 }
j
v2.0+:当设置j:true时,getlasterror调用等待日志提交后才返回。如果服务器没有启用日志功能,它立即返回,并且成功。
> db.runCommand({getlasterror:1,j:true})
w
客户端可以阻塞直到一个写操作被复制到N个服务器。
wtimeout可以联合w一起使用。默认是没有超时(永久等待)。
> db.getLastError(2, 5000) // w=2, timeout 5000ms
注意上面的这些选项可以联合使用:等待日志确认和写操作到达复制组中多数节点的确认是有意义的。
标志和多数
mongodb2.0有能力控制那些节点被写入。代替设置写操作必须被公认的节点个数的方法,你可以在配置中设置基于标志的规则。
除了标志规则外,你还可以设置一个字符串“majority”:
>db.getLastError("majority") // waits for more than 50% of the configured nodes to acknowledge the
write (until replication is applied to the point of that write)
返回值
这个命令的返回值是一个多字段的对象。常用的字段已经列在下面;这里可能还有其他字段。
对于更新:
使用了w:<n>/<tag>
在驱动中使用getLastError
驱动支持命令格式使用getLastError并且很多还为操作提供了"safe"模式。查阅各驱动的文档获取更多关于"safe"模式的信息。
mongo shell REPL 行为
数据库shell在每一次read/eval/print loop命令计算前会执行resetError() - 并且自动打印错误,如果每一次计算发生了一个错误。所以,在一个错误之后,在shell提示符调用db.getLastError()会返回空。尽管如此,如果在返回到提示符前调用,结果会是你期望的:
> try { db.foo.findOne() } catch(e) { print("preverr:" + tojson(db.getPrevError())); print("lasterr:"
+ tojson(db.getLastError()));}
preverr:{"err" : "unauthorized" , "nPrev" : 1 , "ok" : 1}
lasterr:"unauthorized"
getPrevError命令
当执行大量写操作时,resetError()和getPrevError()会是一个检查操作是否成功的高效的方法。
例如我们要插入1000个对象到一个集成,通过网络检查1000次得返回值会很慢。相反,你可以像这样做些事情:
db.resetError();
for( loop 1000 times... )
db.foo.save(something...);
if( db.getPrevError().err )
print("didn't work!");
创建索引
ensureIndex()是创建索引的助手函数。它的执行会创建一个索引通过将索引信息加入到集合system.indexes中。
> use test
> db.myCollection.ensureIndex(<keypattern>);
> // same as:
> db.system.indexes.insert({ name: "name", ns: "namespaceToIndex",
key: <keypattern> });
注意:一旦你插入了索引,所有后续插入的文档都会被索引,就像集合中已经存在的文档一样。如果你有一个很大的集合,这会占用大量时间并且会阻塞其他操作。
你可以查询数据库test的集合foo中的system.indexes来查看所有索引:
>db.system.indexes.find( { ns: "test.foo" } );
在一些驱动中,ensureIndex()记得它近期是否被调用过,并放弃在这种情况下的索引插入操作。即使不是这种情况,ensureIndex()是一个轻负担操作,它可以被经常调用以确保这个索引存在。
丢弃一个索引
从shell中:
db.mycollection.dropIndex(<name_or_pattern>)
db.mycollection.dropIndexes()
// example:
t.dropIndex( { name : 1 } );
从一个驱动中(原始命令对象格式;很多驱动有助手函数):
{ deleteIndexes: <collection_name>, index: <index_name> }
// "*" for <index_name> will drop all indexes except _id
重索引
重索引命令会对一个集合中的所有索引进行重建:
db.myCollection.reIndex()
// same as:
db.runCommand( { reIndex : 'myCollection' } )
通常这是不需要的。你可能希望去重索引,当你的集合发生了显著的变化或者索引占用的磁盘空间出奇的大的时候。
自1.8版本开始索引在更新时会自动被压缩。
重索引是一个阻塞操作(索引在前天被重建)并且对于大的集合会很慢。
修复数据库命令会重建数据库中所有的索引。
索引命名空间
每一个索引拥有自己的命名空间用于btree存储段。这个命名空间是:
<collectionnamespace>.$<indexname>
这是一个内部的不能被直接查询的命名空间。