概念
- 基于分布式文件存储数据库
- C++语言编写
- 支持的数据结构非常松散,时类似json的bson格式(后期插入修改数据写json)
安装
https://www.cnblogs.com/tjp40922/p/11870321.html
基本操作
show databases或者show dbs #查看数据库 默认有三个数据库
db #展示当前使用的数据库
use 数据库名称 #创建并选择数据库
db.dropDatabase() #删除数据库 删除之前首先要选择要被删除的数据库
show collections #查看集合
db.createCollection('集合名称') #显式创建集合
#集合也会有隐式创建,隐式创建实在插入数据时直接使用要创建的集合名称即可
db.集合名称.drop() #删除集合
隐式创建:当选择不存在的数据库时,不会报错,后期该数据库有数据时系统会自动创建该数据库;数据库和集合都存在隐式创建。
数据库命名规则是满足以下条件的任意UTF-8字符串:
- 不能是空字符串""
- 不得含有空格、英文句号、$、/、\和空字符(\0)
- 应全部小写
- 最多64字节
关于默认的三个数据库:
- admin:从权限的角度来看,就是root数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
- local:这个库的数据永远不会被复制,可以用来存储本地单台服务器的任意集合
- config:当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息
文档CRUD
插入文档
首先了解:
- 集合存在,则直接插入;集合不存在,则隐式创建。
- 文档(document)的数据结构和json基本一样,所有存储在集合中的数据都是BSON格式。
- _id:这个东西式mongodb自己就会生成的东西,相当于它的主键,我们也可以自己定义。
先了解一下参数,后面两种(writeConcern
和ordered
)了解即可。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OA1nXDIU-1619231866146)(C:\Users\XQL\AppData\Roaming\Typora\typora-user-images\1619171140305.png)]
语法:db.集合名称.insert(json数据)
注意:
- 对象的键(json数据的键)统一不加引号,但是查看集合数据时系统会自动添加
- mongodb会自动给每条数据增加一个全球唯一的"_id"键;_id键可以自己定义,只需要在json数据中定义_id键即可覆盖(但是实战中强烈不推荐)
语法:db.collectionName.insert(json型数据)
或者db.collectionName.save(json型数据)
#创建并选择数据库test
use test
#创建collection集合
db.createCollection("c")
#插入一条数据
db.c.insert({uname:"张三",age:"20"}) 或者 db.c.save({uname:"张三",age:"20"})
#插入多条数据
db.c.insert([ #用数组封装json数据
{uname:"张三",age:"20"},
{uname:"李四",age:"21"},
{uname:"王五",age:"22"}
]) #或者
db.c.insertMany([
{uname:"张三",age:"20"},
{uname:"李四",age:"21"},
{uname:"王五",age:"22"}
])
#一次性插入千条数据
#mongodb底层使用js引擎实现,所以部分支持js语法,因此可以写for循环
for(var i = 1;i<=10;i++){
db.c.insert({uname:"a"+i,age:i})
}
# 执行该操作后,会提示有一条数据插入成功,原因是for循环时一条一条的插入,前9条插入数据的提示是看不到的,只有最后一条可以看到
#在Java项目中,当我们进行批量插入的操作时,要对该操作使用try catch进行异常捕获
#mongodb4.0支持事务,但是貌似也不好用
查询文档
基本查询
基础语法:db.集合名称.find(条件 [查询的列])
#查询全部数据
db.c.find() 或者 db,c.find({})
#查询age=6的数据
db.c.find({age:6})
#查询age=6且性别为男的数据
db.c.find({age:6.sex:"男"})
#只查询一条age=6的数据
db.c.findOne({age:6})
#查询全部数据,但是数据只显示age字段
db.c.find({},{age:1})
#查询全部数据,但是数据只显示age之外的字段
db.c.find({},{age:0})
#查询age=6的数据,且只显示age字段
db.c.find({age:6},{age:1})
比较查询
运算 | 作用 |
---|---|
$gt | 大于 |
$gte | 大于等于 |
$lt | 小于 |
$lte | 小于等于 |
$ne | 不等于 |
$in | in |
$nin | not in |
#查询年龄大于5岁的数据
db.c1.find({age:{$gt:5}})
#查询年龄是5岁、8岁、10岁的数据
db.c1.find({age:{$in:[5,8,10]}})
如果查出来的数据很多,不方便看,可以将数据的格式 格式化一下,语法:db.集合名.find().pretty()
数据准备
#为下面的统计、条件连接、正则、排序、分页查询做数据准备
use test
db.dropDatabase()
use test
db.createCollection("c")
db.c.insert({_id:1,name:"a",sex:1,age:1})
db.c.insert({_id:2,name:"a",sex:1,age:2})
db.c.insert({_id:3,name:"b",sex:2,age:3})
db.c.insert({_id:4,name:"c",sex:2,age:4})
db.c.insert({_id:5,name:"d",sex:2,age:5})
统计查询
#统计name:a的数据的数量
db.c.count({name:"a"})
条件连接查询
- 如果我们需要查询同时满足两条以上的条件查询,需要使用
$and
操作符将条件进行关联。(相当于mysql的and) - r如果是 或者 的关系,需要使用
$or
语法:$and:[{},{},{}]...
#查询年龄大于等于1小于等于5的数据
db.c.find({$and:[{age:{$gte:1}},{age:{$let:5}}]})
#查询名字为a或者名字为b的数据
db.c.find({$or:[{name:"a"},{name:"b"}]})
正则查询
mongodb的模糊查询是通过正则表达式的形式实现的。
#语法
db.集合名称.find({字段:/正则表达式/})
#说明:正则表达式是js语法,直接量的写法。
#查询名字包含"z"的数据
db.c.find({name:/z/})
#查询名字以"z"开头的数据
db.c.find({name:/^z/})
排序查询
- 语法:db.集合名.find().sort(JSON数据)
- 说明:键 :就是要排序的列/字段;值: 1代表升序 -1代表降序
/降序排列
#按照年龄升序排序
db.c.find().sort({age:1})
#练习按照年龄降序排序
db.c.find().sort({age:-1})
分页查询
- 语法:db.集合名.find().sort().skip(数字).limit(数字)
- 说明:skip表示跳过指定数量(可选),limit表示限制查询的数量
#按照年龄降序查询2条数据
db.c.find().sort({age:-1}).limit(2)
#.skip()可以写可以不写,如果要写,应该是db.c.find().sort({age:-1}).skip(0).limit(2)
#按照年龄降序跳过2条并查询2条数据
db.c1.find().sort({age:-1}).skip(2).limit(2)
提示:skip()、limit()、sort()三者一起使用的时候,执行的顺序是先sort(),然后是skip(),最后是limit(),这与命令编写的顺序无关
实战分页
需求:假设数据库有1-10这十条数据,实现每页显示2条(5页)
#分析
页码 每页要显示的数据 跳过
1页 1 2 0
2页 3 4 2
3页 5 6 4
4页 7 8 6
5页 9 10 8
#skip计算公式:(当前页-1)*每页显示条数
#假设现在是第一页(1-1)*2
db.c.find().skip(0).limit(2)
#假设现在是第二页(2-1)*2
db.c.find().skip(2).limit(2)
聚合查询
简单的统计我们可以通过.count()等来实现,如果需求略微复杂,如何统计数据、如何实现分组统计?
# 语法
db.集合名.aggregate([
{管道:{表达式}}
...
])
# 常用管道
$group #将集合中的文档分组,用于统计结果
$match #过滤数据,只输出符合条件的文档
$sort #聚合数据进一步排序
$skip #跳过指定文档数
$limit #限制集合数据返回文档数
...
# 常用表达式
$sum #$sum:1同.count()表示统计
$avg #平均
$min #最小值
$max #最大值
...
# 准备数据
use test4
db.c1.insert({_id:1,name:"a",sex:男,age:1})
db.c1.insert({_id:2,name:"a",sex:男,age:2})
db.c1.insert({_id:3,name:"b",sex:女,age:3})
db.c1.insert({_id:4,name:"c",sex:女,age:4})
db.c1.insert({_id:5,name:"d",sex:女,age:5})
# 1.分别统计男生的总年龄和女生的总年龄
db.c1.aggregate([
{
$group:{
-id:"$sex", #_id 写法固定,表示根据哪个字段分组
rs:{$sum:"$age"} #rs是自定义的,这里用于表示结果
}
}
])
# 2.分别统计男生的总人数和女生的总人数
db.c1.aggregate([
{
$group:{
-id:"$sex",
rs:{$sum:1}
}
}
])
# 3.求学生总数和平均年龄
db.c1.aggregate([
{
$group:{
_id:null, #null表示不分组
total_num:{$sum:1},
total_avg:{$avg:"$age"}
}
}
])
# 4.查询男生人数、女生人数、按人数升序
db.c1.aggregate([
{
$group:{
_id:"$sex", #null表示不分组
rs:{$sum:1},
}
},
{
$sort:{rs:1}
}
])
修改文档
基础语法:db.集合名.update (条件,新数据[是否新增,是否修改多条])
是否新增:指条件匹配不到对应数据,则插入数据,true插入,false不插入(默认false)
是否修改多条:指将匹配成功的数据都修改,true都修改,false不都修改,默认只修改第一条
#准备工作
use test
db.dropDatabase()
use test
db.createCollection("c")
#插入一些测试数据
for(var i=1;i<=10;i++){
db.c.insert({"uname":"zs"+i,"age"+i});
}
覆盖修改
# 练习1:将{uname:"zs1"}的数据修改为{uname:"zs11"}
#首先,能匹配到的数据是{uname:"zs1",age:1}
db.c.update({uname:"zs1"},{uname:"zs11"})
#修改后,被匹配的数据被修改为{uname:"zs11"} 缺少了age信息;现在10条数据中的第一条数据为{uname:"zs11"}
局部修改
修改器 | 作用 |
---|---|
$inc | 递增 |
$rename | 重命名列 |
$set | 修改列值 |
$unset | 删除列 |
升级语法:db.集合名.update(条件,{修改器:{新数据键:新数据值}})
#修改第一条数据为{uname:"zs1"}
db.c3.update({uname:"zs11"},{$set:{uname:"zs1"}})
# 给{uname:"zs10"}的年龄增加两岁
db.c3.update({uname:"zs10"},{$inc:{age:2}})
# 给{uname:"zs10"}的年龄减两岁
db.c3.update({uname:"zs10"},{$inc:{age:-2}})
是否新增
【验证是否插入数据】修改uname等zs30的年龄为30岁,可以看到该数据不存在
db.c3.update({uname:"zs30"},{$set:{age"30}},true) #最后一个参数为true,该操作会新增一条数据
是否修改多条
【验证是否修改多条】把所有数据改成10岁
db.c3.update({},{$set:{age:10}},{multi:true}) 或者
db.c3.update({},{$set:{age:10}},false,true) # 这里如果最后一个参数改为false,只会改第一条
#multi:是否批量更新,true批量更新;false不批量更新
综合练习
#插入数据
db.c4.insert({uname:"神龙教主",age:888,who:"男",other:"非国人"})
#完成需求:
#uname 改成 bbb (修改器:$set)
#age 增加 111 (修改器:$inc)
#who 改成sex (修改器:$rename)
#other 删除 (修改器:$unset)
db.c4.update({uname:"神龙教主"},{
$set:{uname:"bbb"},
$inc:{age:111},
$rename:{who:"sex"},
$unset:{other:true}
})
删除文档
#语法:db.集合名.remove(条件 ,【是否删除一条】)
#注意:是否删除一条,默认是false(代表删除多条),true代表只删除一条
#删除集合中的全部数据
db.c.remove({})
#删除name=zs1的数据
db.c.remove({name:"zs1"})
高阶查询
准备数据
use test
db.dropDatabase()
use test
db.createCollection("c")
db.c.insert({_id:1,name:"a",sex:1,age:1})
db.c.insert({_id:2,name:"a",sex:1,age:2})
db.c.insert({_id:3,name:"b",sex:2,age:3})
db.c.insert({_id:4,name:"c",sex:2,age:4})
db.c.insert({_id:5,name:"d",sex:2,age:5})
索引
基本语法
#索引:就是一个可以高效查询的数据结构。
#mongodb索引使用B-Tree(B树)
#mysql索引使用B+Tree
索引可以使得mongodb的查询更加高效,如果没有索引,mongodb必须执行全集合的扫描,即扫描集合中的每个文档,以选择符合查询条件的文档。这种扫描方式的效率非常低,特别是在处理大量的数据的时候,查询甚至能花费几十秒甚至几分钟,这对网站的性能是非常致命的。
优点:
- 提高数据查询的效率,降低数据库的io成本
- 通过索引对数据库进行排序,降低数据排序的成本,降低CPU消耗
缺点:
- 占用磁盘空间
- 大量索引影响SQL语句效率,因为每次插入和修改数据都需要更新索引
mongodb中有两种类型的索引:
- 单字段索引(Single Field Index):mongodb支持在文档的单个字段上创建用户定义的升序/降序索引
- 复合索引(Compound Index):mongodb支持在文档的多个字段上创建用户自定义的索引。
删除全部索引:db.集合名.dropIndexes()#创建索引语法:db.集合名.createIndex(待创建索引的列, [额外选项])
#为age字段创建升序索引
db.c.createIndex({age:1})
#为age字段创建降序索引
db.c.createIndex({age:-1})
#额外选项:设置索引的名称或者唯一索引等等,是一个可选参数
#删除全部索引
db.集合名.dropIndexes()
#删除指定索引
db.集合名.dropIndex(索引名)
#查看索引
db.集合名.getIndexes()
练习
#准备:向数据库中新增100000条数据
use test5
for(var i=1;i<=100000;i++){
db.c1.insert({name:"abc"+i,age:i})
}
#给name创建升序索引
db.c1.createIndex({name:1})
#查看当前索引
db.c1.getIndexes() #会发现有两个索引,一个是默认索引,一个是我们给name添加的索引,查看索引显示的参数中,key代表给哪个键设置了索引,name代表索引名,默认系统生成,也可自定义
#删除给name创建的索引
db.c1.dropIndex("name_1") #刚才我们查看索引,发现name的索引名称为"name_1"
#给name添加索引,并起名为gift
db.creatrIndex({name:1},{name:"gift"})
#给name和age创建索引(复合索引)
db.c1.createIndex({name:1,age:1})
#创建唯一索引
#语法:db.集合名.createIndex(待添加索引的列,{unique:列名})
#给name添加唯一索引
db.c1.createIndex({name:1},{unique:name})
如果name=a的数据已经存在了,并且name字段设置了唯一索引,因此你再插入相同数据会报错
其他索引
- 地理空间索引(Geospatial Index):为了支持对地理空间坐标的有效查询,mongodb提供了两种特殊的索引,即返回结果时使用平面几何的二维索引和返回结果时使用球面几何的二维球面索引
- 文本索引(Text Index):支持在集合中搜索字符串内容。这些文本索引不存储特定语言的停止词(例如:“the”、“a”、“or”),而将集合中的词作为词干,只存储根词。
- 哈希索引(Hashed Index):为了支持基于散列的分片而提供哈希索引,它对字段值的散列进行索引。这些索引在其范围内的值分布更加随机,但只支持相等匹配,不支持基于范围的查询。
索引使用
执行计划
分析查询性能,通常使用计划(解释计划、Explain Plan)来查看查询的情况,如查询消费的时间、是否基于索引查询等等。
#语法:db.集合名.find().explain(options) --options可选
#查看该条件下查询的详细情况
db.c.find({name:"a"}).explain()
响应的情况中有三种类型:
- COLLSCAN全表扫描
- IXSCAN 索引扫描
- FETCH 依据索引去检索指定document
# 为age添加索引,分析索引
db.c1.createIndex({age:1})
语法:db.c1.find({age:18}).explain()
涵盖的查询
Covered Queries:当查询的条件和查询的投影仅包含索引字段时,mongodb直接从索引返回结果,而不扫描任何文档或将文档带入内存。这些覆盖的查询非常有效。
选择原则
- 为常做条件、排序、分组的字段建立索引
- 选择唯一性索引 ps:同值较少如邮箱字段
- 选择较小的数据列,为较长的字符串使用前缀索引 ps:索引文件更小
权限机制
保护数据库安全
语法:
db.createUser({
"user":"账号",
"pwd":"密码",
"roles":[{
role:"角色",
db:"所属数据库"
}]
})
超级用户角色:root
数据库用户角色:read、readWrite
数据库管理角色:dbAdmin、userAdmin
集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManger
备份恢复角色:backup、restore
所有数据库角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase
# 角色说明
root:只在admin数据库中可用。超级账号,超级权限
read:允许用户读取指定数据库
readWrite:允许用户读写指定数据库
dbAdmin:允许用户在指定数据库中执行管理函数,如索引创建、删除、查看统计或访问system.profile
dbAdminAnyDatabase:只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限
该模块比较复杂,这里笔者有时间再记录
备份还原
备份数据库
mongodump -h -port -u -p -d -o #导出数据语法
-h host 服务器ip地址(一般不写,默认本机地址)
-port 端口(一般不写,默认27017)
-u user 账号
-p pwd 密码
-d database 数据库(不写则导出全部)
-o open 备份到指定目录下
测试:备份所有数据:mongodump -u admin -p admin123 -o /root/mongodb/backup
还原数据库
mongorestore -h -port -u -p -d --drop 备份数据目录
-h host 服务器ip地址(一般不写,默认本机地址)
-port 端口(一般不写,默认27017)
-u user 账号
-p pwd 密码
-d database 数据库(不写则还原全部数据)
-o open 备份到指定目录下
--drop 先删除数据库再导入,不写则直接覆盖
实战可视化管理工具
adminMongo WEB/PC端网页管理 https://adminmongo.markmoffat.com/
Robo 3T * 客户端软件 https://robomongo.org/download/
MongoVue 客户端软件
视频中推荐第二个
这种东西网上一大堆,自己百度
mongoose
是node中提供的操作mongodb的模块,能够通过node语法实现mongodb数据库增删改下,从而实现用node写程序来管理mongndb数据库
笔者没有去关注node语法使用mongodb,后续node使用mongodb不在学习
整合SpringBoot
关于Springboot使用mongodb的教程建议学习一下这位大佬的作品:
https://blog.csdn.net/yanpenglei/article/details/79261875