1. MongoDB 相关概念
1.1 业务场景
==传统的关系型数据库 (比如 MySQL), 在数据操作的”三高”需求以及对应的 Web 2.0 网站需求面前, 会有”力不从心”的感觉。==所谓的三高需求:
高并发, 高性能, 高可用, 简称三高
- High Performance: 对数据库的高并发读写的要求
- High Storage: 对海量数据的高效率存储和访问的需求
- High Scalability && High Available: 对数据的高扩展性和高可用性的需求
而 MongoDB 可以应对三高需求
具体的应用场景:
- 社交场景:使用 MongoDB 存储存储用户信息, 以及用户发表的朋友圈信息, 通过地理位置索引实现附近的人, 地点等功能.
- 游戏场景:使用 MongoDB 存储游戏用户信息, 用 户的装备, 积分等直接以内嵌文档的形式存储, 方便查询, 高效率存储和访问.
- 物流场景:使用 MongoDB 存储订单信息, 订单状态在运送过程中会不断更新, 以 MongoDB 内嵌数组的形式来存储, 一次查询就能将订单所有的变更读取出来.
- 物联网场景:使用 MongoDB 存储所有接入的智能设备信息, 以及设备汇报的日志信息, 并对这些信息进行多维度的分析.
- 视频直播:使用 MongoDB 存储用户信息, 点赞互动信息等.
这些应用场景中, 数据操作方面的共同点有:
- 数据量大
- 写入操作频繁
- 价值较低的数据, 对事务性要求不高
对于这样的数据, 更适合用 MongoDB 来实现数据存储
那么我们什么时候选择 MongoDB 呢?
除了架构选型上, 除了上述三个特点之外, 还要考虑下面这些问题:
- 应用不需要事务及复杂 JOIN 支持
- 新应用, 需求会变, 数据模型无法确定, 想快速迭代开发
- 应用需要 2000 - 3000 以上的读写QPS(更高也可以)
- 应用需要 TB 甚至 PB 级别数据存储
- 应用发展迅速, 需要能快速水平扩展
- 应用要求存储的数据不丢失
- 应用需要
99.999%
高可用 - 应用需要大量的地理位置查询, 文本查询
如果上述有1个符合, 可以考虑 MongoDB, 2个及以上的符合, 选择 MongoDB 绝不会后悔.
如果用MySQL呢?
相对MySQL, 可以以更低的成本解决问题(包括学习, 开发, 运维等成本)
1.2 MongoDB 简介
MongoDB是一个开源, 高性能, 无模式的文档型数据库, 当初的设计就是用于简化开发和方便扩展, 是NoSQL数据库产品中的一种.是最像关系型数据库(MySQL)的非关系型数据库.
它支持的数据结构非常松散, 是一种类似于 JSON 的 格式叫BSON, 所以它既可以存储比较复杂的数据类型, 又相当的灵活.
MongoDB中的记录是一个文档, 它是一个由字段和值对(field:value)组成的数据结构.MongoDB文档类似于JSON对象, 即一个文档认 为就是一个对象.字段的数据类型是字符型, 它的值除了使用基本的一些类型外, 还可以包括其他文档, 普通数组和文档数组.
1.3MongDB和MySQL体系结构对比
MongoDB 数据模型是面向文档的, 所谓文档就是一种类似于 JSON 的结构, 简单理解 MongoDB 这个数据库中存在的是各种各样的 JSON(BSON)
- 数据库 (database)
- 数据库是一个仓库, 存储集合 (collection)
- 集合 (collection)
- 类似于数组, 在集合中存放文档
- 文档 (document)
- 文档型数据库的最小单位, 通常情况, 我们存储和操作的内容都是文档
在 MongoDB 中, 数据库和集合都不需要手动创建, 当我们创建文档时, 如果文档所在的集合或者数据库不存在, 则会自动创建数据库或者集合
1.4 数据模型
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存储原理
- mongoDb采用内存加磁盘的方式存储数据;
- mongoDb支持数据分片,当单一的服务器中磁盘不够用的时候,还可以串联其他服务器;
- 客户端的请求到达内存时,先在日志中记录下操作记录,然后再去操作内存;
- 内存中的日志每10ms向磁盘中的日志进行同步一次,数据则每分钟同步一次;
- 客户端先去内存中查询数据,内存中没有再去查询磁盘;
- 当客户端写入的时候,会先写入到内存中,内存中写入后请求直接返回,内存中的数据会根据同步策略同步到磁盘;
- 如果机器宕机,在重启服务的时候会解析磁盘中的日志和磁盘中的数据进行对比,将未入到磁盘中的数据写入磁盘,但可能会丢失10ms的数据;
1.6Docker安装MongoDB
注意:MongoDB 默认直接连接,无须身份验证,如果当前机器可以公网访问,且不注意Mongodb 端口(默认 27017)的开放状态,那么Mongodb就会产生安全风险
- 拉取镜像
docker pull mongo:latest
- 创建mongo数据持久化目录
mkdir -p /mydata/mongodb/data
- 运行容器
docker run -it -d --name mongo -v /mydata/mongodb/data:/data/db -p 27017:27017 mongo:latest --auth
- -v: 将宿主机的/docker_volume/mongodb/data映射到容器的/data/db目录,将数据持久化到宿主机,以防止删除容器后,容器内的数据丢失
- –auth:需要密码才能访问容器服务
- 登录mongo容器,并进入到【admin】数据库
docker exec -it mongo mongo admin
# MongoDB 6.0 及以上版本使用以下命令
docker exec -it mongo mongosh admin
- 创建一个用户,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’】:赋予用户读写权限
- 使用命令连接数据库
db.auth('root', '123456')
- 测试插入数据
db.user.insert({"name":"zhangsan","age":18})
- 查询刚才插入的语句
db.user.find()
- navicat连接测试
2. 基本常用命令
2.1 数据库操作
mongodb默认保留的数据库
- admin: 从权限角度考虑, 这是
root
数据库, 如果将一个用户添加到这个数据库, 这个用户自动继承所有数据库的权限, 一些特定的服务器端命令也只能从这个数据库运行, 比如列出所有的数据库或者关闭服务器 - local: 数据永远不会被复制, 可以用来存储限于本地的单台服务器的集合 (部署集群, 分片等)
- config: Mongo 用于分片设置时,
config
数据库在内部使用, 用来保存分片的相关信息
2.1.1选择和创建数据库
- 选择和创建数据库的语法格式:
use 数据库名称
如果数据库不存在则自动创建,例如,以下语句创建 articledb数据库:
use articledb
2.1.2查看有权限查看的所有的数据库命令
show dbs
或
show databases
注意: 在 MongoDB 中,集合只有在内容插入后才会创建! 就是说,创建集合(数据表)后要再插入一个文档(记录),集合才会真正创建。
- 查看当前正在使用的数据库命令
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字段描述 |
---|---|---|
capped | boolean | (可选)如果为tue,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档. |
size | number | (可选) 为固定集合指定一个最大值(字节单位),如果capped为true,也需要指定该字段 |
max | number | (可选) 指定固定集合种包含文档的最大数量 |
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
- 引入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
@Autowired
MongoTemplate mongoTemplate;
@Test
public void testCollection(){
boolean exists = mongoTemplate.collectionExists("emp");
if (exists) {
//删除集合
mongoTemplate.dropCollection("emp");
}
//创建集合
mongoTemplate.createCollection("emp");
}