微信技术群:Day9884125
1. 大纲
- MongDB的概念
- MongDB安装配置与基础命令
- MongoDB CRUD
2. MongDB的体系结构
2.1 摘要
- Nosql的概念
- Nosql的应用场景
- MongoDB的逻辑组成
2.2 NoSql的概念
NoSql它不需要预先定义模式,没有主键关联、支持分片、支持副本
2.2.1 NoSql的分类
2.2.1.1 键值(key-value)存储数据库
这一类数据库主要会使用到一个哈希表,这个表中有一个特定的键和一个指针指向特定的数据。Key/Value模型对于IT系统来说的优势在于简单,易部署。但是如果DBA只对部分值进行查询或更新的时候,Key/Value就显得效率低下了。例如tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB
2.2.1.2 列存储数据库
这部分数据库通常是用来应对分布式存储的海量数据。键仍然存在,但是他们的特点指向了多个列。这些列是由列家族来安排的。例如:Cassandra、HBase、Riak
2.2.1.3 文档型数据库
它同第一种键值存储相类似。该类型的数据模型是版本化的文档,板结构化的文档以特定的格式存储,比如josn,文档型数据库可以看作是键值数据库的升级版,允许之间嵌套键值。而且文档型数据库比键值数据库的查询效率更高。例如:CouchDB,MongoDB,SequoialDB
2.2.1.4 图形数据库
图形结构的数据库同其他行列以及刚性结构的sql数据库不同,它是使用灵活的图形模型,并且能够扩展到多个服务器上。NoSql数据库没有标准的查询语句,因此进行数据库查询需要制定数据模型。许多NoSql数据库都有REST式的数据接口或者查询API。例如:Neo4J、InfoGrid、Infinite Graph
2.3 NoSql的应用场景
NoSql数据库在以下场景比较适用
- 数据模型比较简单
- 需要灵活性更强的IT系统
- 对数据库性能要求比较高
- 不需要高度的数据一致性 基于豆瓣电影举例说明NoSql的应用场景
- 电影基本信息分析
- 电影与明星关系存储
2.4 MongoDB的逻辑组成
2.4.1 体系结构
2.4.2 逻辑结构与关系数据库的对比
| 关系型数据库 | MongoDB |
|---|---|
| database(数据库) | database(数据库) |
| table(表) | collection(集合) |
| row(行) | document(BSON文档) |
| column(列) | fileId(字段) |
| index(唯一索引、主键索引) | index(全文索引) |
| join(主外键关联) | embedded Document(嵌套文档) |
| primary key(指定1至N个列做主键) | primary key(指定_id field做主键) |
| aggreation(groupy) | aggreation(pipeline mapReduce) |
3 MongoDB安装配置与基础命令
3.1 介绍
- mongodb版本说明
- mongodb启动参数说明
- 客户端shell的使用及参数说明
- 数据库与集合的基础操作
3.2 mongodb社区版说明
下载地址:www.mongodb.com/download-ce…
#下载
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.5.tgz
# 解压
tar -zxvf mongodb-linux-x86_64-4.0.5.tgz
3.3 mongodb启动参数说明
mongodb由C++编写,下载下来可以直接启动
#创建数据库目录
mkdir -p /data/mongo
# 启动mongo
./bin/mongod --dbpath=/data/mongo/
3.3.1 常规参数
| 参数 | 说明 |
|---|---|
| dbpath | 数据库⽬录,默认/data/db |
| bind_ip | 监听IP地址,默认全部可以访问 |
| port | 监听的端口,默认27017 |
| logpath | 日志路径 |
| logappend | 是否追加日志 |
| auth | 是否开启用户密码登录 |
| fork | 是否已后台启动的方式登录 |
| config | 指定配置文件 |
3.3.2 配置文件示例
vim mongo.conf
内容
dbpath=/data/mongo/
port=27017
bind_ip=0.0.0.0
fork=true
logpath = /data/mongo/mongodb.log
logappend = true
auth=false
已配置文件方式启动
./bin/mongod -f mongo.conf
3.3.3 客户端shell的使用及参数说明
#启动客户端 连接 本机的地的默认端口
./bin/mongo
# 指定IP和端口
./bin/mongo --host=127.0.0.1 --port=27017
mongo shell 是一个js控台,可以执行js相关运算如:
3.4 数据库与集合的基础操作
# 查看数据库
show dbs;
# 切换数据库
use luban;
# 创建数据库与集合,在插入数据时会自动 创建数据库与集和
db.friend.insertOne({name:"wukong",sex:"man"});
# 查看集合
show tables;
show collections;
# 删除集合
db.friend.drop();
# 删除数据库
db.dropDatabase();
4 MongoDB CRUD
4.1 概要
- 数据的新增方式
- 数据的查询
- 数据的修改删除
- 全文索引查询
4.2 数据的新增的方式
关于mongodb数据插入的说明
- 数据库的新增不需要事先设计模型结构,插入数据时会自动创建
- 同一个集合中不同数据字段结构不一样
4.2.1 插入相关方法
//插入单条
db.friend.insertOne({name:"wukong",sex:"man"});
// 插入多条
db.friend.insertMany([
{name:"wukong",sex:"man"},{name:"diaocan",sex:"woman",age:18,birthday:new
Date("1995-11-02")},{name:"zixiao",sex:"woman"}
]);
// 指定ID 8 db.friend.insert([ 9
{_id:1,name:"wokong",sex:"man",age:1},
{_id:2,name:"diaocan",sex:"women",birthday:new Date("1988-11-11")}
])
4.3 数据的查询
4.3.1 介绍
a. 基于条件的基础查询
b. or、gt、lt、$lte 运算符
c. 基于 sort skip limit ⽅法实现排序与分⻚
d. 嵌套查询
e. 数组查询
f. 数组嵌套查询
- 逻辑运算:or 其值为多个逻辑的组合运算,后⾯跟中括号,中括号包括多个⼤括号。
- 值运算:gt、lt、$lte
4.3.2 基础查询
# 基于ID查找
db.emp.find({_id:1101})
# 基于属性查找
db.emp.find({"name":"鲁班"})
# && 运算 与大于 运算
db.emp.find({"job":"讲师","salary":{$gt:8000}})
# in 运算
db.emp.find({"job":{$in:["讲师","客服部"]}})
# or 运算
db.emp.find({$or:[{job:"讲师" },{job:"客服部"}] })
db.friend.find({$or:[{"name":"diaocan"},{age:{$gte:18}}]});
4.3.3 排序与分页
// sort skip limit
db.emp.find().sort({dep:1,salary:-1}).skip(5).limit(2)
4.3.4 嵌套查询
# 错误示例:无结果
db.student.find({grade:{redis:87,dubbo:90 });
# 错误示例:无结果
db.student.find({grade:{redis:87,dubbo:90,zookeper:85} })
# 基于复合属性查找 时必须包含其所有的值 并且顺序一至
db.student.find({grade:{redis:87,zookeper:85,dubbo:90} })
# 基于复合属性当中的指定值 查找。注:名称必须用双引号
db.student.find({"grade.redis":87});
db.student.find({"grade.redis":{"$gt":80}});
4.3.5 数组查询
db.subject.insertMany([
{_id:"001",name:"陈霸天",subjects:["redis","zookeper","dubbo"]},
{_id:"002",name:"张明明",subjects:["redis","Java","mySql"]},
{_id:"003",name:"肖炎炎",subjects:["mySql","zookeper","bootstrap"]},
{_id:"004",name:"李鬼才",subjects:["Java","dubbo","Java"]},
])
#无结果
db.subject.find({subjects:["redis","zookeper"]})
#无结果
db.subject.find({subjects:["zookeper","redis","dubbo"]})
# 与嵌套查询一样,必须是所有的值 并且顺序一至
db.subject.find({subjects:["redis","zookeper","dubbo"]})
# $all 匹配数组中包含该两项的值。注:顺序不作要求
db.subject.find({subjects:{"$all": ["redis","zookeper"]}})
注:
# 简化数组查询
db.subject.find({subjects:"redis"})
# 简化数组查询 ,匹配数组中存在任意一值。与$all相对应
db.subject.find({subjects:{$in: ["redis","zookeper"]}})
4.3.6 数组嵌套查询
#基础查询 ,必须查询全部,且顺序一至
db.subject2.find({subjects:{name:"redis",hour:12} })
#指定查询第一个数组 课时大于12
db.subject2.find({"subjects.0.hour":{$gt:12}})
#查询任科目 课时大于12
db.subject2.find({"subjects.hour":{$gt:12}})
# $elemMatch 元素匹配,指定属性满足,且不要求顺序一至
db.subject2.find({subjects:{$elemMatch:{name:"redis",hour:12}}})
# 数组中任意元素匹配 不限定在同一个对象当中
db.subject2.find({"subjects.name":"mysql","subjects.hour":120})
4.3.7 数组的删除与修改
修改
#设置值
db.emp.update({_id:1101} ,{ $set:{salary:10300} })
#自增
db.emp.update({_id:1101} ,{ $inc:{salary:200}})
#基于条件 更新多条数据
# 只会更新第一条
db.emp.update({"dep":"客服部"},{$inc:{salary:100}})
# 更新所有 匹配的条件
db.emp.updateMany({"dep":"客服部"},{$inc:{salary:100}})
删除
// 基于查找删除
db.emp.deleteOne({_id:1101})
// 删除整个集合
db.project.drop()
// 删除库
db.dropDatabase()
5 应用操作
- mongoDB的聚合操作
- mongoDB的索引特性
5.1 mongoDB的聚合操作
5.1.1 知识点
- pipline聚合
- mapRedurce聚合
5.1.2 pipline与mapRedurce比较
pipline速度快,但只能运行在单机上,适合数据量小的实时聚合操作
mapRedurce可以运行在分布式节点,适合大数据量并且复杂的聚合分析操作
5.1.3 mpipline聚合
mpipline聚合其特性是运行速度快,只能运行在单机上,并且对资源的使用有一定限制如下:
- 单个的聚合操作耗费的内存不能超过20%
- 返回的结果集大小在16M以内
5.1.4 语法说明
aggredate方法接收任意多个参数,每个参数都是一个具体类别的聚合操作,通过参数的顺序组成一个执行链。每个操作执行完后将返回结果交给下一个操作。直到最后产生结果。
5.1.5 pipline相关运算符
project:返回需要聚合的字段
$group:统计聚合数据,必须指定_id列
- $max:求出最大值
- $sum:求和
- $avg:求平均值
- $push:将结果插入至一个数组当中
- $addToSet:将结果插入至一个数组当中,并去重
- $first:取第一个值
- limit:用来限制MongoDB聚合管道返回的文档数
unwind:(flatmap)将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值
$sort:将输入文档排序后输出
5.1.5.1 示例
$match 条件过滤
db.emp.aggregate({$match:{"job":"讲师"}})
$project 指定列返回
# 返回指定列_id自动带上
db.emp.aggregate({$match:{"job":"讲师"}},{$project:{"job":1,"salary":1}})
# 返回指定列,并修改列名
db.emp.aggregate({$match:{"job":"讲师"}},{$project:{"工作":"$job","薪水":"$salary"}})
$group操作(必须指定_id列)
# 基于工作分组,并求出薪水总和
db.emp.aggregate({$group:{_id:"$job",total:{$sum:"$salary"}}})
#求出薪水最大值
db.emp.aggregate({$group:{_id:"$job",total:{$max:"$salary"}}})
# 将所有薪水添加列表
db.emp.aggregate({$group:{_id:"$job",total:{$push:"$salary"}}})
# 将所有薪水添加列表 并去重
db.emp.aggregate({$group:{_id:"$job",total:{$addToSet:"$salary"}}})
聚合操作可以任意个数和顺序的组合
# 二次过滤
db.emp.aggregate({$match:{"job":"讲师"}},{$project:{"工作":"$job","薪水":"$salary"}},{$match:{"薪水":{$gt:8000}}})
$skip 与 $limit 跳转 并限制返回数量
db.emp.aggregate({$group:{_id:"$job",total:{$push:"$salary"}}},{$limit:4},{$skip:2});
#sort 排序
db.emp.aggregate( {$project:{"工作":"$job","salary":1}},{$sort:{"salary":1,"工作":1}});
#unwind 操作,将数组拆分成多条记录
db.emp.aggregate({$group:{_id:"$job",total:{$push:"$salary"}}},{$unwind:"$total"});
5.1.4 mapRedurce聚合
mapRedurce非常适合实现非常复杂,并且数量大的聚合计算,其可运行在多台节点上实行分布式计算。
5.1.4.1 mapRedurce概念
MapReduce 现大量运用于hadoop大数据计算当中,其最早来自于google 的一遍论,解决大PageRank搜索结果排序的问题。其大至原理如下:
5.1.4.2 mongodb中mapRedurce的使用流程
- 创建Map函数,
- 创建Redurce函数
- 将map、Redurce 函数添加至集合中,并返回新的结果集
- 查询新的结果集
5.1.4.3 示例
5.1.4.3.1 基础示例
//创建map对象
var map1 = function(){
emit(this.job,this.name); // 内置函数 key,value
}
//创建reduce对象
var reduce1=function(job,count){
return Array.sum(count);
}
// 执行mapReduce 任务 并将结果放到新的集合 result 当中
db.emp.mapReduce(map1,reduce1,{out:"result"}).find()
//查询新的集合
db.result.find()
使用复合对象作为key
# 使用复合对象作为key
var map2=function (){
emit({"job":this.job,"dep":this.dep},{"name":this.name,"dep":this.dep});
}
var reduce2=function(key,values){
return values.length;
}
db.emp.mapReduce(map2,reduce2,{out:"result2"}).find()
调式mapReduce执行
var emit=function(key,value){
print(key+":"+value);
}
六 mongoDB的索引特性
6.1 知识点
- 索引的基础概念
- 单键索引
- 多键索引
- 复合索引
- 过期索引
- 全文索引
6.2 索引的基础概念
查看执行计划:
db.emp.find({"salary":{$gt:500}}).explain()
创建简单索引
#创建索引
db.emp.createIndex({salary:1})
#查看索引
db.emp.getIndexes()
#查看执行计划
db.emp.find({"salary":{$gt:500}}).explain()
6.2.1 单键索引
单个例上创建索引
db.subject.createIndex({"name":1})
# 嵌套文档中的列创建索引
db.subject.createIndex({"grade.redis":1})
#整个文档创建索引
db.subject.createIndex({"grade":1})
6.2.2 多键索引
创建多键索引
db.subject.createIndex({"subjects":1})
6.2.3 复合索引(组合索引)
创建复合索引
db.emp.createIndex( { "job":1,"salary":-1 } )
查看执行计划:
db.emp.find({"job":"讲师", "salary":{$gt:500}}).explain()
db.emp.find({"job":"讲师"}).explain()
db.emp.find({"salary":{$gt:500}}).explain()
6.2.4 复合索引在排序中的应用
db.emp.find({}).sort({"job":1, "salary":-1}).explain()
db.emp.find({}).sort({"job":-1, "salary":1}).explain()
db.emp.find({}).sort({"job":-1, "salary":-1}).explain()
db.emp.find({}).sort({"job":1, "salary":1}).explain()
job_1_salary_-1
db.emp.find({"job":"讲师","salary":{$gt:5000}}).explain() // 走索引
db.emp.find({"salary":{$gt:5000},"job":"讲师"}).explain() // 走索引
db.emp.find({"job":"讲师"}).explain() // 走索引
db.emp.find({"salary":{$gt:5000}}).explain()
6.2.5 排序 场景
db.emp.find({}).sort({"job":1,"salary":-1}).explain()// 完全匹配 ==>走索引
db.emp.find({}).sort({"job":-1,"salary":1}).explain()//完全不匹配 ==>走索引
db.emp.find({}).sort({"job":1,"salary":1}).explain()// 一半匹配 ==>不走索引
db.emp.find({}).sort({"job":-1,"salary":-1}).explain()// 一半匹配 ==>不走索引
db.emp.find({}).sort({"job":-1}).explain() // ==>走索引
db.emp.find({}).sort({"salary":-1}).explain() // ==>不走索引
6.2.4 过期索引
过期索引存在一个过期的时间,如果时间过期,相应的数据会被自动删除
6.2.4.1 示例
#插入数据
db.log.insert({"title":"this is logger info","createTime":new Date()})
#创建过期索引
db.log.createIndex({"createTime":1},{expireAfterSeconds:10})
6.2.5 全文索引
创建全文索引
db.project.createIndex( {"name":"text","description":"text"})
使用全文索引进行查询
db.project.find({$text:{$search:"java dubbo"}})
-用于屏蔽关键字
db.project.find({$text:{$search:"java -dubbo"}})
短语查询,\" 包含即可
db.project.find({$text:{$search:"\"Apache Dubbo\""}})
中文查询
db.project.find({$text:{$search:"阿里 开源"}})
7 MongoDB复制集特性
7.1 知识点
- 复制集群的架构
- 复制集群搭建
- 复制集群的选举配置
7.2 复制集群的架构
7.3 复制集群搭建基础示例
7.3.1 主节点配置
dbpath=/data/mongo/master
port=27017
fork=true
logpath=master.log
replSet=tulingCluster
7.3.2 从节点配置
dbpath=/data/mongo/slave
port=27018
fork=true
logpath=slave.log
replSet=tulingCluster
#子节点配置2
dbpath=/data/mongo/slave2
port=27019
fork=true
logpath=slave2.log
replSet=tulingCluster
- 分别启动三个节点
- 进入其中一个节点
7.3.3 集群复制配置管理
#查看复制集群的帮助方法
rs.help()
7.3.4 添加配置
// 声明配置变量
var cfg ={"_id":"tuling",
"members":[
{"_id":1,"host":"127.0.0.1:27017"},
{"_id":2,"host":"127.0.0.1:27018"} ]
}
// 初始化配置
rs.initiate(cfg)
// 查看集群状态
rs.status()
7.3.5 变更节点示例
// 插入新的复制节点
rs.add("127.0.0.1:27019")
// 删除slave 节点
rs.remove("127.0.0.1:27019")
7.4 演示复制状态
- 进入主节点客户端
- 插入数据
- 进入从节点查看数据
- 尝试在从节点查看数据 注:默认节点下从节点不能读取数据。调用 rs.slaveOk() 解决。
7.5 复制集群选举操作
为了保证高可用,在集群当中如果主节点挂掉后,会自动 在从节点中选举一个 重新做为主节点。
- 演示节点的切换操作
- kill主节点
- 进入从节点查看集群状态
7.5.1 选举的原理
在mongodb中通过在集群配置中的rs.属性值大小来决定选举谁作为主节点,通时也可以设置arbiterOnly为true表示作为裁判节点用于执行选举操作,该配置下的节点永远不会被选举为主节点和从节点
7.5.1.1 示例
重新配置节点
var cfg ={"_id":"tuling",
"protocolVersion" : 1,
"members":[
{"_id":1,"host":"127.0.0.1:27017","priority":10},
{"_id":2,"host":"127.0.0.1:27018","priority":0},
{"_id":3,"host":"127.0.0.1:27019","priority":5},
{"_id":4,"host":"127.0.0.1:27020","arbiterOnly":true}
]
}
// 重新装载配置,并重新生成集群节点。
rs.reconfig(cfg)
//重新查看集群状态
rs.status()
节点说明 primary节点:可以查询和新增数据 secondary节点:只能查询不能新增,基于priority权重可以被选为主节点 rbiter节点:不能查询数据和新增数据,不能变成主节点
8 MongoDB分片操作
8.1 知识点
- 分片的概念
- mongodb中的分片架构
- 分片示例
8.2 为什么需要分片
随着数据的增长,单机实例的瓶颈是很明显的。可以通过复制的机制应对压力,但mongodb中单个集群的 节点数量限制到了12个以内,所以需要通过分片进一步横向扩展。此外分片也可节约磁盘的存储。
8.3 mongodb中的分片架构
8.4 分片中的节点说明
- 路由节点(mongos):用于分发用户的请求,起到反向代理的作用。
- 配置节点(config):用于存储分片的元数据信息,路由节基于元数据信息 决定把请求发给哪个分片。(3.4版本之后,该节点,必须使用复制集。)
- 分片节点(shard):用于实际存储的节点,其每个数据块默认为64M,满了之后就会产生新的数据库。
8.5 分片示例流程
1.配置 并启动config 节点集群
a.配置集群信息
2.配置并启动2个shard 节点
3.配置并启动路由节点
a.添加shard 节点
b.添加shard 数据库
c.添加shard 集合
4.插入测试数据
a.检查数据的分布
5.插入大批量数据查看shard 分布
a.设置shard 数据块为一M
b.插入10万条数据
8.6 配置 并启动config节点集群
# 节点1 config1-37017.conf
dbpath=/data/mongo/config1
port=37017
fork=true
logpath=logs/config1.log
replSet=configCluster
configsvr=true
# 节点2 config2-37018.conf
dbpath=/data/mongo/config2
port=37018
fork=true
logpath=logs/config2.log
replSet=configCluster
configsvr=true
8.7 进入shell并添加config集群配置
var cfg ={"_id":"configCluster",
"protocolVersion" : 1,
"members":[
{"_id":0,"host":"127.0.0.1:37017"},
{"_id":1,"host":"127.0.0.1:37018"}
]
}
// 重新装载配置,并重新生成集群。
rs.initiate(cfg)
# 配置 shard 节点集群==============
# 节点1 shard1-47017.conf
dbpath=/data/mongo/shard1
port=47017
fork=true
logpath=logs/shard1.log
shardsvr=true
# 节点2 shard2-47018.conf
dbpath=/data/mongo/shard2
port=47018
fork=true
logpath=logs/shard2.log
shardsvr=true
配置 路由节点 mongos ==============
# 节点 route-27017.conf
port=27017
bind_ip=0.0.0.0
fork=true
logpath=logs/route.log
configdb=conf/127.0.0.1:37017,127.0.0.1:37018
// 添加分片节点
sh.status()
sh.addShard("127.0.0.1:47017");
sh.addShard("127.0.0.1:47018");
为数据库开启分片功能
sh.enableSharding("tuling")
为指定集合开启分片功能
sh.shardCollection("tuling.emp",{"_id":1})
修改分片大小
use config
db.settings.find()
db.settings.save({_id:"chunksize",value:1})
尝试插入1万条数据:
for(var i=1;i<=100000;i++){
db.emp.insert({"_id":i,"name":"copy"+i});
}
db.emp.createIndex({_id: 'hashed'})
9 MongoDB用户权限管理
// 创建管理员用户
use admin;
db.createUser({"user":"admin","pwd":"123456","roles":["root"]})
#验证用户信息
db.auth("admin","123456")
#查看用户信息
db.getUsers()
# 修改密码
db.changeUserPassword("admin","123456")
以auth 方式启动mongod,需要添加auth=true 参数 ,mongdb 的权限体系才会起作用:
#以auth 方向启动mongod (也可以在mongo.conf 中添加auth=true 参数)
./bin/mongod -f conf/mongo.conf --auth
# 验证用户
use admin;
db.auth("admin","123456")
创建只读用户
db.createUser({"user":"dev","pwd":"123456","roles":["read"]})
重新登陆 验证用户权限
use luban ;
db.auth("dev","123456")