MongoDB

12 篇文章 1 订阅

https://www.yuque.com/books/share/27406466-0fb6-48da-88db-0270fbeecb8b/zg9z0c

MongoDB 简介

NoSQL 简介

2008 年左右,网站 、 论坛、社交网络开始高速发展,传统的关系型数据库在存储及处理数据的时候受到了很大的挑战 ,其中主要体现在以下几点:

  • 难以应付每秒上万次的高并发数据写入 。
  • 查询上亿量级数据的速度极其缓慢 。
  • 分库、分表形成的子库到达一定规模后难以进一步扩展 。
  • 分库、分表 的规则可能会因为需求变更而发生变更。
  • 修改表结构困难 。

在数据量暴增的时代,若想用传统的关系型数据库来满足数据高并发读写、巨量数据的存储、数据库的扩展和高可用,则需要增加软硬件的规格,这将大幅提高成本。

为了解决上述问题,必须有一款自废武功,以求在更高层次上突破瓶颈的数据库系统。就像张无忌忘记招式从而学习太极一样,摈弃了固有模式的 MongoDB 才能应对 Facebook 上亿比特的海量数据。

NoSQL(NoSQL = Not Only SQL ),意即"不仅仅是SQL",也叫非关系型数据库(non-relational)。

非关系型简单理解就是把数据直接放进一个大仓库,不标号、不连线、单纯的堆起来,从而提高了对海量数据的高性能存储及访问需求。

NoSQL 是一项全新的数据库革命性运动,早期就有人提出,发展至2009年趋势越发高涨。NoSQL 的拥护者们提倡运用非关系型的数据存储,相对于铺天盖地的关系型数据库运用,这一概念无疑是一种全新的思维的注入。

NoSQL 分类

(1)键值数据库

这类数据库主要是使用数据结构中的键 Key 来查找特定的数据Value。

  • 优点:在存储时不采用任何模式,因此极易添加数据

这类数据库具有极高的读写性能,用于处理大量数据的高访问负载比较合适。
键值对数据库适合大量数据的高访问及写入负载场景,例如日志系统。
主要代表是 Redis、Flare。

(2)文档型数据库

这类数据库满足了海量数据的存储和访问需求,同时对字段要求不严格,可以随意增加、删除、修改字段,且不需要预先定义表结构,所以适用于各种网络应用。
主要代表是 MongoDB、CouchDB。

(3)列存储型数据库

主要代表是 Cassandra 、Hbase。
这类数据库查找速度快,可扩展性强,适合用作分布式文件存储系统。

(4)图数据库

主要代表是 InfoGrid 、Neo4J 。
这类数据库利用“图结构”的相关算法来存储实体之间的关系信息,适合用于构建社交网络和推荐系统的关系图谱。

NoSQL 与 RDB 该这么选

既然 NoSQL 数据库有这么多的优势,那它是否可以直接取代关系型数据库?

NoSQL 并不能完全取代关系型数据库,NoSQL 主要被用来处理大量且多元数据的存储及运算问题。在这样的特性差异下,我们该如何选择合适的数据库以解决数据存储与处理问题呢?这里提供以下几点作为判断依据。

1、数据模型的关联性要求

NoSQL 适合模型关联性比较低的应用。因此:

  • 如果需要多表关联,则更适合用 RDB
  • 如果对象实体关联少,则更适合用 NoSQL 数据库
    • 其中 MongoDB 可以支持复杂度相对高的数据结构,能够将相关联的数据以文档的方式嵌入,从而减少数据之间的关联操作

2、数据库的性能要求

如果数据量多切访问速度至关重要,那么使用 NoSQL 数据库可能是比较合适的。NoSQL 数据库能通过数据的分布存储大幅地提供存储性能。

3、数据的一致性要求

NoSQL 数据库有一个缺点:其在事务处理与一致性方面无法与 RDB 相提并论。
因此,NoSQL 数据库很难同时满足强一致性与高并发性。如果应用对性能有高要求,则 NoSQL 数据库只能做到数据最终一致。

4、数据的可用性要求

考虑到数据不可用可能会造成风险,NoSQL 数据库提供了强大的数据可用性(在一些需要快速反馈信息给使用者的应用中,响应延迟也算某种程度的高可用)。

一个项目并非只选择一种数据库,可以将其拆开设计,将需要 RDB 特性的放到 RDB 中管理,而其它数据放到 NoSQL 中管理。

什么是 MongoDB

  • 官方文档:https://www.mongodb.com/
  • MongoDB 是由 C++ 语言编写的,是一个基于分布式文件存储的开源 NoSQL 数据库系统。
  • MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
    • 这会让曾经使用过关系型数据库的人比较容易上手
  • MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
    在这里插入图片描述
  • MongoDB 的查询功能非常强大
    • 不仅支持大部分关系型数据库中的单表查询,还支持范围查询、排序、聚合、MapReduce 等
    • MongoDB 的查询语法类似于面相对象的程序语言

MongoDB 有哪些特点

  • 文档型数据库
  • 高性能
  • 灵活性
  • 可扩展性
  • 强大的查询语言
  • 优异的性能
  • 高性能:支持使用嵌入数据时,减少系统I/O负担,支持子文档查询
  • 多种查询类型支持,且支持数据聚合查询、文本检索、地址位置查询
  • 高可用、水平扩展:支持副本集与分片
  • 多种存储引擎:WiredTiger , In-Memory

MongoDB 发展历史

  • 2007年10月,MongoDB 由 10gen 团队所发展
  • 2009年2月首度推出 1.0 版
  • 2011年9月,发布 2.0 版本
    • 分片、复制等功能
  • 2015年3月,发布 3.0 版本
    • WiredTiger存储引擎支持
  • 2018年6月,发布 4.0 版本
    • 推出ACID事务支持,成为第一个支持强事务的NoSQL数据库;
  • ……

MongoDB 适用于哪些场景

1、需要处理大量的低价值数据,且对数据处理性能有较高要求

比如,对微博数据的处理就不需要太高的事务性,但是对数据的存取性能有很高的要求,这时就非常适合使用 MongoDB。

2、需要借助缓存层来处理数据

因为 MongoDB 能高效的处理数据,所以非常适合作为缓存层来使用。将 MongoDB 作为持久化缓存层,可以避免底层存储的资源过载。

3、需要高度的伸缩性

对关系型数据库而言,当表的大小达到一定数量级后,其性能会急剧下降。这时可以使用多台 MongoDB 服务器搭建一个集群环境,实现最大程度的扩展,且不影响性能。

安装 MongoDB

建议参考官方文档中的安装教程:https://docs.mongodb.com/manual/installation/

  • 在 Linux 中安装 MongoDB
    • 方式一:使用 Linux 发行版中的包管理器安装,例如 CentOS Linux 中的 yum、Ubuntu 中的 apt
    • 方式二:下载安装包手动安装
  • 在 macOS 中安装 MongoDB
    • 方式一:使用 Homebrew 包管理器安装管理 MongoDB 服务
    • 方式二:下载安装包手动安装
  • 在 Windows 中安装 MongoDB
    • 方式一:使用 .msi 安装程序快速安装
    • 方式二:下载安装包手动安装

注意事项

  • 关于 MongoDB 的版本号
    • MongoDB 版本形式为 X.Y.Z,例如 4.4.2
    • 如果 Y 是奇数(例如 4.3),则为开发版,建议开发测试使用
    • 如果 Y 是偶数(例如 4.4),则为稳定版,建议生产环境使用
  • 从版本 3.2 之后不再支持 32 位操作系统
  • 课程中使用到的版本是最新稳定版 4.4
    • Windows Server 2019
    • Windows 10 / Windows Server 2016
    • macOS 10.13 及更高版本
    • MongoDB 不支持 WSL(Windows Subsystem for Linux)

安装 MongoDB

这里以 Windows 手动安装 MongoDB 为例。

1、下载 MongoDB 安装包

2、解压压缩包,将解压出来的资源文件放到一个稳定的目录中

3、关于 MongoDB 软件包目录文件
在这里插入图片描述
4、将 MongoDB 安装包中的 bin 目录配置到环境 PATH 变量

配置 PATH 环境变量的目的是为了能够在命令行中的任何位置都能够访问到 bin 目录中的可执行程序。

5、确认是否配置成功

mongod --version
  • 注意:如果是配置环境变量之前打开的命令行,则需要在配置完环境变量之后将命令行重启才能生效。

启动和停止 MongoDB 数据库服务

mongod --dbpath="数据存储目录"
  • mongod 默认监听 127.0.0.1:27017
  • 如果单独执行 mongod,它会默认使用执行 mongod 命令所处磁盘根目录/data/db 作为数据存储目录。

mongo Shell

什么是 mongo Shell

  • mongo Shell 是 MongoDB 官方提供的一个在命令行中用来连接操作 MongoDB 服务的客户端工具
  • 使用 mongo Shell 可以对 MongoDB 数据库进行数据的管理

下载 mongo Shell

  • mongo Shell 包含在 MongoDB 服务器安装中。如果您已经安装了服务器,则 mongo Shell 将安装在与服务器二进制文件相同的位置。

  • 另外,如果您想从 MongoDB 服务器上单独下载 mongo shell,可以参考这里: https://docs.mongodb.com/manual/mongo/#download-the-mongo-shell

启动 mongo Shell 并连接到 MongoDB

  • 连接默认端口上的本地 MongoDB 服务

    mongo
    
  • 连接非默认端口上的本地 MongoDB 服务

    mongo --port 28015
    
  • 连接远程主机上的 MongoDB 服务

    mongo "mongodb://mongodb0.example.com:28015"
    or
    mongo --host mongodb0.example.com:28015
    or
    mongo --host mongodb0.example.com --port 28015
    
  • 连接具有身份认证的 MongoDB 服务

    mongo "mongodb://alice@mongodb0.examples.com:28015/?authSource=admin"
    or
    mongo --username alice --password --authenticationDatabase admin --host mongodb0.examples.com --port 28015
    

mongo Shell 执行环境

  • 提供了 JavaScript 执行环境
  • 内置了一些数据库操作命令
    • show dbs
    • db
    • use database
    • show collections
  • 提供了一大堆的内置 API 用来操作数据库
    • db.users.insert({ name: ‘Jack’, age: 18 })

退出连接

  • exit
  • quit()
  • Ctrc + C

基础操作(CRUD)

创建文档
在这里插入图片描述
查询文档

读取操作从集合中检索文档;即查询集合中的文档。 MongoDB提供了以下方法来从集合中读取文档:

  • db.collection.find(query, projection)
    • query :可选,使用查询操作符指定查询条件
    • projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。
  • db.collection.findOne()

有关示例,请参见:

查询所有文档

db.inventory.find( {} )

等价于 SQL 中的 SELECT * FROM inventory 语句

指定返回的文档字段

db.inventory.find({},{
	item: 1,
	qty: 1
})

相等条件查询

db.inventory.find({ status: "D"})

等价于 SQL 中的 SELECT * FROM inventory WHERE status = “D”

指定 AND 条件

以下示例检索状态为“ A”且数量小于($ lt)30的清单集合中的所有文档:

db.inventory.find({ status: "A", qty: { $lt: 30 }})

该操作对应于以下SQL语句:

SELECT * FROM inventory WHERE status="A" AND qty < 30

指定 OR 条件

db.inventory.find($or: [
	{ status: "A" },
	{ qty: { $lt: 30 } }
} )

SELECT * FROM inventory WHERE status="A" OR qty < 30

指定 AND 和 OR 条件

db.inventory.find({
	status: "A",
	$or: [
		{ qty: { $lt: 30 }},
		{ item: /^p/ }
	]
})

SELECT * FROM inventory WHERE status="A" AND ( qty < 30 OR item LIKE "P%" )

使用查询运算符指定条件

下面的示例从状态为“ A”或“ D”等于“库存”的清单集中检索所有文档:

db.inventory.find({ status: { $in: [ "A", "D" ]} })

SELECT * FROM inventory WHERE status in ("A", "D")

完成的查询运算符参考:https://docs.mongodb.com/manual/reference/operator/query/

查询运算符

参考:https://docs.mongodb.com/manual/reference/operator/query-comparison/
在这里插入图片描述
在这里插入图片描述
查询嵌套文档

  • 整个嵌入式文档上的相等匹配要求与指定的 文档完全匹配,包括字段顺序。
    db.inventory.find({  
    	size: { h: 14, w: 21, uom: "com" }
    })
    

查询嵌套字段

要在嵌入式/嵌套文档中的字段上指定查询条件,请使用点符号 (“field.nestedField”)

  • 在嵌套字段上指定相等匹配

    db.inventory.find({  
    	"size.uom": "in"
    })
    
  • 使用查询运算符指定匹配项

    db.inventory.find({
    	"size.h": { $lt: 15 }
    })
    
  • 指定 AND 条件

    db.inventory.find({
    	"size.h": { $lt: 15 },
    	"size.uom": "in",
    	status: "D"
    })
    

查询数组

  • 匹配一个数组
    // 下面的示例查询所有文档,其中字段标签值是按指定顺序恰好具有两个元素 
    db.inventory.find({
    	tags: ["red", "blank"]
    })
    
    // 相反,如果您希望找到一个同时包含元素的数组,
    // 而不考虑顺序或该数组中的其他元素,请使用 $all  运算符:
    db.inventory.find({
    	tags: { $all: ["red", "blank"] }
    })
    
  • 查询数组中的元素
    // 以下示例查询所有文档,其中  tag  是一个包含字符串  "red"  
    // 作为其元素之一的数组:
    db.inventory.find({
    	tags: "red"
    })
    
    // 例如,以下操作查询数组  包含至少一个值大于 25 的元素的所有文档。
    db.inventory.find({
    	dim_cm: { $lt: 25 }
    })
    
  • 为数组元素指定多个条件
    • 使用数组元素上的复合过滤条件查询数组

      db.inventory.find( { dim_cm: { $gt: 15, $lt: 20 } } )
      
    • 查询满足多个条件的数组元素
      使用 $elemMatch 运算符可以在数组的元素上指定多个条件,以使至少一个数组元素满足所有指定的条件。

      db.inventory.find({
        dim_cm: { $elemMatch: { $gt: 22, $lt: 30 } }
      })
      
    • 通过数组索引位置查询元素

      db.inventory.find( { "dim_cm.1": { $gt: 25 } } )
      
    • 通过数组长度查询数组

      db.inventory.find( { "tags": { $size: 3 } } )
      

更新文档

更新操作会修改集合中的现有文档。 MongoDB 提供了以下方法来更新集合的文档:

删除文档

在 Node.js 中操作 MongoDB

参考:

初始化示例项目

mkdir node-mongodb-demo

cd node-mongodb-demo

npm  init -y

npm install mongodb

连接到 MongoDB

const { MongoClient } = require('mongodb')

const client = new MongoClient('mongodb://127.0.0.1:27017', {
  useUnifiedTopology: true
})

async function run () {
  try {
    // 开始连接
    await client.connect()

    const testDb = client.db('test')
    const inventoryCollection = testDb.collection('inventory')
    
  } catch (err) {
    // 连接失败
    console.log('连接失败', err)
  } finally {
    // 关闭连接
    await client.close()
  }
}

run()

CRUD 操作

  • 创建文档

    • 插入一个:

      const ret = await inventoryCollection.insertOne({
        a: 1,
        b: '2',
        c: true,
        d: [1, 2, 3]
      })
      
      console.log(ret)
      
    • 插入多个:

      const ret = await inventoryCollection.insertOne([{
        a: 1,
        b: '2',
        c: true,
        d: [1, 2, 3]
      },{
        a: 2,
        b: '3',
        c: true,
        d: [1, 2, 3]
      }])
      
      console.log(ret)
      
  • 查询文档

     const ret = await inventoryCollection.findOne({
       item: 'notebook'
     })
    
     // find()  ret.toArray()
     // findOne() ret
     console.log(ret)
     console.log(await ret.toArray())
    
  • 删除文档

    const ret = await inventoryCollection({
      _id: ObjectID('5fa5164f95060000060078b1')
    })
    console.log(ret)
    
  • 修改文档

    const ret = await inventoryCollection.updateOne({
      _id: ObjectID('5fa5164f95060000060078af')
    }, {
      $set: {
        qty: 100
      }
    })
    console.log(ret)
    

MongoDB 数据库结合 Web 服务

在这里插入图片描述

const express = require('express')
const { MongoClient, ObjectID } = require('mongodb')

const connectUri = 'mongodb://localhost:27017'

const dbClient = new MongoClient(connectUri)

const app = express()

// 配置解析请求体数据 application/json
// 它会把解析到的请求体数据放到 req.body 中
// 注意:一定要在使用之前就挂载这个中间件
app.use(express.json())

app.get('/', (req, res) => {
  res.send('Hello World!')
})

app.post('/articles', async (req, res, next) => {
  try {
    // 1. 获取客户端表单数据
    const { article } = req.body

    // 2. 数据验证
    if (!article || !article.title || !article.description || !article.body) {
      return res.status(422).json({
        error: '请求参数不符合规则要求'
      })
    }

    // 3. 把验证通过的数据插入数据库中
    //    成功 -> 发送成功响应
    //    失败 -> 发送失败响应
    await dbClient.connect()

    const collection = dbClient.db('test').collection('articles')

    article.createdAt = new Date()
    article.updatedAt = new Date()
    const ret = await collection.insertOne(article)

    article._id = ret.insertedId
    
    res.status(201).json({
      article
    })
  } catch (err) {
    // 由错误处理中间件统一处理
    next(err)
    // res.status(500).json({
    //   error: err.message
    // })
  }
})

app.get('/articles', async (req, res, next) => {
  try {
    let { _page = 1, _size = 10 } = req.query
    _page = Number.parseInt(_page)
    _size = Number.parseInt(_size)
    await dbClient.connect()
    const collection = dbClient.db('test').collection('articles')
    const ret = await collection
      .find() // 查询数据
      .skip((_page - 1) * _size) // 跳过多少条 10 1 0 2 10 3 20 n
      .limit(_size) // 拿多少条
    const articles = await ret.toArray()
    const articlesCount = await collection.countDocuments()
    res.status(200).json({
      articles,
      articlesCount
    })
  } catch (err) {
    next(err)
  }
})

app.get('/articles/:id', async (req, res, next) => {
  try {
    await dbClient.connect()
    const collection = dbClient.db('test').collection('articles')

    const article = await collection.findOne({
      _id: ObjectID(req.params.id)
    })

    res.status(200).json({
      article
    })
  } catch (err) {
    next(err)
  }
})

app.patch('/articles/:id', async (req, res, next) => {
  try {
    await dbClient.connect()
    const collection = dbClient.db('test').collection('articles')

    await collection.updateOne({
      _id: ObjectID(req.params.id)
    }, {
      $set: req.body.article
    })

    const article = await await collection.findOne({
      _id: ObjectID(req.params.id)
    })

    res.status(201).json({
      article
    })
  } catch (err) {
    next(err)
  }
})

app.delete('/articles/:id', async (req, res, next) => {
  try {
    await dbClient.connect()
    const collection = dbClient.db('test').collection('articles')
    await collection.deleteOne({
      _id: ObjectID(req.params.id)
    })
    res.status(204).json({})
  } catch (err) {
    next(err)
  }
})

// 它之前的所有路由中调用 next(err) 就会进入这里
// 注意:4个参数,缺一不可
app.use((err, req, res, next) => {
  res.status(500).json({
    error: err.message
  })
})

app.listen(3000, () => {
  console.log('app listenning at port 3000.')
})

使用 mongoose 操作 MongoDB

mongoose 是用来操作 MongoDB 数据库的开源 ORM 框架

快速体验

  • 安装:

    npm install mongoose
    
  • 连接数据库:

    const mongoose = require('mongoose');
    mongoose.connect('mongodb://localhost/test', {useNewUrlParser: true, useUnifiedTopology: true});
    
    const db = mongoose.connection;
    db.on('error', console.error.bind(console, '连接失败:'));
    db.once('open', function() {
      // we're connected!
      console.log('连接成功')
    });
    
  • 定义 Schema:

    const kittySchema = new mongoose.Schema({
      name: String
    });
    
  • 将 Schema 发布为 Model:

    const Kitten = mongoose.model('Kitten', kittySchema);
    
  • 使用 Model 操作数据库:

    // 添加
    const fluffy = new Kitten({ name: 'fluffy' });
    fluffy.save().then(...)
    
    // 查询
    Kitten.find().then(...)
    
    // 更新
    Kitten.findOneAndUpdate(
     {}, // 查询条件
     {} // 更新数据
    ).then(...)
    
    // 删除
    Kitten.findOneAndDelete(
      {} // 条件
    ).then(...)
    

Schemas

参考:https://mongoosejs.com/docs/guide.html

SchemaTypes

参考:https://mongoosejs.com/docs/schematypes.html

Connections

参考:https://mongoosejs.com/docs/connections.html

Models

参考:https://mongoosejs.com/docs/models.html

Documents

参考:https://mongoosejs.com/docs/documents.html

Subdocuments

参考:https://mongoosejs.com/docs/subdocs.html

Queries

参考:https://mongoosejs.com/docs/queries.html

Validation

参考:https://mongoosejs.com/docs/queries.html

Populate

参考:https://mongoosejs.com/docs/populate.html

MongoDB 配置

参考官方配置说明:https://docs.mongodb.com/manual/reference/configuration-options/

可视化管理工具

Robo 3T

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值