MongoDB极简教程.docx
- 文档编号:16587003
- 上传时间:2023-07-15
- 格式:DOCX
- 页数:13
- 大小:514.33KB
MongoDB极简教程.docx
《MongoDB极简教程.docx》由会员分享,可在线阅读,更多相关《MongoDB极简教程.docx(13页珍藏版)》请在冰点文库上搜索。
MongoDB极简教程
1.MongDB简介
MongoDB 是专为可扩展性,高性能和高可用性而设计的数据库。
它可以从单服务器部署扩展到大型、复杂的多数据中心架构。
利用内存计算的优势,MongoDB能够提供高性能的数据读写操作。
MongoDB的本地复制和自动故障转移功能使您的应用程序具有企业级的可靠性和操作灵活性。
1.1文档型数据库
简而言之,MongoDB是一个免费开源跨平台的NoSQL数据库,与关系型数据库不同,MongoDB的数据以类似于JSON格式的二进制文档存储:
{
name:
"我没有三颗心脏",
age:
22,
}
文档型的数据存储方式有几个重要好处:
1.文档的数据类型可以对应到语言的数据类型,如数组类型(Array)和对象类型(Object);
2.文档可以嵌套,有时关系型数据库涉及几个表的操作,在MongoDB中一次就能完成,可以减少昂贵的连接花销;
3.文档不对数据结构加以限制,不同的数据结构可以存储在同一张表;
4.MongoDB的文档数据模型和索引系统能有效提升数据库性能;
5.复制集功能提供数据冗余,自动化容灾容错,提升数据库可用性;
6.分片技术能够分散单服务器的读写压力,提高并发能力,提升数据库的可拓展性;
7.MongoDB高性能,高可用性、可扩展性等特点。
1.2MongoDB基础概念
可以使用我们熟悉的MySQL数据库来加以对比:
MySQL基础概念
MongoDB对应概念
数据库(database)
容器(database)
表(table)
集合(collection)
行(row)
文档(document)
列(column)
域(filed)
索引(index)
索引(index)
)的图来更加形象生动的说明一下:
这很容易理解,但是问题在于:
我们为什么要引入新的概念呢?
(也就是为什么我们要把“表”替换成“集合”,“行”替换成“文档”,“列”替换成“域”呢?
)原因在于,其实在 MySQL这样的典型关系型数据中,我们是在定义表的时候定义列的,但是由于上述文档型数据库的特点,它允许文档的数据类型可以对应到语言的数据类型,所以我们是在定义文档的时候才会定义域的。
也就是说,集合中的每个文档都可以有独立的域。
因此,虽说集合相对于表来说是一个简化了的容器,而文档则包含了比行要多得多的信息。
2搭建环境
怎么样都好,搭建好环境就行,这里以OS环境为例,你可以使用OSX的brew安装mongodb:
brew install mongodb
在运行之前我们需要创建一个数据库存储目录 /data/db:
sudo mkdir -p /data/db
然后启动mongodb,默认数据库目录即为 /data/db(如果不是,可以使用 --dbpath 指令来指定):
sudo mongd
过一会儿你就能看到你的mongodb运行起来的提示:
3基于Shell的CRUD
3.1连接实例
通过上面的步骤我们在系统里运行了一个mongodb实例,接下来通过 mongo 命令来连接它:
mongo [options] [db address] [file names]
由于上面运行的mongodb运行在27017端口,并且灭有启动安全模式,所以我们也不需要输入用户名和密码就可以直接连接:
mongo 127.0.0.1:
27017
或者通过 --host 和 --port 选项指定主机和端口。
一切顺利的话,就进入了mongoDBshell,shell 会报出一连串权限警告,不过不用担心,这并不会影响之后的操作。
在添加授权用户和开启认证后,这些警告会自动消失。
3.2CRUD操作
在进行增删改查操作之前,我们需要先了解一下常用的 shell 命令:
∙db 显示当前所在数据库,默认为 test
∙showdbs 列出可用数据库
∙showtables showcollections 列出数据库中可用集合
∙use 用于切换数据库
mongoDB预设有两个数据库,admin和local,admin用来存放系统数据,local用来存放该实例数据,在副本集中,一个实例的local数据库对于其它实例是不可见的。
使用use命令切换数据库:
> use admin
> use local
> use newDatabase
可以use一个不存在的数据库,当你存入新数据时,mongoDB会创建这个数据库:
> use newDatabase
> db.newCollection.insert({x:
1})
WriteResult({ "nInserted" :
1 })
以上命令向数据库中插入一个文档,返回1表示插入成功,mongoDB自动创建newCollection集合和数据库newDatabase。
下面将对增查改删操作进行一个简单的演示。
3.2.1创建(Create)
MongoDB提供 insert 方法创建新文档:
∙db.collection.inserOne() 插入单个文档
WriteResult({"nInserted":
1})
∙db.collection.inserMany() 插入多个文档
∙db.collection.insert() 插入单条或多条文档
我们接着在刚才新创建的newDatabase下面新增数据吧:
db.newCollection.insert({name:
"wmyskxz",age:
22})
根据以往经验应该会觉得蛮奇怪的,因为之前在这个集合中插入的数据格式是 {x:
1} 的,而这里新增的数据格式确是 {name:
"wmyskxz",age:
22} 这个样子的。
还记得吗,文档型数据库的与传统型的关系型数据的区别就是在这里!
并且要注意,age:
22 和 age:
"22" 是不一样的哦,前者插入的是一个数值,而后者是字符串,我们可以通过 db.newCollection.find() 命令查看到刚刚插入的文档:
> db.newCollection.find()
{ "_id" :
ObjectId("5cc1026533907ae66490e46c"), "x" :
1 }
{ "_id" :
ObjectId("5cc102fb33907ae66490e46d"), "name" :
"wmyskxz", "age" :
22 }
这里有一个神奇的返回,那就是多了一个叫做 _id 的东西,这是MongoDB为你自动添加的字段,你也可以自己生成。
大部分情况下还是会让MongoDB为我们生成,而且默认情况下,该字段是被加上了索引的。
3.2.2查找(Read)
MongoDB提供 find 方法查找文档,第一个参数为查询条件:
> db.newCollection.find() # 查找所有文档
{ "_id" :
ObjectId("5cc1026533907ae66490e46c"), "x" :
1 }
{ "_id" :
ObjectId("5cc102fb33907ae66490e46d"), "name" :
"wmyskxz", "age" :
22 }
> db.newCollection.find({name:
"wmyskxz"}) # 查找 name 为 wmyskxz 的文档
{ "_id" :
ObjectId("5cc102fb33907ae66490e46d"), "name" :
"wmyskxz", "age" :
22 }
> db.newCollection.find({age:
{$gt:
20}}) # 查找 age 大于 20 的文档
{ "_id" :
ObjectId("5cc102fb33907ae66490e46d"), "name" :
"wmyskxz", "age" :
22 }
上述代码中的$gt对应于大于号>的转义。
第二个参数可以传入投影文档映射数据:
> db.newCollection.find({age:
{$gt:
20}},{name:
1})
{ "_id" :
ObjectId("5cc102fb33907ae66490e46d"), "name" :
"wmyskxz" }
上述命令将查找 age 大于20的文档,返回 name 字段,排除其他字段。
投影文档中字段为1或其他真值表示包含,0或假值表示排除,可以设置多个字段位为1或0,但不能混合使用。
为了测试,我们为这个集合弄了一些奇奇怪怪的数据:
> db.newCollection.find()
{ "_id" :
ObjectId("5cc1026533907ae66490e46c"), "x" :
1 }
{ "_id" :
ObjectId("5cc102fb33907ae66490e46d"), "name" :
"wmyskxz", "age" :
22 }
{ "_id" :
ObjectId("5cc108fb33907ae66490e46e"), "name" :
"wmyskxz-test", "age" :
22, "x" :
1, "y" :
30 }
然后再来测试:
> db.newCollection.find({age:
{$gt:
20}},{name:
1,x:
1})
{ "_id" :
ObjectId("5cc102fb33907ae66490e46d"), "name" :
"wmyskxz" }
{ "_id" :
ObjectId("5cc108fb33907ae66490e46e"), "name" :
"wmyskxz-test", "x" :
1 }
> db.newCollection.find({age:
{$gt:
20}},{name:
0,x:
0})
{ "_id" :
ObjectId("5cc102fb33907ae66490e46d"), "age" :
22 }
{ "_id" :
ObjectId("5cc108fb33907ae66490e46e"), "age" :
22, "y" :
30 }
> db.newCollection.find({age:
{$gt:
20}},{name:
0,x:
1})
Error:
error:
{
"ok" :
0,
"errmsg" :
"Projection cannot have a mix of inclusion and exclusion.",
"code" :
2,
"codeName" :
"BadValue"
}
从上面的命令我们就可以把我们的一些想法和上面的结论得以验证,perfect!
除此之外,还可以通过 count、skip、limit 等指针(Cursor)方法,改变文档查询的执行方式:
> db.newCollection.find().count()
3
> db.newCollection.find().skip
(1).limit(10).sort({age:
1})
{ "_id" :
ObjectId("5cc102fb33907ae66490e46d"), "name" :
"wmyskxz", "age" :
22 }
{ "_id" :
ObjectId("5cc108fb33907ae66490e46e"), "name" :
"wmyskxz-test", "age" :
22, "x" :
1, "y" :
30 }
上述查找命令跳过1个文档,限制输出10个,以 age 子段正序排序(大于0为正序,小于0位反序)输出结果。
最后,可以使用Cursor方法中的pretty方法,提升查询文档的易读性,特别是在查看嵌套的文档和配置文件的时候:
> db.newCollection.find().pretty()
{ "_id" :
ObjectId("5cc1026533907ae66490e46c"), "x" :
1 }
{
"_id" :
ObjectId("5cc102fb33907ae66490e46d"),
"name" :
"wmyskxz",
"age" :
22
}
{
"_id" :
ObjectId("5cc108fb33907ae66490e46e"),
"name" :
"wmyskxz-test",
"age" :
22,
"x" :
1,
"y" :
30
}
3.2.3更新(Update)
MongoDB提供 update 方法更新文档:
∙db.collection.updateOne() 更新最多一个符合条件的文档
∙db.collection.updateMany() 更新所有符合条件的文档
∙db.collection.replaceOne() 替代最多一个符合条件的文档
∙db.collection.update() 默认更新一个文档,可配置multi参数,跟新多个文档
以 update() 方法为例。
其格式:
> db.collection.update(
{
upsert:
multi:
}
)
各参数意义:
∙query为查询条件
∙update为修改的文档
∙upsert为真,查询为空时插入文档
∙multi为真,更新所有符合条件的文档
下面我们测试把 name 字段为 wmyskxz 的文档更新一下试试:
> db.newCollection.update({name:
"wmyskxz"},{name:
"wmyskxz",age:
30})
WriteResult({ "nMatched" :
1, "nUpserted" :
0, "nModified" :
1 })
要注意的是,如果更新文档只传入 age 字段,那么文档会被更新为{age:
30},而不是{name:
"wmyskxz",age:
30}。
要避免文档被覆盖,需要用到 $set 指令,$set 仅替换或添加指定字段:
> db.newCollection.update({name:
"wmyskxz"},{$set:
{age:
30}})
如果要在查询的文档不存在的时候插入文档,要把upsert参数设置真值:
> db.newCollection.update({name:
"wmyskxz11"},{$set:
{age:
30}},{upsert:
true})
update方法默认情况只更新一个文档,如果要更新符合条件的所有文档,要把multi设为真值,并使用 $set 指令:
> db.newCollection.update({age:
{$gt:
20}},{$set:
{test:
"A"}},{multi:
true})
WriteResult({ "nMatched" :
3, "nUpserted" :
0, "nModified" :
3 })
> db.newCollection.find()
{ "_id" :
ObjectId("5cc1026533907ae66490e46c"), "x" :
1 }
{ "_id" :
ObjectId("5cc102fb33907ae66490e46d"), "name" :
"wmyskxz", "age" :
30, "test" :
"A" }
{ "_id" :
ObjectId("5cc108fb33907ae66490e46e"), "name" :
"wmyskxz-test", "age" :
22, "x" :
1, "y" :
30, "test" :
"A" }
{ "_id" :
ObjectId("5cc110148d0a578f03d43e81"), "name" :
"wmyskxz11", "age" :
30, "test" :
"A" }
3.2.4删除(Delete)
MongoDB提供了 delete 方法删除文档:
∙db.collection.deleteOne() 删除最多一个符合条件的文档
∙db.collection.deleteMany() 删除所有符合条件的文档
∙db.collection.remove() 删除一个或多个文档
以remove方法为例:
> db.newCollection.remove({name:
"wmyskxz11"})
> db.newCollection.remove({age:
{$gt:
20}},{justOne:
true})
> db.newCollection.find()
{ "_id" :
ObjectId("5cc1026533907ae66490e46c"), "x" :
1 }
{ "_id" :
ObjectId("5cc108fb33907ae66490e46e"), "name" :
"wmyskxz-test", "age" :
22, "x" :
1, "y" :
30, "test" :
"A" }
MongoDB提供了drop方法删除集合,返回true表面删除集合成功:
> db.newCollection.drop()
3.2.5小结
相比传统关系型数据库,MongoDB的CURD操作更像是编写程序,更符合开发人员的直觉,不过MongoDB同样也支持SQL语言。
MongoDB的CURD引擎配合索引技术、数据聚合技术和JavaScript引擎,赋予MongoDB用户更强大的操纵数据的能力。
4MongoDB数据模型的一些讨论
这是一个抽象的话题,与大多数NoSQL方案相比,在建模方面,面向文档的数据库算是和关系数据库相差最小的。
这些差别是很小,但是并不是说不重要。
4.1没有连接(Join)
您要接受的第一个也是最基本的一个差别,就是 MongoDB没有连接(join)。
我不知道MongoDB不支持某些类型连接句法的具体原因,但是我知道一般而言人们认为连接是不可扩展的。
也就是说,一旦开始横向分割数据,最终不可避免的就是在客户端(应用程序服务器)使用连接。
且不论MongoDB为什么不支持连接,事实是数据是有关系的,可是MongoDB不支持连接。
(译者:
这里的关系指的是不同的数据之间是有关联的,对于没有关系的数据,就完全不需要连接。
)
为了在没有连接的MongoDB中生存下去,在没有其他帮助的情况下,我们必须在自己的应用程序中实现连接。
基本上我们需要用第二次查询去找到相关的数据。
找到并组织这些数据相当于在关系数据库中声明一个外来的键。
现在先别管什么独角兽了,我们来看看我们的员工。
首先我们创建一个员工的数据(这次我告诉您具体的_id值,这样我们的例子就是一样的了):
db.employees.insert({_id:
ObjectId("4d85c7039ab0fd70a117d730"), name:
'Leto'})
然后我们再加入几个员工并把 Leto 设成他们的老板:
db.employees.insert({_id:
ObjectId("4d85c7039ab0fd70a117d731"), name:
'Duncan', manager:
ObjectId("4d85c7039ab0fd70a117d730")});
db.employees.insert({_id:
ObjectId("4d85c7039ab0fd70a117d732"), name:
'Moneo', manager:
ObjectId("4d85c7039ab0fd70a117d730")});
(有必要再强调一下,_id可以是任何的唯一的值。
在实际工作中你很可能会用到ObjectId,所以我们在这里也使用它)
显然,要找到Leto的所有员工,只要执行:
db.employees.find({manager:
ObjectId("4d85c7039ab0fd70a117d730")})
没什么了不起的。
在最糟糕的情况下,为弥补连接的缺失需要做的只是再多查询一次而已,该查询很可能是经过索引了的。
4.1.1数组和嵌入文档(EmbeddedDocuments)
MongoDB没有连接并不意味着它没有其他的优势。
还记得我们曾说过MongoDB支持数组并把它当成文档中的一级对象吗?
当处理多对一或是多对多关系的时候,这一特性就显得非常好用了。
用一个简单的例子来说明,如果一个员工有两个经理,我们可以把这个关系储存在一个数组当中:
({name:
'Siona', manager:
[ObjectId("4d85c7039ab0fd70a117d730"), ObjectId("4d85c7039ab0fd70a117d732")] })
需要注意的是,在这种情况下,有些文档中的 manager 可能是一个向量,而其他的却是数组。
在两种情况下,前面的 find 还是一样可以工作:
db.employees.find({manager:
ObjectId("4d85c7039ab0fd70a117d730")})
很快您就会发现数组中的值比起多对多的连接表(join-table)来说要更容易处理。
除了数组,MongoDB还支持嵌入文档。
尝试插入含有内嵌文档的文档,像这样:
db.employees.insert({_id:
ObjectId("4d85c7039ab0fd70a117d734"), name:
'Ghanima', family:
{mother:
'Chani', father:
'Paul', brother:
ObjectId("4d85c7039ab0fd70a117d730")}})
也许您会这样想,确实也可以这样做:
嵌入文档可以用‘.’符号来查询:
db.employees.find({'family.mother':
'Chani'})
就这样,我们简要地介绍了嵌入文档适用的场合以及您应该怎样使用它。
4.1.2DBRef
MongoDB支持一个叫做DBRef的功能,许多MongoDB的驱动都提供对这一功能的支持。
当驱动遇到一个 DBRef 时它会把当中引用的文档读取出来。
DBRef 包含了所引用的文档的ID和所在的集合。
它通常专门用于这样的场合:
相同集合中的文档需要引用另外一个集合中的不同文档。
例如,文档1的 DBRef 可能指向 managers 中的文档,而文档2中的 DBRef 可能指向 employees中的文档。
4.1.3范规范化(Denormalization)
代替连接的另一种方法就是反规范化数据。
在过去,反规范化是为性能敏感代码所设,或者是需要数据快照(例如审计日志)的时候才应用的。
然而,随着NoSQL的日渐普及,有许多这样的数据库并不提供连接操作,于是作为规范建模的一部分,反规范化就越来越常见了。
这样说并不是说您就需要为每个文档中的每一条信息创建副本。
与此相反,与其在
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- MongoDB 教程
![提示](https://static.bingdoc.com/images/bang_tan.gif)