★: 了解、可以明确描述内容
★★: 熟悉、工作中经常会用到
★★★: 必背、面试中经常会问到
- MongoDB 是一个基于分布式文件存储的NoSQL数据库。
- 由C++语言编写,运行稳定,性能高,旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。
- MongoDB是为快速开发互联网web应用而设计的数据库系统。
- MongoDB的设计目标是极简单、灵活、作为Web应用栈的一部分。
- MongoDB的数据模型是面向文档的(BSON),所谓文档是一种类似JSON的结构,简单理解MongoDB这个数据库中存在的是各种各样的JSON。
- MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
- 为什么选择MongoDB
- 游戏场景,使用 MongoDB 存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、更新
- 物流场景,使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。
- 社交场景,使用 MongoDB 存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能
- 物联网场景,使用 MongoDB 存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析
- 视频直播,使用 MongoDB 存储用户信息、礼物信息等
- ......
- 什么情况下该选择MongoDB
应用特征 | Yes / No |
应用不需要事务及复杂 join 支持 | 必须 Yes |
新应用,需求会变,数据模型无法确定,想快速迭代开发 | ? |
应用需要2000-3000以上的读写QPS(更高也可以) | ? |
应用需要TB甚至 PB 级别数据存储 | ? |
应用发展迅速,需要能快速水平扩展 | ? |
应用要求存储的数据不丢失 | ? |
应用需要99.999%高可用 | ? |
应用需要大量的地理位置查询、文本查询 | ? |
-
- 都有哪些公司在使用MongoDB
jiaxing@atguigu:~$ sudo apt install mongodb
- 进入MongoDB Shell
jiaxing@atguigu:~$ mongo
- 直接启动mongodb服务端
mongod
- 开启MongoDB服务
jiaxing@atguigu:~$ sudo service mongodb start
- 停止MongoDB服务
jiaxing@atguigu:~$ sudo service mongodb stop
- 重启MongoDB服务
jiaxing@atguigu:~$ sudo service mongodb restart
E: 无法获得锁 /var/lib/dpkg/lock - open (11: Resource temporarily unavailable)
E: 无法锁定管理目录(/var/lib/dpkg/),是否有其他进程正占用它?
解决方法一:
jiaxing@atguigu:~$ ps -aux | grep apt
root 5765 0.0 1.0 18204 15504 ? SN 04:02 0:00 apt-get -qq -d
找到最后一列以apt-get 开头的进程
jiaxing@atguigu:~$ sudo kill -9 该进程的PID
解决方法二:
sudo rm /var/cache/apt/archives/lock
sudo rm /var/lib/dpkg/lock
-
- 基本概念
SQL术语/概念 | MongoDB术语/概念 | 解释/说明 |
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
column | field | 数据字段/域 |
index | index | 索引 |
table joins |
| 表连接,MongoDB不支持 |
primary key | primary key | 主键,MongoDB自动将_id字段设置为主键 |
- Windows版本文档
- Ubuntu版本文档
- 进入MongoDB Shell
jiaxing@atguigu:~$ mongo
- 查看当前数据库名称(默认test)
> db
- 查看当前数据库状态
> db.stats()
- 查看mongodb命令
> help
- 查看所有数据库
> show dbs
- 数据库切换(选择)/ 创建数据库(创建集合后才会创建数据库)
> use sgg
- 创建集合
> db.createCollection('user')
- 删除集合
db.user.drop()
- 删除数据库
> use sgg # 切换到要删除的数据库
> db.dropDatabase() # 使用命令删除数据库
- 创建集合 db.createCollection(name, options)
> db.createCollcetion(‘user’)
- 查看当前数据库中所有集合
> show collections
> show tables # mongodb文档中是show collections 但show tables有相同效果
- 插入一条数据
> db.user.insert({key:’value’})
# 插入数据时,key可以不加引号,但value必须有引号
- 查看集合中所有数据
> db.user.find()
- Options可选参数
字段 | 类型 | 描述 |
capped | 布尔 | 如果为 true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。 当该值为 true 时,必须指定 size 参数。 |
size | 数值 | 为固定集合指定一个最大值(以KB字节计)。 如果 capped 为 true,也需要指定该字段。 |
max | 数值 | 指定固定集合中包含文档的最大数量。 |
- Options使用案例: 集合名称为user,限制集合大小为1KB,最多有2条数据
> db.createCollcetion(‘user’, {capped:true, size:1, max:2})
- 删除集合
> db.user.drop()
- MongoDB数据类型 ( 插入数据时mongodb会自动检测其类型 可以使用$type查看类型 )
Type | Number | Alias | Notes |
Double | 1 | “double” | 存储浮点值 |
String | 2 | “string” | 字符串,最常用,必须是有效的UTF-8 |
Object | 3 | “object” | 用于嵌入式的文档,即一个值为一个文档 |
Array | 4 | “array” | 数组或列表,多个值存储到一个键 |
ObjectId | 7 | “objectId” | 文档ID,一般系统维护,自己也可以维护 |
Boolean | 8 | “bool” | 存储一个布尔值,true或false |
Date | 9 | “date” | 存储当前日期或时间的UNIX时间格式 |
Null | 10 | “null” | 存储Null值 |
- ObjectId
> db.user.insertOne( { key : value } )
- 插入多条数据 # 接收一个列表
> db.user.insertMany( [ { key1 : value1 }, { key2 : value2 } ] )
在2.6版本中,插入单条数据为
db.user.insert( { key : value } )
插入多条数据为:
db.user.insert( [ { key:value } , { key:value } ] )
- 更新单条数据 # 更新找到的第一条数据
> db.user.updateOne( { key : value }, {参数: { key : value }} )
- $参数说明:
- $set 仅修改设置的字段,如字段不存在,给数据增加字段
- $unset 仅修改设置的字段,如字段不存在,不增加新字段
- $rename 修改字段名,格式 {$rename:{‘原名称’:’新名称’}}
- $min 仅在修改的值小于当前值时可以被修改
- $max 仅在修改的值大于当前值时可以被修改
- $inc 给匹配到的数据的字段增加指定值, 格式{ $inc : { age : 11 } }
- 更新多条数据
> db.user.updateMany( { key : value }, {$参数: { key : value }})
更新单条数据
db.user.update( { key:value } , { $set:{ key:newvalue } } )
更新多条数据
db.user.update( { key:value } , { $set:{ key:newvalue } } , { multi: true })
- 删除匹配到keyvalue的单条数据 # 删除找到的第一条数据
> db.user.deleteOne( { key : value } )
- 删除匹配到keyvalue的所有数据
> db.user.deleteMany( { key : value } )
- 删除所有数据 # 对设置了capped的集合无效
> db.user.deleteMany( { } )
删除单条数据
db.user.remove( {key:value} , 1 )
删除多条数据
db.user.remove( { key:value } )
> db.user.find ({key:value}, projection )
- Projection参数说明
Projection可以指定显示或不显示某些字段, _id键默认返回
> db.user.find({key:value}, {title: 1, by: 1}) // 包含模式 指定返回的键,不返回其他键
> db.user.find({key:value}, {title: 0, by: 0}) // 排除模式 指定不返回的键,返回其他键
> db.user.find({key:value}, {_id:0, title: 1, by: 1}) // 可以设置不显示_id
- 查询符合条件的第一条数据
> db.user.findOne ( {key:value}, projection )
- 查询集合中所有数据
> db.user.find()
> db.user.find({})
- 更美观的查看数据
> db.user.find ( {key:value}, projection ).pretty()
- 准备数据
> for(i=20;i<26;i++){db.user.insert({city:'beijing',age:i})}
> for(i=20;i<26;i++){db.user.insert({city:'shanghai',age:i})}
操作 | 格式 | 范例 |
等于 | { key :{$eq: value }} / { key: value } | > db.user.find({age:{$eq:24}}) |
小于 | { key :{$lt: value }} | > db.user.find({age:{$lt:24}}) |
小于或等于 | { key :{$lte: value }} | > db.user.find({age:{$lte: 24}}) |
大于 | { key :{$gt: value }} | > db.user.find({age:{$gt: 24}}) |
大于或等于 | { key :{$gte: value }} | > db.user.find({age:{$gte: 24}}) |
不等于 | { key :{$ne: value }} | > db.user.find({age:{$ne: 24}}) |
操作 | 格式 | 范例 |
AND | { key1 : value1 , key2 : value2 } | > db.user.find({city:"beijing", age:23}) |
OR | {$or:[{ key1 :value1}, {key2:value2}]} | > db.user.find({$or:[{ city:"beijing"},{age:23}]}) |
操作 | 格式 | 范例 |
包含 | { key :{$in:[value1,value2]}} | > db.user.find({ age:{$in:[22,23,24]}) |
不包含 | { key :{$nin:[value1,value2]}} | > db.user.find({ age:{$nin:[22,23,24]}) |
操作 | 格式 | 范例 |
限制 | limit(int) | > db.user.find().limit(2) |
跳过(偏移) | skip(int) | > db.user.find().skip(2) |
混合使用 | limit(int).skip(int) | > db.user.find().skip(2).limit(2) |
排序 | sort({key:type}) # 1正 -1 逆 | > db.user.find().sort({age:1}) |
混合使用2 | limit(int).skip(int).sort({key:type}) | 执行顺序: sort -> skip -> limit |
MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似sql语句中的 count(*)
- 案例实操
- $sum # 根据city分组并计算age总和
> db.user.aggregate([{$group:{_id:'$city', sum_age:{$sum:'$age'}}}])
- $avg # 根据city分组并计算age平均值
> db.user.aggregate([{$group:{_id:'$city', avg_age:{$avg:'$age'}}}])
- $min # 根据city分组并计算age最小值
> db.user.aggregate([{$group:{_id:'$city', min_age:{$min:'$age'}}}])
- $max # 根据city分组并计算age最大值
> db.user.aggregate([{$group:{_id:'$city', max_age:{$max:'$age'}}}])
- $push # 根据city分组并将age的数据放入到一个数组中
> db.user.aggregate([{$group:{_id:'$city', age:{$push:'$age'}}}])
- $addToSet # 根据city分组并将age的数据放入到一个数组中,但没有重复数据,类似集合
> db.user.aggregate([{$group:{_id:'$city', age:{$addToSet:'$age'}}}])
- $first # 根据city分组并得到第一个age值
> db.user.aggregate([{$group:{_id:'$city', age:{$first:'$age'}}}])
- $last # 根据city分组并得到最后一个age值
> db.user.aggregate([{$group:{_id:'$city', age:{$last:'$age'}}}])
管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的参数。
MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。
db.user.aggregate([{$group:{_id:'$city', age:{$addToSet:'$age'}}}, {$project:{_id:0}}])
db.user.aggregate([{$match:{age:{$in:[22,23,24]}}},{$group:{_id:'$city', age:{$addToSet:'$age'}}}, {$project:{_id:0}}])
管道操作符 | 描述 | 实例 |
$project | 用于选择显示哪些域,类似于find中的包含模式和排除模式。 | > db.user.aggregate([{$project:{_id:0, city:1, age:1}}]) |
$match | 用于过滤文档。用法类似于 find() 方法中的参数。 | > db.user.aggregate([{$match:{age:{$gte:23}}}]) |
$limit | 限制返回的文档数量 | > db.user.aggregate([{$limit:3}]) |
$skip | 跳过指定数量的文档,并返回余下的文档。 | > db.user.aggregate([{$skip:3}]) |
$unwind | 将文档中数组类型的字段拆分成多条,每条文档包含数组中的一个值。 | > db.user.insert({size:['S','M','L']}) > db.user.aggregate({$unwind:'$size'}) |
$group | 将集合中的文档进行分组,可用于统计结果。 | > db.user.aggregate([{$group:{_id:'$city', avg_age:{$avg:'$age'}}}]) |
$sort | 将集合中的文档进行排序。 | > db.user.aggregate([{$sort:{age:1}}]) |
索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。
这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构
-
-
- 创建数据及查询性能分析
-
- 创建数据
> for(i=1;i<=100000;i++){db.test.insert({name:'guigu'+i, age:i})}
- 查询数据并查看性能分析 "executionTimeMillis" : 41, 单位为毫秒
> db.test.find({age:5000}).explain('executionStats')
- 创建索引之后再次查看性能分析 "executionTimeMillis" : 6, 单位为毫秒
> db.test.find({age:5000}).explain('executionStats')
- 普通方式创建索引 1表示升序; -1表示降序
> db.test.createIndex({age:1})
- 创建索引并指定索引名
> db.test.createIndex({age:1},{name:’age_index’})
- 查看所有索引
> db.test.getIndexes()
- 查看所有索引键
> db.test.getIndexKeys()
- 删除指定索引
> db.test.dropIndex('<IndexName>')
- 删除所有索引
> db.test.dropIndexes()
复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性,并可以保证数据的安全性,复制还允许从硬件故障和服务中断中恢复数据
- 保障数据的安全性
- 数据高可用性 (24*7)
- 灾难恢复
- 无需停机维护(如备份,重建索引,压缩)
- 分布式读取数据
mongodb的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。
mongodb各个节点常见的搭配方式为:一主一从、一主多从。
主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。
- N 个节点的集群
- 任何节点可作为主节点
- 所有写入操作都在主节点上
- 自动故障转移
- 自动恢复
- 创建数据库目录 mkdir <path name>
jiaxing@atguigu:~$ mkdir mongo1 mongo2
- 启动服务器 mongod --port <Port> --dbpath <Data Path> --replSet <Replication Name>
jiaxing@atguigu:~$ mongod --port 27021 --dbpath ~/mongo1 --replSet rs1
jiaxing@atguigu:~$ mongod --port 27022 --dbpath ~/mongo2 --replSet rs1
- 启动客户端 mongo --port <Port>
jiaxing@atguigu:~$ mongo --port 27021
jiaxing@atguigu:~$ mongo --port 27022
- 查看副本集状态
> rs.status()
- 副本集初始化
> rs.initiate()
- 副本集添加成员 rs.add(HOST_NAME:PORT)
> rs.add('localhost:27022')
- 从数据库获取数据 ( 在27022客户端操作 )
rs1:SECONDARY> rs.slaveOk()
- 主机添加数据
rs1:PRIMARY> use py3
rs1:PRIMARY> db.user.insert({name:'tom'})
- 查看从机数据
rs1:SECONDARY> use py3
rs1:SECONDARY> db.user.find()
- 演示宕机后从机自动升为主机
使用Ctrl+C或者kill关闭主机27021服务端
重启27021服务端,发现27021显示为rs1:SECONDARY>
在27022输入任意一条命令, 发现已经变更为rs1:PRIMARY>
此时从机需要读取数据需要重新输入 rs.slaveOk()
- 删除从机节点 rs.remove(HOST_NAME:PORT)
需要先输入rs.status()命令查看当前副本集信息
rs1:PRIMARY>> rs.status()
rs1:PRIMARY>> rs.remove('localhost:27022')
在MongoDB中我们使用mongodump命令来备份MongoDB数据。该命令可以导出所有数据到指定目录中。
mongodump命令可以通过参数指定导出的数据量级转存的服务器。
- 标准语法
mongodump -h dbhost -d dbname -o dbdirectory
-h:
MongDB所在服务器地址,可以指定端口号:127.0.0.1:27017
-d:
需要备份的数据库实例名称,例如:py3 如果不指定 则备份所有数据库
-o:
备份的数据存放位置,例如:/home/jiaxing/mongobak1,当然该目录需要提前建立,在备份完成后,系统自动在dump目录下建立一个数据库同名目录,这个目录里面存放该数据库实例的备份数据。如不指定-o 则在运行目录下自动创建dump文件夹
- 案例实操
- 备份整个数据库到家目录的mongobak1文件夹中
jiaxing@atguigu:~$ mongodump -o ~/mongobak1
- 备份py3数据库中user集合到mongobak2文件夹中
jiaxing@atguigu:~$ mongodump --collection user --db py3 -o ~/mongobak2
mongorestore --host <hostname:port> --drop <dbname> --dir <filepath>
-h <hostname:port>
MongoDB所在服务器地址,默认为: localhost:27017
-d
指定数据库恢复之后新的名字,与直接恢复不同, -d 新数据库名 --dir必须选择到BSON文件
--drop:
恢复的时候,先删除当前数据,然后恢复备份的数据。就是说,恢复后,备份后添加修改的数据都会被删除,慎用!
# 只针对备份文件中有的BSON备份, 例如备份前有两个集合 user 和 stu 备份文件中只有user集合,那么备份后是先将user集合中数据删除再进行恢复,备份文件中没有的stu集合不受任何影响
--dir
指定备份的目录,或者可以不使用 –-dir 直接在命令末尾处填写恢复文件所在的文件夹路径
- 案例实操
- 从mongobak1恢复数据库
jiaxing@atguigu:~$ mongorestore --dir /home/jiaxing/mongobak1
- 从mongobak2恢复数据库并使用--drop选项
jiaxing@atguigu:~$ mongorestore --dir /home/jiaxing/mongobak2 --drop