前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MongoDB 学习笔记2 - 基础知识和使用

MongoDB 学习笔记2 - 基础知识和使用

作者头像
张云飞Vir
发布2020-04-09 14:42:04
1.2K0
发布2020-04-09 14:42:04
举报
文章被收录于专栏:写代码和思考写代码和思考

0. 背景

MongoDB 的一些基础知识和使用。

MongoDB

1. 基础知识

六个简单的概念:

  • (1) database(数据库):MongoDB中 也有 数据库 的概念,和关系型数据中的的"数据库"一样的概念。一个 MongoDB 实例中,可以有零个或多个数据库,用于存储数据。
  • (2) collections (集合):一个数据库中可以有多个 collections (集合)。它和传统意义上的 table 是一样的东西。
  • (3) documents (文档):一个集合由多个 documents (文档)组成。一个文档可以看成是一条数据的记录( row 或者 record)。
  • (4) fields (字段:一个文档是由多个 fields (字段)组成。它就是 columns。
  • (5) Indexes (索引): MongoDB 的索引和 RDBMS 中的一样。
  • (6) Cursors (游标):当你问 MongoDB 拿数据的时候,它会给你返回一个结果集的指针而不是真正的数据,这个指针我们叫它游标。

概念对照表:

MongoDB

传统的关系型数据库

database

database, 相同

collections

table

documents

row

fields

columns

Indexes

Indexes 相同

Cursors

-

当我们从 MongoDB 获取数据的时候,我们通过 cursor 来操作,读操作会被延迟到需要实际数据的时候才会执行。

核心差异在于,在MongoDB里,collection中的每个documents都可以有自己独立的 field (字段),而关系型数据中每行的字段都智能相同

要点就是,集合不对存储内容严格限制 (所谓的无模式(schema-less))。字段由每个独立的文档进行跟踪处理。

总结:MongoDB 可以每行数据的结构都不同,支持非结构化数据。 区别于 传统的严格结构化数据。

2. 基本操作

2.1 连接到数据库

MongoDB 的 shell MongoDB 的 shell 是一个连接数据库服务的客户端控制台工具。MongoDB 启动shell ,在命令行输入:

代码语言:javascript
复制
mongo

shell 用的是 JavaScript。 因为这是一个 JavaScript shell,如果你输入的命令漏了 (),你会看到这个命令的源码,拿到一个以 function (...){ 开头的返回的内容。

MongoDB 内部用二进制序列化 JSON 格式,称为 BSON。

如果要操作当前数据库,用 db ,比如 db.help() 或者 db.stats()。

use 用来切换数据库,比如,输入 use learn。

大多数情况下我们会操作集合, 而不是数据库。比如:用 db.COLLECTION_NAME ,比如 db.unicorns.help() 或者 db.unicorns.count()。

注意,除你指定的字段之外,会多出一个 _id 字段。每个文档都会有一个唯一 _id 字段。你可以自己生成一个,或者让 MongoDB 帮你生成一个 ObjectId 类型的。

2.2 插入数据

代码语言:javascript
复制
db.unicorns.insert({name: 'Aurora',
    gender: 'f', weight: 450})

2.3 删除数据

代码语言:javascript
复制
db.unicorns.remove({})。

2.4 查询

掌握选择器(Selector):MongoDB 的查询选择器就像 SQL 语句里面的 where 一样。

因此,你会在对集合的文档做查找,计数,更新,删除的时候用到它。选择器是一个 JSON 对象,最简单的是就是用 {} 匹配所有的文档。比如可以用 {gender:'f'}。

{field: value} 用来查找那些 field 的值等于 value 的文档。 {field1: value1, field2: value2} 相当于 and 查询。还有

lt,
lt,

lte,

gt,
gt,

gte 和 $ne 被用来处理 小于,小于等于,大于,大于等于,和不等于操作。

代码语言:javascript
复制
db.unicorns.find({gender: 'm',
    weight: {$gt: 700}})

db.unicorns.find({gender: {$ne: 'f'},
    weight: {$gte: 701}})

2.5 判断是否存在

$exists 用来匹配字段是否存在,比如:

代码语言:javascript
复制
db.unicorns.find({
vampires: {$exists: false}})

2.6 是否被包含用 $in

'$in' 被用来匹配查询文档在我们传入的数组参数中是否存在匹配值,比如:

代码语言:javascript
复制
db.unicorns.find({
loves: {$in:['apple','orange']}})

2.7 逻辑操作中的 or,通过 $or 操作符 来操作。

使用 $or 操作符,再给它一个我们要匹配的数组:

代码语言:javascript
复制
db.unicorns.find({gender: 'f',
$or: [{loves: 'apple'},
      {weight: {$lt: 500}}]})

2.8 查询 _id 字段

_id 字段生成的 ObjectId 可以这样查询:

代码语言:javascript
复制
db.unicorns.find(
{_id: ObjectId("TheObjectId")})

3. 更新数据 (Update)

如果改变一个或者几个字段的值的时候,你应该用 MongoDB 的 $set 操作。

$set 操作

代码语言:javascript
复制
db.unicorns.update({weight: 590}, {$set: {
name: 'Roooooodles',
dob: new Date(1979, 7, 18, 18, 44),
loves: ['apple'],
gender: 'm',
vampires: 99}})

直接用 update 会产生覆盖效果,谨慎使用:

代码语言:javascript
复制
db.unicorns.update({name: 'Roooooodles'},
{weight: 590})

数值的增减

$inc 可以用来给一个字段增加一个正/负值

db.unicorns.update({name: 'Pilot'}, {$inc: {vampires: -2}})

增加新自动,用 $push

字段加一个值,通过 $push 操作:

db.unicorns.update({name: 'Aurora'}, {$push: {loves: 'sugar'}})

Upserts

update 语法还支持 upsert 更新,即:在文档中找到匹配值时更新它,无匹配时向文档插入新值。

要使用 upsert 我们需要向 update 写入第三个参数 {upsert:true},示例:

代码语言:javascript
复制
db.hits.update({page: 'unicorns'},
{$inc: {hits: 1}}, {upsert:true});

db.hits.find();

批量 Updates时,multi 选项需要设为 true:

代码语言:javascript
复制
db.unicorns.update({},
{$set: {vaccinated: true }},
{multi:true});
db.unicorns.find({vaccinated: true});

4. 查询

字段选择

find 有第二个可选参数,叫做 "projection"。这个参数是我们要检索或者排除字段的列表。

代码语言:javascript
复制
db.unicorns.find({}, {name: 1});

默认的,_id 字段总是会返回的。我们可以通过这样显式的把它从返回结果中排除 {name:1, _id: 0}。

排序(Ordering)

sort 用于排序,我们指定我们希望排序的字段,以 JSON 方式,其中 1 表示升序 -1 表示降序。比如:

代码语言:javascript
复制
//heaviest unicorns first
db.unicorns.find().sort({weight: -1})

//by unicorn name then vampire kills:
db.unicorns.find().sort({name: 1,
    vampires: -1})

MongoDB 对未经索引的字段进行排序是有大小限制的。如果你试图对一个非常大的没有经过索引的结果集进行排序的话,你会得到个异常。

分页(Paging)

对结果分页可以通过 limitskip 游标方法来实现。比如:

代码语言:javascript
复制
db.unicorns.find()
    .sort({weight: -1})
    .limit(2)
    .skip(1)

通过 limitsort 的配合,可以在对非索引字段进行排序时避免引起问题。

count 计数

shell 中可以直接对一个集合执行 count ,像这样:

代码语言:javascript
复制
db.unicorns.count({vampires: {$gt: 50}})

实际上,count 是一个 cursor 的方法,shell 只是简单的提供了一个快捷方式。

以不提供快捷方式的方法来执行的时候需要这样:

代码语言:javascript
复制
db.unicorns.find({vampires: {$gt: 50}})
    .count()

5. 数据建模

不支持 join

mongoDB 没有 join (链接, 比如 内连接inner Join,外连接out join)。传统数据库中的 join 基本上意味着不可扩展。就是说,如果想把数据水平扩展,你只能放弃在使用join。事实就是,数据之间的关系, 在 MongoDB 中无法直接表达和查询。只能在我们的应用代码中自己实现,需要进行二次查询 find ,把相关数据保存到另一个集合中。

示例:

先添加一个名叫 Leto 的 主管

代码语言:javascript
复制
db.employees.insert({_id: ObjectId(
    "4d85c7039ab0fd70a117d730"),
    name: 'Leto'})

然后再加几个工人,把他们的 主管 设置为 Leto:

代码语言:javascript
复制
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 负责管理的所有工人,只需要执行:

代码语言:javascript
复制
db.employees.find({manager: ObjectId(
    "4d85c7039ab0fd70a117d730")})

数组

示例:

代码语言:javascript
复制
// 插入一个 manager 是单个对象。
db.employees.insert({_id: ObjectId(
    "4d85c7039ab0fd70a117d732"),
    name: 'Moneo',
    manager: ObjectId(
    "4d85c7039ab0fd70a117d730")});

// 插入一个 manager 是 数组。
db.employees.insert({_id: ObjectId(
    "4d85c7039ab0fd70a117d733"),
    name: 'Siona',
    manager: [ObjectId(
    "4d85c7039ab0fd70a117d730"),
    ObjectId(
    "4d85c7039ab0fd70a117d732")] })

注意上面的文档,manager 字段的值既可以是单个对象,也可以是数组。而我们原来的 find 查询依旧可用:

代码语言:javascript
复制
db.employees.find({manager: ObjectId(
    "4d85c7039ab0fd70a117d730")})

内嵌文档

MongoDB 还支持内嵌文档。来试试看向文档插入一个内嵌文档,像这样:

代码语言:javascript
复制
db.employees.insert({_id: ObjectId(
"4d85c7039ab0fd70a117d734"),
name: 'Ghanima',
family: {mother: 'Chani',
    father: 'Paul',
    brother: ObjectId(
"4d85c7039ab0fd70a117d730")}})

像你猜的那样,内嵌文档可以用 dot-notation 查询:

代码语言:javascript
复制
db.employees.find({
'family.mother': 'Chani'})

反规范化(Denormalization)

一些对冗余处理 的讨论。 并不是需要对你文档里的每条数据都做冗余处理。而是说,与其对冗余数据心存恐惧,让它影响你的设计决策,不如在建模的时候考虑什么信息应当属于什么文档。

假设你要写一个论坛应用。

  • 传统的方式是通过 posts 中的 userid 列,来关联一个特定的 user 和一篇 post 。这样的建模,在显示 posts 的时候要查询 (链接到) users。
  • 一个代替案是“增加冗余字段”,在每篇 post 中都冗余的多存储 name 和 userid 两个字段。这要用到内嵌文档,比如 user: {id: ObjectId('Something'), name: 'Leto'}。缺点是,如果用户可以更新他们的名字,那将不得不对所有的文档都进行更新。

其他选择

记住: 一个独立文档的大小当前被限制在 16MB

处理一对多(one-to-many)或者多对多(many-to-many)场景的时候,id 数组通常是一个正确的选择。

内嵌文档经常使用的情形:大多数情况下多是很小的数据块,面对总是被和父节点一起拉取的数据块。

集合的规模讨论:单个大而全?还是拆分小而专?

比如,常见的例子就是博客。你是应该分成一个 posts 集合和一个 comments 集合呢,还是应该每个 post 下面嵌入一个 comments 数组?

MongoDB 的处理方式:MongoDB 的灵活架构允许你把这两种方式结合起来,你可以把评论放在独立的集合中,同时在博客帖子下嵌入一小部分评论 (比如说最新评论) ,以便和帖子一同显示。

这遵守以下的规则:“ 你到底想在一次查询中获取到什么内容,那就怎么做。”

想一想,如果在关系型数据库中,要把上面说的这两种方式结合起来用,“要不要再建一个关联表呢?”

6. MongoDB 适用场景

单一解决方案还是多技术方案? 对于许多项目来说 - 或者说大多数 - 单一解决案是一个明智的选择。只有你自己才知道,引进新技术是否利大于弊。引入MongoDB 往往不会完全替换旧的方案(比如用Mongo替换MySQL),而是说“不用再依赖单一的解决案来处理你的数据”,作为数据存储的局部替代方案,是对你现有数据存储方案能力的局部增强。

比如说用 Lucene 作为关系型数据库的全文检索索引的加强,或者用 Redis 作为持久型 key-value 存储对缓存存储的增强,MongoDB 就是用来保存你的数据能力的处理增强。

写操作(Writes)

MongoDB 可以胜任的一个特殊角色是在日志领域。有两点使得 MongoDB 的写操作非常快。首先,你可以选择发送了写操作命令之后立刻返回,而无须等到操作完成。

如果想让你的数据 "过期" ,基于时间而不是整个集合的大小,你可以用 TTL 索引 ,所谓 TTL 是 "time-to-live" 的缩写。

持久性(Durability)

从 2.0 版的 MongoDB 开始,日志是默认启动的,该功能允许快速恢复服务器,比如遭遇到了服务器崩溃或者停电的情况。

事务(Transactions)

MongoDB 不支持事务。两个代替案:

  • 第一个方案,就是各种原子更新操作。比如
inc 和
inc 和

set。还有像 findAndModify 命令,可以更新或删除文档之后,自动返回修改过的文档

  • 第二个方案,当原子操作不能满足的时候,回到两段提交上来。

地理空间查询(Geospatial)

一个很强大的功能就是 MongoDB 支持 geospatial 索引。这允许你保存 geoJSON 或者 x 和 y 坐标到文档,并查询文档,用如

near 来获取坐标集,或者
near 来获取坐标集,或者

within 来获取一个矩形或圆中的点。

7. 聚合管道(Aggregation Pipeline)

聚合管道提供了一种方法用于转换整合文档到集合。你可以通过管道来传递文档,就像 Unix 的 "pipe" 一样,将一个命令的输出传递到另第二个,第三个,等等

8. 性能和工具

索引(Index)

创建索引用 ensureIndex :

代码语言:javascript
复制
// where "name" is the field name
db.unicorns.ensureIndex({name: 1});

删除索引用 dropIndex:

代码语言:javascript
复制
db.unicorns.dropIndex({name: 1});

可以创建唯一索引,这需要把第二个参数 unique 设置为 true:

代码语言:javascript
复制
db.unicorns.ensureIndex({name: 1},
{unique: true});

索引可以内嵌到字段中和任何数组字段。我们可以这样创建复合索引:

代码语言:javascript
复制
db.unicorns.ensureIndex({name: 1,
vampires: -1});

Explain

需要检查你的查询是否用到了索引,你可以通过 explain 方法:

代码语言:javascript
复制
db.unicorns.find().explain()

复制(Replication)

MongoDB 的复制在某些方面和关系型数据库的复制类似。所有的生产部署应该都是副本集,理想情况下,三个或者多个服务器都保持相同的数据。写操作被发送到单个服务器,也即主服务器,然后从它异步复制到所有的从服务器上。你可以控制是否允许从服务器上进行读操作,这可以让一些特定的查询从主服务器中分离出来,当然,存在读取到旧数据的风险。如果主服务器异常关闭,从服务中的一个将会自动晋升为新的主服务器继续工作。

分片(Sharding)

MongoDB 支持自动分片。分片是实现数据扩展的一种方法,依靠在跨服务器或者集群上进行数据分区来实现。一个最简单的实现是把所有的用户数据,按照名字首字母 A-M 放在服务器 1 ,然后剩下的放在服务器 2。

状态(Stats)

你可以通过 db.stats() 查询数据库的状态。

分析器(Profiler)

可以这样执行 MongoDB profiler :

代码语言:javascript
复制
db.setProfilingLevel(2);

备份和还原

来备份我们的 learn 数据库导 backup 文件夹,我们需要在控制台或者终端中执行执行

代码语言:javascript
复制
mongodump --db learn --out backup

如果只还原 unicorns 集合,我们可以这样做:

代码语言:javascript
复制
mongorestore --db learn --collection unicorns \
backup/learn/unicorns.bson

文件导入导入数据 mongoexport 和 mongoimport 是另外两个可执行文件,用于导出和从 JSON/CSV 格式文件导入数据。比如说,我们可以像这样导出一个 JSON:

代码语言:javascript
复制
mongoexport --db learn --collection unicorns

CSV 格式是这样:

代码语言:javascript
复制
mongoexport --db learn \
--collection unicorns \
--csv --fields name,weight,vampires

9. 参考

https://github.com/ilivebox/the-little-mongodb-book/blob/master/zh-cn/mongodb.markdown

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0. 背景
  • 1. 基础知识
  • 2. 基本操作
    • 2.1 连接到数据库
      • 2.2 插入数据
        • 2.3 删除数据
          • 2.4 查询
            • 2.5 判断是否存在
              • 2.6 是否被包含用 $in
                • 2.7 逻辑操作中的 or,通过 $or 操作符 来操作。
                  • 2.8 查询 _id 字段
                  • 3. 更新数据 (Update)
                    • 数值的增减
                      • 增加新自动,用 $push
                        • Upserts
                        • 4. 查询
                          • 字段选择
                            • 排序(Ordering)
                              • 分页(Paging)
                                • count 计数
                                • 5. 数据建模
                                  • 不支持 join
                                    • 数组
                                      • 内嵌文档
                                        • 反规范化(Denormalization)
                                          • 其他选择
                                            • 集合的规模讨论:单个大而全?还是拆分小而专?
                                            • 6. MongoDB 适用场景
                                              • 写操作(Writes)
                                                • 持久性(Durability)
                                                  • 事务(Transactions)
                                                    • 地理空间查询(Geospatial)
                                                    • 7. 聚合管道(Aggregation Pipeline)
                                                    • 8. 性能和工具
                                                      • 索引(Index)
                                                        • Explain
                                                          • 复制(Replication)
                                                            • 分片(Sharding)
                                                              • 状态(Stats)
                                                                • 分析器(Profiler)
                                                                  • 备份和还原
                                                                  • 9. 参考
                                                                  相关产品与服务
                                                                  云数据库 MongoDB
                                                                  腾讯云数据库 MongoDB(TencentDB for MongoDB)是腾讯云基于全球广受欢迎的 MongoDB 打造的高性能 NoSQL 数据库,100%完全兼容 MongoDB 协议,支持跨文档事务,提供稳定丰富的监控管理,弹性可扩展、自动容灾,适用于文档型数据库场景,您无需自建灾备体系及控制管理系统。
                                                                  领券
                                                                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档