淘先锋技术网

首页 1 2 3 4 5 6 7

1. MongoDB 相关概念

1.1 业务场景

==传统的关系型数据库 (比如 MySQL), 在数据操作的”三高”需求以及对应的 Web 2.0 网站需求面前, 会有”力不从心”的感觉。==所谓的三高需求:

高并发, 高性能, 高可用, 简称三高

  • High Performance: 对数据库的高并发读写的要求
  • High Storage: 对海量数据的高效率存储和访问的需求
  • High Scalability && High Available: 对数据的高扩展性和高可用性的需求

而 MongoDB 可以应对三高需求

具体的应用场景:

  • 社交场景:使用 MongoDB 存储存储用户信息, 以及用户发表的朋友圈信息, 通过地理位置索引实现附近的人, 地点等功能.
  • 游戏场景:使用 MongoDB 存储游戏用户信息, 用 户的装备, 积分等直接以内嵌文档的形式存储, 方便查询, 高效率存储和访问.
  • 物流场景:使用 MongoDB 存储订单信息, 订单状态在运送过程中会不断更新, 以 MongoDB 内嵌数组的形式来存储, 一次查询就能将订单所有的变更读取出来.
  • 物联网场景:使用 MongoDB 存储所有接入的智能设备信息, 以及设备汇报的日志信息, 并对这些信息进行多维度的分析.
  • 视频直播:使用 MongoDB 存储用户信息, 点赞互动信息等.

这些应用场景中, 数据操作方面的共同点有:

  1. 数据量大
  2. 写入操作频繁
  3. 价值较低的数据, 对事务性要求不高

对于这样的数据, 更适合用 MongoDB 来实现数据存储

那么我们什么时候选择 MongoDB 呢?

除了架构选型上, 除了上述三个特点之外, 还要考虑下面这些问题:

  • 应用不需要事务及复杂 JOIN 支持
  • 新应用, 需求会变, 数据模型无法确定, 想快速迭代开发
  • 应用需要 2000 - 3000 以上的读写QPS(更高也可以)
  • 应用需要 TB 甚至 PB 级别数据存储
  • 应用发展迅速, 需要能快速水平扩展
  • 应用要求存储的数据不丢失
  • 应用需要 99.999% 高可用
  • 应用需要大量的地理位置查询, 文本查询

如果上述有1个符合, 可以考虑 MongoDB, 2个及以上的符合, 选择 MongoDB 绝不会后悔.

如果用MySQL呢?

相对MySQL, 可以以更低的成本解决问题(包括学习, 开发, 运维等成本)

1.2 MongoDB 简介

image-20230227083638827

MongoDB是一个开源, 高性能, 无模式的文档型数据库, 当初的设计就是用于简化开发和方便扩展, 是NoSQL数据库产品中的一种.是最像关系型数据库(MySQL)的非关系型数据库.

它支持的数据结构非常松散, 是一种类似于 JSON 的 格式叫BSON, 所以它既可以存储比较复杂的数据类型, 又相当的灵活.

MongoDB中的记录是一个文档, 它是一个由字段和值对(field:value)组成的数据结构.MongoDB文档类似于JSON对象, 即一个文档认 为就是一个对象.字段的数据类型是字符型, 它的值除了使用基本的一些类型外, 还可以包括其他文档, 普通数组和文档数组.

1.3MongDB和MySQL体系结构对比

image-20230227090136852

image-20230227093642509

MongoDB 数据模型是面向文档的, 所谓文档就是一种类似于 JSON 的结构, 简单理解 MongoDB 这个数据库中存在的是各种各样的 JSON(BSON)

  • 数据库 (database)
    • 数据库是一个仓库, 存储集合 (collection)
  • 集合 (collection)
    • 类似于数组, 在集合中存放文档
  • 文档 (document)
    • 文档型数据库的最小单位, 通常情况, 我们存储和操作的内容都是文档

在 MongoDB 中, 数据库和集合都不需要手动创建, 当我们创建文档时, 如果文档所在的集合或者数据库不存在, 则会自动创建数据库或者集合

1.4 数据模型

image-20230227093629734

1.5 MongoDB 的特点

1.4.1 高性能

MongoDB 提供高性能的数据持久化

  • 嵌入式数据模型的支持减少了数据库系统上的 I/O 活动
  • 索引支持更快的查询, 并且可以包含来自嵌入式文档和数组的键 (文本索引解决搜索的需求, TTL 索引解决历史数据自动过期的需求, 地理位置索引可以用于构件各种 O2O 应用)
  • mmapv1, wiredtiger, mongorocks (rocksdb) in-memory 等多引擎支持满足各种场景需求
  • Gridfs 解决文件存储需求

1.4.2 高可用

MongoDB 的复制工具称作副本集 (replica set) 可以提供自动故障转移和数据冗余

1.4.3 高扩展

水平扩展是其核心功能一部分,分片将数据分布在一组集群的机器上 (海量数据存储, 服务能力水平扩展)

MongoDB 支持基于片键创建数据区域, 在一个平衡的集群当中, MongoDB 将一个区域所覆盖的读写只定向到该区域的那些片

1.4.4 丰富的查询支持

MongoDB支持丰富的查询语言, 支持读和写操作(CRUD), 比如数据聚合, 文本搜索和地理空间查询等.

1.4.5其他特点

无模式(动态模式), 灵活的文档模型

1.6mongoDB存储原理

image-20230227140746011

  1. mongoDb采用内存加磁盘的方式存储数据;
  2. mongoDb支持数据分片,当单一的服务器中磁盘不够用的时候,还可以串联其他服务器;
  3. 客户端的请求到达内存时,先在日志中记录下操作记录,然后再去操作内存;
  4. 内存中的日志每10ms向磁盘中的日志进行同步一次,数据则每分钟同步一次;
  5. 客户端先去内存中查询数据,内存中没有再去查询磁盘;
  6. 当客户端写入的时候,会先写入到内存中,内存中写入后请求直接返回,内存中的数据会根据同步策略同步到磁盘;
  7. 如果机器宕机,在重启服务的时候会解析磁盘中的日志和磁盘中的数据进行对比,将未入到磁盘中的数据写入磁盘,但可能会丢失10ms的数据;

1.6Docker安装MongoDB

注意:MongoDB 默认直接连接,无须身份验证,如果当前机器可以公网访问,且不注意Mongodb 端口(默认 27017)的开放状态,那么Mongodb就会产生安全风险

  1. 拉取镜像
docker pull mongo:latest
  1. 创建mongo数据持久化目录
mkdir -p /mydata/mongodb/data
  1. 运行容器
docker run -it -d --name mongo -v /mydata/mongodb/data:/data/db -p 27017:27017 mongo:latest --auth
  1. -v: 将宿主机的/docker_volume/mongodb/data映射到容器的/data/db目录,将数据持久化到宿主机,以防止删除容器后,容器内的数据丢失
  2. –auth:需要密码才能访问容器服务
  1. 登录mongo容器,并进入到【admin】数据库
docker exec -it mongo mongo admin
# MongoDB 6.0 及以上版本使用以下命令
docker exec -it mongo mongosh admin
  1. 创建一个用户,mongo 默认没有用户
db.createUser({ user:'root',pwd:'123456',roles:[ { role:'userAdminAnyDatabase', db: 'admin'},'readWriteAnyDatabase']});

【user:‘root’ 】:设置用户名为root

【pwd:‘123456’】:设置密码为123456

【role:‘userAdminAnyDatabase’】:只在admin数据库中可用,赋予用户所有数据库的userAdmin权限

【db: ‘admin’】:可操作的数据库

【‘readWriteAnyDatabase’】:赋予用户读写权限

  1. 使用命令连接数据库
db.auth('root', '123456')
  1. 测试插入数据
 db.user.insert({"name":"zhangsan","age":18})

image-20230227123236800

  1. 查询刚才插入的语句
 db.user.find()

image-20230227103336658

  1. navicat连接测试
image-20230227123556339

2. 基本常用命令

image-20230227140357307

2.1 数据库操作

mongodb默认保留的数据库

  • admin: 从权限角度考虑, 这是 root 数据库, 如果将一个用户添加到这个数据库, 这个用户自动继承所有数据库的权限, 一些特定的服务器端命令也只能从这个数据库运行, 比如列出所有的数据库或者关闭服务器
  • local: 数据永远不会被复制, 可以用来存储限于本地的单台服务器的集合 (部署集群, 分片等)
  • config: Mongo 用于分片设置时, config 数据库在内部使用, 用来保存分片的相关信息

2.1.1选择和创建数据库

  1. 选择和创建数据库的语法格式:
use 数据库名称

如果数据库不存在则自动创建,例如,以下语句创建 articledb数据库:

use articledb

2.1.2查看有权限查看的所有的数据库命令

show dbs
或
show databases

​ 注意: 在 MongoDB 中,集合只有在内容插入后才会创建! 就是说,创建集合(数据表)后要再插入一个文档(记录),集合才会真正创建。

  1. 查看当前正在使用的数据库命令
db

2.1.3数据库的删除

  • MongoDB 删除数据库的语法格式如下:
db.dropDatabase()
或
db.数据库名.drop()

2.2集合操作

# 查看集合
show collections
# 创建集合
db.createCollection("user_collection")
# 删除集合
db.user_collection.drop()

创建集合语法扩展
db.createCollection(name,options)
适用场景: 生产者消费者. 日志覆写

字段类型options字段描述
cappedboolean(可选)如果为tue,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档.
sizenumber(可选) 为固定集合指定一个最大值(字节单位),如果capped为true,也需要指定该字段
maxnumber(可选) 指定固定集合种包含文档的最大数量

2.3 插入操作

/*插入一条数据*/
db.user.insertOne({"name":"张三","age":"21"})
/*插入多条数据*/
db.book.insertMany([{"bookName":"哈利波特"},
    {"bookName":"风"}])

/*执行js脚本*/
load("book.js")

2.4 查询操作

语法格式: db.collection.find(query,projection)

  • query 可选,使用查询操作符指定查询条件
  • projection 可选,使用投影操作符指定返回的键。查询时返回文档中所有键值,只需省略该参数即可(默认省略)。投影时,id为1的时候,其他字段必须是1;id是0的时候,其他字段可以是0;如果没有_id字段约束,多个其他字段必须同为0或同为1。

条件查询
在这里插入图片描述
在这里插入图片描述

/*插入一条数据*/
db.user.insertOne({"name":"张三","age":"21"})
/*插入多条数据*/
db.book.insertMany([
    {"bookName":"哈利波特","id":"1","type":"魔法"},
    {"bookName":"风","id":"2","type":"魔法"},
    {"bookName":"爱","id":"3","type":"爱情"}])
/*查询数据*/
db.user.find()
db.book.find()
db.user.find({"age":"21"})
db.book.find({"type":"魔法"})
db.book.findOne({"type":"魔法"})
// 上面定义的是字符串类型,不能用数值. 查找包含id 1 2 3 的  
db.book.find({"id":{$in:["1","2","3"]}})
// 上面定义的是字符串类型,不能用数值. 查找id小于等于2的  
db.book.find({"id":{$lte:"2"}})
// 根据id降序 
db.book.find().sort({"id": -1})
// 正则表达式查询 
db.book.find({"type":{$regex:"魔"}})
// 分页查询 skip跳过的数据条数  limit限制返回条数
db.book.find().skip((currentPage-1)*size).limit(size)

2.5更新文档

语法格式: db.collection.update(query,update,options)

  • query:描述更新的查询条件
  • update:描述更新的动作及新的内容;
  • options:描述更新的选项
    • upsert:可选,如果不存在update的记录,是否插入新的记录。默认false,不插入
    • multi:可选,是否按条件查询出的多条记录全部更新。默认false,只更新找到的第
      条记录
    • writeConcern :可选,决定一个写操作落到多少个节点上才算成功。

在这里插入图片描述

/*插入多条数据*/
db.book.insertMany([
    {"bookName":"哈利波特","id":1,"type":"魔法","number":100},
    {"bookName":"风","id":2,"type":"魔法","number":1024},
    {"bookName":"爱","id":3,"type":"爱情","number":100}])
// 找到id为1的数据.让number属性自增1   第一个参数为查询条件,第二个参数为修改内容
db.book.updateOne({"id":1},{$inc:{"number":1}})
db.book.find({"id":1})
// 更新一条新值
db.book.updateOne({"id":2},{$set:{"bookName":"西游记"}})
db.book.find({"id":2})

在这里插入图片描述


在这里插入图片描述
repalace全文档替换.一条数据全部被覆盖


在这里插入图片描述
默认情况下,findAndModify会返回修改前的“旧”数据。如果希望返回修改后的数据,则可以指定new选

在这里插入图片描述
修改嵌套数据:

db.students.updateOne({
    "name": "李慧英"
}, {
    $set: {
        "score.english": 88
    }
})

修改数据数据

//将张力的书架上的第二本书修改为“C#”
db.students.updateOne({
    "name": "张力"
}, {
    $set: {
        "books.1": "C#"
    }
})

2.6删除文档

  • remove 命令需要配合查询条件使用;
  • 匹配查询条件的文档会被删除;
  • 指定一个空文档条件会删除所有文档;
  • 推荐db.book.deleteOne(),db.book.deleteMany()
db.book.deleteOne({"id":2})
db.book.deleteMany({"type":"魔法"})
db.book.deleteMany({"id":{$lt:2}}) // 删除id小于2的
db.book.deleteMany({}) // 删除所有行记录
db.book.deleteMany() //报错

2.7文档聚合

在这里插入图片描述

#检索books集合中所有文档的计数 
# db.collection.estimatedDocumentCount() 不使用查询过滤器,而是使用元数据返回集合的计数。
db.books.estimatedDocumentCount()
#计算与查询匹配的所有文档
#它执行文档的聚合以返回准确的计数,即使在异常关闭后或分片群集中存在孤立的文档之后也是如此。
db.books.countDocumentCount({favCount:{$gt:50}})
#返回不同type的数组
db.books.distinct("type")
#返回收藏数大于90的文档不同type的数组
db.books.distinct("type",{favCount:{$gt:90}})

3.SpringBoot集成Mongodb

  1. 引入pom依赖
<!--spring data mongodb-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
spring:
	data:
		mongodb:
			database: test
			host: localhost
			port: 27017
			username: admin
			password: 123456
			authentication-database: admin
			#uri等同于下面的配置
			uri: mongodb://admin:123456@localhost:27017/test?authSource=admin

使用时注入mongoTemplate

mongodbTemplate 官网api

@Autowired
MongoTemplate mongoTemplate;

@Test
public void testCollection(){
boolean exists = mongoTemplate.collectionExists("emp");
if (exists) {
//删除集合
mongoTemplate.dropCollection("emp");
}
//创建集合
mongoTemplate.createCollection("emp");
}