MongoDB入门
《MongoDB权威指南》的读书笔记。
Table of Contents
1 开始使用MongoDB
1.1 启动MongoDB
Windows平台下在Cygwin中启动MongoDB,要注意的是:
- 需要手动创建/data/db文件夹 mkdir -p /data/db
- 要为/data/db设置正确的权限 chown -R user:group /data
现在就可以启动MongoDB了。我是将data文件夹建在MongoDB文件夹里了,所以可以使用dbpath参数指定目录。
./mongod --dbpath ../data/db &
MongoDB的Shell是一个功能完备的JavaScript解释器,可以运行任何JavaScript程序。Shell会在启动时自动连接到本地的MongoDB服务器。
./mongo
1.2 核心概念
1.2.1 文档
文档是MongoDB的核心概念,由多个键及其关联的值有序地放在一起便是文档。对应关系型数据库中行的概念。集合就是一组文档,就如同数据库表。但集合是无模式的,这意味着一个集合里面的文档可以是各式各样的。
1.2.2 集合
在关系型数据库中,如果要保存people其address,一般会拆分成两个表中的两行。在MongoDB中,就可以将address文档直接嵌入people文档中。这样做也有坏处,因为MongoDB会储存更多重复的数据,这样是反规范化的。
1.2.3 ObjectId
1.3 数据类型
MongoDB的文档类似于JSON。但JSON仅有6中数据类型,表现力有限。MongoDB在保留JSON基本的键/值对的基础上,添加了一些其他数据类型。
2 插入、更新、删除及查询文档
2.1 插入
当执行插入时,使用的驱动程序会将数据转换成BSON的形式。数据库解析BSON,检验是否包含“_id”并且文档不超过4MB,除此之外不做别的数据验证,就只是简单地将文档原样存入数据库中。坏处是允许插入无效的数据,好处是让数据库更加安全,远离注入式攻击。
db.blog.insert({"title" : "Hello MongoDB", "author" : "cdai"}) db.blog.find() { "_id" : ObjectId("514580655da8aea55cb4b7a8"), "title" : "Hello MongoDB", "author" : "cdai" }
2.2 更新
2.2.1 文档整体替换
当模式结构发生较大变化,完全用一个新文档替代匹配的文档。
db.blog.update({"author" : "cdai"}, {"username" : "cdai", "comments" : ["good"]}) db.blog.find() { "_id" : ObjectId("514580655da8aea55cb4b7a8"), "username" : "cdai", "comments" : [ "good" ] }
2.2.2 $set修改器
如果键不存在就创建它,这对更新模式,增加用户定义的键来说非常方便。
db.blog.update({"username" : "cdai"}, {"$set" : {"pageviews" : 52}}) db.blog.find() { "_id" : ObjectId("514580655da8aea55cb4b7a8"), "comments" : [ "good" ], "pageviews" : 52, "username" : "cdai" }
2.2.3 $inc修改器
$inc只能用于整数、长整数或双精度浮点数。
db.blog.update({"username" : "cdai"}, {"$inc" : {"pageviews" : 1}}) db.blog.find() { "_id" : ObjectId("514580655da8aea55cb4b7a8"), "comments" : [ "good" ], "pageviews" : 53, "username" : "cdai" }
2.2.4 $push修改器
$push会向已有的数组末尾加入一个元素,要是不存在会创建一个新数组。
db.blog.update({"username" : "cdai"}, {"$push" : {"comments" : "not bad"}}) db.blog.find() { "_id" : ObjectId("514580655da8aea55cb4b7a8"), "comments" : [ "good", "not bad" ], "pageviews" : 53, "username" : "cdai" }
2.2.5 修改器速度
$inc能就地修改,因为不需要改变文档的大小。而数组修改器可能更改了文档的大小,就会慢一些。MongoDB预留了些补白给文档,来适应大小变化。但是要是超出了原来的空间,最后还是要分配一块新的空间。
2.3 删除
删除集合里所有数据。不会删除集合本身,原有的索引也会保留。
db.blog.remove()
删除集合。速度很快,整个集合都被删除,所有的索引也都不见了。
db.drop_collection("blog")
2.4 查询
2.4.1 指定返回的键
find或findOne函数的第二个参数指定想要/剔除的键。
db.blog.find({"username" : "cdai"}, {"pageviews" : 1}) { "_id" : ObjectId("514580655da8aea55cb4b7a8"), "pageviews" : 53 } db.blog.find({"username" : "cdai"}, {"pageviews" : 0}) { "_id" : ObjectId("514580655da8aea55cb4b7a8"), "comments" : [ "good", "not bad" ], "username" : "cdai" }
2.4.2 查询范围
$lt、$lte、$gt、$gte就是全部的比较运算符。
db.blog.findOne({"pageviews" : {"$gte" : 1, "$lte" : 100}}) { "_id" : ObjectId("514580655da8aea55cb4b7a8"), "comments" : [ "good", "not bad" ], "pageviews" : 53, "username" : "cdai" }
类似的运算符还有$ne、$in、$not、$or等,用法类似,就不一一介绍了。
2.4.3 正则表达式
MongoDB使用Perl兼容的PCRE库来匹配正则表达式。
db.blog.find({"username" : /dai/i}) { "_id" : ObjectId("514580655da8aea55cb4b7a8"), "comments" : [ "good", "not bad" ], "pageviews" : 53, "username" : "cdai" }
2.4.4 查询数组
2.4.5 查询内嵌文档
3 创建索引
MongoDB的索引几乎与传统的关系型数据库索引一模一样。
db.blog.ensureIndex({"username" : 1}) Sun Mar 17 15:41:46 [conn4] build index test.blog { username: 1.0 } Sun Mar 17 15:41:46 [conn4] build index done. scanned 1 total records. 0.036 secs
如果索引包含N个键,则对于前几个键的查询都会有帮助。比如有个索引{"a" : 1, "b" : 1, "c" : 1},实际上是有了{"a" : 1},{"a" : 1, "b" : 1}和{"a" : 1, "b" : 1, "c" : 1}三个索引。
有些时候最有效的方法居然是不使用索引。一般来说,要是查询返回集合中一半以上的结果,用表扫描会比几乎每条文档都要查索引要高效一些。
所以,建立索引时要考虑如下问题。 (1)会做什么样的查询?其中哪些键需要索引? (2)每个键的索引方向是怎样的? (3)如何应对扩展?有没有种不同的键的排列可以使常用数据更多地保留在内存中?
4 管理MongoDB
4.1 监控页面
通过–rest选项开启REST支持,可以更好地使用监控页面。
./mongod --dbpath ../data/db/ --rest &
4.2 停止MongoDB
使用kill或kill -2停止MongoDB,会等到当前运行的操作或者文件预分配完成,关闭所有打开的链接,将缓存的数据刷新到磁盘,最后停止。千万不要向运行中的MongoDB发送SIGKILL(kill -9)信号。这会导致上面的步骤全被忽略,数据库直接关闭。
use admin switched to db admin db.shutdownServer()
4.3 备份和修复
4.3.1 停机备份
在运行MongoDB时复制数据目录不太安全,所以就得先把服务器关了,再复制数据目录。假设服务器安全关闭了,数据库目录中就是关闭那一刻数据的快照。
4.3.2 mongodump
mongodump使用普通的查询机制,所以产生的备份不一定是服务器数据的实时快照。服务器在备份过程中处理写入时尤为明显。
4.3.3 fsync
fsync命令会强制服务器将所有缓冲区写入磁盘。还可以选择上锁对数据库的进一步写入,直到释放锁为止。要是数据库运行在有快照功能的文件系统上,这个会很有用。
4.3.4 从属备份
因为不太在乎从属服务器的性能或是能不能读写,于是就能随意选择上面的3种备份方式。
4.3.5 修复
修复数据库的过程实际上非常简单:将所有的文档导出然后马上导入,忽略那些无效的文档。完成以后,会重新建立索引。修复数据库还能起到压缩数据的作用。闲置的空间(删除大量文档后腾出的空间)在修复后被重新回收。