简介
- MongoDB 是一个面向文档(document-oriented)的数据库。
- 不再有预定义模式(predefined schema):文档的键(key)和值(value)不再是固定的类型和大小。
- MongoDB 能自动处理跨集群的数据和负载,自动重新分配文档,以及将用户请求路由到正确的机器上。
- MongoDB 并不具备一些在关系型数据库中很普遍的功能,如连接(join)和复杂的多行事务(multirow transaction)。省略这些功能是出于架构上的考虑(为了得到更好的扩展性),因为在分布式系统中这两个功能难以高效地实现。
- MongoDB 把尽可能多的内存用作缓存(cache),试图为每次查询自动选择正确的索引。
基本概念
文档–>行
- 是键值对的一个有序集
集合–>表
- 组织集合的一种惯例是使用“.”分隔不同命名空间的子集合。
数据库
- 多个文档组成集合,而多个集合可以组成数据库。
- MongoDB 的一个实例可以拥有多个相互独立的数据库(database),每一个数据库都拥有自己的集合。
- 按照经验,我们将有关一个应用程序的所有数据都存储在同一个数据库中。
- 把数据库名添加到集合名前,得到集合的完全限定名,即命名空间(namespace)。
安装
sudo yum install mongodb mongodb-server
mongod 还会启动一个非常基本的 HTTP 服务器,监听数字比主端口号高 1000 的端口,也就是 28017 端口。这意味着,通过浏览器访问 http://localhost:28017,能获取数据库的管理信息。
启动配置
sudo vim /etc/mongod.conf <!-- 配置后台启动 --> fork = true
启动
使用 -f 或 –config 标记,告知服务器使用配置文件mongod --config /etc/mongod.conf mongod -f /etc/mongod.conf
数据类型
MongoDB的文档类似于JSON。JSON只有 null 、布尔、数字、字符串、数组和对象这几种数据类型。没有日期类型。仅有一种数字类型,不能区分浮点数和整数,不能区分32位和64位。没有正则或函数等。MongoDB添加了一些其他类型。
null
布尔型 true false
数值
默认64位浮点型。整型可用NumberInt(4字节带符号整数),NumberLong(8字节带符号整数)。字符串
日期
日期被存储为自新纪元以来经过的毫秒数,不存储时区正则
语法也与 JavaScript 的正则表达式语法相同{"x" : /foobar/i}
数组
数组可包含不同数据类型的元素。{"things" : ["pie", 3.14]}
MongoDB 可以查询出 “things” 数组中包含 3.14这个元素的所有文档。要是经常使用这个查询,可以对 “things” 创建索引来提高性能。
MongoDB 可以使用原子更新对数组内容进行修改,比如深入数组内部将 pie 改为
pi。内嵌文档
{"x" : {"foo" : "bar"}}
ObjectId
不同的机器都能用全局唯一的同种方法方便地生成它。在多个服务器上同步自动增加主键值既费力又费时。所以能够在分片环境中生成唯一的标示符非常重要。12字节。0 1 2 3 4 5 6 7 8 9 10 11 时间戳 机器 PID 计数器 {"x" : ObjectId()}
自动生成的_id
如果插入文档时没有 “_ id” 键, 系统 会自动帮你创建一个。 可以由MongoDB 服务器来做这件事,但通常会在客户端由驱动程序完成。这一做法非常好地体现了 MongoDB 的哲学:能交给客户端驱动程序来做的事情就不要交给服务器来做。
二进制数据
代码
查询和文档中可以包括任意 JavaScript 代码{"x" : function() { /* ... * / }}
shell
mongo some-host:30000/myDB
<!-- 启动时不连接任何数据库 -->
mongo --nodb
conn = new Mongo("some-host:30000")
db = conn.getDB("myDB")
mongo 是一个简化的 JavaScript shell,可以通过查看 JavaScript 的在线文档得到大量帮助。对于 MongoDB 特有的功能,shell 内置了帮助文档,可以使用 help命令查看
help
执行脚本
<!-- 命令行执行 --> mongo script1.js script2.js script3.js <!-- 交互执行 --> load("script1.js")
如果某些脚本会被频繁加载,可以将它们添加到 .mongorc.js 文件中。这个文件会在启动 shell 时自动运行。
.mongorc.js 最常见的用途之一是移除那些比较“危险”的 shell 辅助函数。可以在这里集中重写这些方法,比如为dropDatabase 或者 deleteIndexes 等辅助函数添加 no 选项,或者取消它们的定义。
var no = function() { print("Not on my watch."); }; // 禁止删除数据库 db.dropDatabase = DB.prototype.dropDatabase = no; // 禁止删除集合 DBCollection.prototype.drop = no; // 禁止删除索引 DBCollection.prototype.dropIndex = no;
在启动 shell 时指定 –norc 参数,就可以禁止加载 .mongorc.js。
定制shell提示
prompt = function() { return (new Date())+"> "; }; prompt = function() { if (typeof db == 'undefined') { return '(nodb)> '; } // 检查最后的数据库操作 try { db.runCommand({getLastError:1}); } catch (e) { print(e); } return db+"> "; };
配置编辑器
EDITOR="/usr/bin/emacs"
编辑变量
var wap = db.books.findOne({title: "War and Peace"}) edit wap
客户端常用命令
查看db当前指向的数据库
> db test
切换数据库
> use foobar switched to db foobar
创建、更新和删除文档
插入
db.foo.insert({"bar" : "baz"})
<!-- 批量插入 -->
db.foo.batchInsert([{"_ id" : 0}, {"_ id" : 1}, {"_ id" : 2}])
<!-- 忽略错误并且继续执行添加 continueOnError -->
删除
db.foo.remove()
<!-- 删除 mailing.list 集合中所有 "opt-out" 为true 的人 -->
db.mailing.list.remove({"opt-out" : true})
<!-- drop 直接删除集合会更快 -->
db.tester.drop()
更新
文档替换
db.people.update({"_ id" : ObjectId("4b2b9f67a1f631733d917a7c")}, joe)
使用原子性的修改器,指定对文档中的某些字段进行更新。
要有人访问页面,就增加计数器。
db.analytics.update({"url" : "www.example.com"}, {"$inc" : {"pageviews" : 1}})、
“$set” 用来指定一个字段的值。如果这个字段不存在,则创建它。
$unset 删除键
db.users.update({"name" : "joe"}, {"$unset" : {"favorite book" : 1}})
增加、修改或删除键时,应该使用 $ 修改器。
“$inc” 修改器用来增加已有键的值,或者该键不存在那就创建一个。
“$push” 会向已有的数组末尾加入一个元素,要是没有就创建一个新的数组。
使用 “$each” 子操作符,可以通过一次 “$push” 操作添加多个值。
可以将 “$slice” 和 “$push” 组合在一起
使用,这样就可以保证数组不会超出设定好的最大长度可以在清理元素之前使用 “$sort”,只要向数组中添加子对象就需要清理:
db.movies.find({"genre" : "horror"},{"$push" : {"top10" : {"$each" : [{"name" : "Nightmare on Elm Street", "rating" : 6.6},{"name" : "Saw", "rating" : 4.3}],"$slice" : -10,"$sort" : {"rating" : -1}}}})
不能只将 “$slice” 或者 “$sort” 与 “$push” 配合使用,且必须使用 “$each”。
将数组作为set使用
db.users.update({"_ id" : ObjectId("4b2d75476cc613d5ee930164")},{"$addToSet" : {"emails" : "joe@gmail.com"}})
{“$pop”:{“key”:1}} 从数组末尾删除一个元素,{“$pop”:{“key”:-1}} 则从头部删除。需要基于特定条件来删除元素,”$pull”。
db.lists.update({}, {"$pull" : {"todo" : "laundry"}})
MongoDB 提供了定位操作符 “$”,用来定位查询文档已经匹配的数组元素,并进行更新。
upsert
使用 upsert,既可以避免竞态问题,又可以缩减代码量(update 的第 3 个参数表示这是个 upsert)
db.analytics.update({"url" : "/blog"}, {"$inc" : {"pageviews" : 1}}, true)
“$setOnInsert” 只会在文档插入时设置字段的值。
save 是一个 shell 函数,如果文档不存在,它会自动创建文档;如果文档存在,它就更新这个文档。
要更新所有匹配的文档,可以将 update 的第 4 个参数设置为 true。
运行 getLastError 命令(可以理
解为“返回最后一次操作的相关信息”)。键 “n” 的值就是被更新文档的数量。调用 getLastError 仅能获得关于更新的有限信息,并不能返回被更新的文档。可以通过 findAndModify 命令得到被更新的文档。这对于操作队列以及执行其他需要进行原子性取值和赋值的操作来说,十分方便。
查询
find
指定需要返回的键
db.users.find({}, {"username" : 1, "email" : 1}) <!-- 剔除 --> db.users.find({}, {"fatal_weakness" : 0})
“$lt”、”$lte”、”$gt” 和 “$gte” 就是全部的比较操作符,分别对应 <、<=、> 和 >=
“$ne”表示“不相等”
db.users.find({"username" : {"$ne" : "joe"}})
or
db.raffle.find({"ticket_no" : {"$in" : [725, 542, 390]}}) db.raffle.find({"ticket_no" : {"$nin" : [725, 542, 390]}}) db.raffle.find({"$or" : [{"ticket_no" : 725}, {"winner" : true}]})
not
“$mod” 会将查询的值除以第一个给定值,若余数等于第二个给定值则匹配成功db.users.find({"id_num" : {"$mod" : [5, 1]}}) db.users.find({"id_num" : {"$not" : {"$mod" : [5, 1]}}})
条件语句
注意,查询优化器不会对 “$and” 进行优化,这与其他操作符不同。db.users.find({"$and" : [{"x" : {"$lt" : 1}}, {"x" : 4}]})
索引
唯一索引
复合唯一索引创建
db.users.ensureIndex({"username" : 1, "age" : 1}, {"unique" : true})
在已存在集合创建唯一索引,去除重复值
db.people.ensureIndex({"username" : 1}, {"unique" : true, "dropDups" : true})
稀疏索引
唯一索引会把null当作一个值,稀疏索引可以避免这种情况
db.foo.ensureIndex({"email" : 1}, {"unique" : true, "sparse" : true})
索引管理
查看指定集合的索引信息
db.foo.getIndexes()
mongoimport
支持JSON,CSV,TSV,CSV格式delimiter仅支持”,”和”\t”
–fields 指定字段名字和类型
示例
<!-- --upsert insert or update objects that already exist --> <!-- --jsonArray load a json array, not one item per line. --> mongoimport --db db --collection foo --file f.json