MongoDB基础
1.业务应用场景
1)High performance:对数据库高并发读写的需求
2)Huge Storage:对海量数据的高效存储和访问的需求
3)High Scalability &&High Avaliablity:对数据的高可扩展性和可用性的需求
2.MongoDB简介
MongoDB是一个开源,高性能,无模式的文档型数据库,是NoSQL数据库产品中的一种,是最像关系型数据的非关系型数据库。
MongoDB中的记录是一个文档,它是由字段和值对(fileld:value)组成的数据结构,MongoDB文档类似于JSON对象,即一个文档认为就是一个对象。
3.MongoDB特点
1)高性能
2)高可用
3)高扩展性
4)丰富的查询支持
3.单机部署
3.1Windows系统中的安装启动
第一步:下载安装包
Mongodb预编译二进制包下载地址:https://www.mongodb.com/try#community
https://www.mongodb.com/try/download/community?tck=docs_server
根据上图所示下载zip包
提示:版本的选择
MongoDB的版本命名规范如:x.y.z
y为奇数时表示当前版本为开发版本,如:1.5.2,4.1.13
y为偶数时表示当前版本为稳定版,如:1.6.3,4.0.10
z是修正版本号,数字越大越好
第二步:解压安装启动
将压缩包解压到一个目录中
在解压目录中,手动建立一个目录用于存放数据文件,如data/db
方式一:命令行参数方式启动服务
在bin目录中打开命令行提示符,输入如下命令:
mongod --dbpath=..\data\db
我们在启动信息中可以看到,mongodb默认端口是27017,如果我们想改变默认的启动端口,也可以指定默认端口
方式二:配置文件方式启动服务
在解压目录中新建config文件夹,该文件夹中新建配置文件mongo.conf,内容参考如下:
storage:
dbPath: D:\SoftWare\MongoDB\mongodb-win32-x86_64-2008plus-ssl-4.0.28\data\db
注意:mongodb 3.0之后配置文件采用YAML格式,这种格式非常简单,使用:表示,开头使用“空格”作为缩进。需要注意的是,“:”之后有value的话,需要紧跟一个空格,如果key只是表示层级,则无需在“:”后增加空格(比如:systemLog:后面既不需要空格)。按照层级,每行4个空格缩进,第二级则8个空格,依次轮推,顶层则不需要空格缩进
启动:
在bin目录中打开命令行提示符,输入如下命令:
mongod -f ../conf/mongod.conf 或 mongod --config ../config/mongod.conf
启动成功:
3.2 Shell 连接(mongo命令)
方式一:
在命令提示符输入以下shell命令即可完成登录(注意:Mongodb服务端要保持开启)
mongo 或:mongo --host=127.0.0.1 --port=127017
方式二:
Compass-图形化界面客户端
到MongoDB官网下载MongoDB Compass
地址:https://www.mongodb.com/try/download/compass
建议安装压缩版,直接解压安装即可
3.2Linux系统中的安装启动和连接
(1)先到官网下载压缩包:mongodb-linux-x86_64-4.0.28.tgz
官网地址:https://www.mongodb.com/try/download/community?tck=docs_server
(2)上传压缩包到linux中,解压到当前目录
tar -xvf mongodb-linux-x86_64-4.0.28.tgz
(3)移动解压后的文件夹到指定的目录中
mv mongodb-linux-x86_64-4.0.28 /usr/local/mongodb
(4)新建几个目录,分别用来存储数据和日志
# 数据存储目录
mkdir -p /mongodb/single/data/db
#日志存储目录
mkdir -p /mongodb/single/log
(5)新建并修改配置文件
vim /mongodb/single/mongod.conf
配置文件内容如下:
systemLog:
# Mongodb发送所有日志输出的目标指定为文件
destination: file
# Mongod日志记录路径
path: "/mongodb/single/log/mongod.log"
# 当mongod实例重启时,会将新条目附加到现有日志文件的末尾
logAppend: true
storage:
# mongod实例存储其数据的目录,storage.dbPath设置仅适用于mongod
dbPath: "/mongodb/single/data/db"
journal:
# 启用或禁用持久性日志以确保数据文件保持有效和可恢复
enabled: true
processManagement:
# 启用在后台运行mongod进程的守护进程模式
fork: true
net:
# 服务实例绑定的IP,默认是localhost
bindIp: 192.168.228.128
# 绑定的端口,默认是27017
port: 27017
(6)启动MongoDB服务
/usr/local/mongodb/bin/mongod -f /mongodb/single/mongod.conf
注意:
如果启动后不是successfully,则是启动失败了,原因基本上就是配置文件有问题
通过进程来查看服务是否启动了
ps -ef |grep mongod
(7)分别用mongo命令和compass工具来连接测试
mongo --host=192.168.228.128
注意:如果远程连接不上,需要关闭防火墙(包括windows防火墙)
最后点确定或者应用
再ping一次,互相ping都ok了
4.MongoDB基本操作
4.1)数据库的创建和删除
admin:从权限的角度来看,这是"root"数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,如列出所有数据库或关闭服务器
local:这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
config:当mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息
查看数据库:show dbs 或 show databases
创建数据库:use 数据库名
删除数据库:db.dropDatabase()
4.2)集合的创建和删除
显示创建集合:db.createCollection(name)
查看当前库中的集合:show collection 或 show tables
删除集合:db.集合.drop()
4.3)集合插入数据
4.3.1)要向comment的集合(表)中插入一条测试数据:
db.comment.insert({"articleid":"10000","content":"今天天气真好,阳光明媚","userid":"1001",
"nickname":"Rose","createdatetime":new Date(),"likenum":NumberInt(10),"state":null})
查询插入数据:db.comment.find()
提示:
1)comment集合如果不存在,则会隐式创建
2)mongo中的数字,默认情况下是double类型,如果要存整形,必须使用函数NumberInt(整形数字),否则取出来就有问题
3)插入当前日期使用new Date()
4)插入的数据没有指定_id,会自动生成主键值
5)如果某字段没值,可以 赋值为null,或者不写该字段
4.3.2)插入多条数据:
db.comment.insertMany([{},{},{},{}......])
4.3.3)数据查询:
全表查询:db.comment.find()
查询一条数据:db.comment.findOne({articleid:"100001"})
投影查询:
db.comment.find({articleid:"100001"}) # 返回所有数据
db.comment.find({articleid:"100001"},{articleid:1}) #只返回articleid这一列数据,带_id字段)
db.comment.find({articleid:"100001"},{articleid:1,_id:0}) # 只返回articleid这一列数据,不带_id字段4.3.4)数据插入校验
如果某条数据插入失败,将会终止插入,但已经插入成功的数据不会回滚掉。因此批量插入由于数据较多容易出现失败,因此,可以使用try catch()进行异常捕捉处理。
try{
db.comment.insertMany([{},{},{},{}......]);
} catch(e) {
print(e);
}
4.4)文档的更新
4.4.1)覆盖的修改
修改_id为1的记录,点赞量为1001,本行数据修改后只保留likenum这个字段
db.comment.update({_id:"1"},{likenum:NumberInt(1001)})
4.4.2)局部的修改
使用$set,修改_id为2的记录,浏览量为889
// 默认只修改第一条数据
db.comment.update({_id:"2"},{$set:{likenum:NumberInt(889)}})
// 修改所有符合条件的数据
db.comment.update({_id:"2"},{$set:{likenum:NumberInt(889)}},{multi:true})
4.4.3)列值增长的修改
若想实现对某列值在原有值的基础上进行增加或减少,可以使用$inc运算符来实现
对_id为3的数据点赞数,每次递增1
db.comment.update({_id:"3"},{$inc:{likenum:NumberInt(1)}})
4.5)删除文档
4.5.1)将数据全部删除
db.comment.remove({})
4.5.2)删除id=1的记录
db.comment.remove({_id:"1"})
4.6)文档的分页查询
4.6.1)统计所有记录数
统计comment集合的所有的记录数:
db.comment.count()
4.6.2)按条件统计记录数
例如:统计userid为1003的记录条数
db.comment.count({userid:"1003"})
4.6.3)如果你想返回指定条数的记录,可以在find方法后调用limit来返回结果(TopN),默认值20
db.comment.find().limit(2)
skip方法同样接受一个数字参数作为跳过的记录条数(前n个不要),默认值是0
db.comment.find().skip(3)
4.7)排序查询
sort()方法对数据进行排序,sort()方法可以通过参数指定排序的字段,并使用1和-1来指定排序的方式,其中1为升序排列,-1为降序排列
例如:对userid降序排列,并对访问量进行升序排列
db.comment.find().sort({userid:-1,likenum:1})
提示:skip(),limit(),sort()三个放在一起执行的时候,执行的顺序是先sort(),然后是skip(),最后是显示的limit(),和命令编写顺序无关
4.8)文档的更多查询
1)正则查询
MongoDB的模糊查询是通过正则表达式的方式实现的,格式为:
db.collection.find({field:/正则表达式/}) 或 db.集合.find({字段:/正则表达式/})
例如,我要查询评论内容包含"开水"的所有文档,代码如下:
db.comment.find({content:/开水/})
如果要查询评论的内容中以”专家“开头的,代码如下:
db.comment.find({content:/^专家/})
2)比较查询
<,<=,>,>=这个操作符也是很常用的,格式如下
db.集合名称.find({"field":{$gt:value}}) // 大于:field > value
db.集合名称.find({"field":{$lt:value}}) // 小于:field < value
db.集合名称.find({"field":{$gte:value}}) // 大于等于:field >= value
db.集合名称.find({"field":{$lte:value}}) // 小于等于:field <= value
db.集合名称.find({"field":{$ne:value}}) // 不等于:field != value
例如:查询评论点赞数量大于700的记录
db.comment.find({likenum:{$gt:NumberInt(700)}})
3)包含查询
包含使用$in操作符
例如:查询评论的集合中userid字段包含1003或1004的文档
db.comment.fing({userid:{$in:["1003","1004"]}})
不包含使用$nin操作符
例如:查询评论集合中userid字段不包含1003和1004的文档
db.comment.find({userid:{$nin:["1003","1004"]}})
4)条件连接查询
我们如果需要查询同时满足两个以上条件,需要使用$and操作符条件进行关联(相当于sql中的and)
格式为:$and:[{},{},{}]
例如:查询评论集合中likenum大于等于700并且小于2000的文档
db.comment.find({$and:[{likenum:{$gte:NumberInt(700)}},{likenum:{$lt:NumberInt(2000)}}]})
如果两个以上条件之间是或者关系,我们使用操作符进行关联,与前面and的使用方式相同
格式为:$or:[{},{},{}]
例如:查询评价集合中userid为1003,或者点赞数小于1000的文档记录
db.comment.find({$or:[{userid:"1003"},{likenum:{$lt:Number(1000)}}]})
4.9)索引的概述和类型
1)概述:索引支持在MongoDB中高效地执行查询。如果没有索引,MongoDB必须执行全集合扫描,即扫描集合中的每个文档,以选择与查询语言匹配的文档。这种扫描全集合的查询效率是非常低的,特别在处理大量数据时,查询可能花费几十秒甚至几分钟。如果查询存在适当的索引,MongoDB可以使用该索引限制必须检查的文档数。
索引是特殊的数据结构,它以易于遍历的形式存储集合数据集的一小部分,索引存储特定字段或一组字段的值,按字段值排序。索引项的排序支持有效的相等匹配和基于范围的查询操作。此外,MongoDB还可以使用索引中的排序返回排序结果。
2)单字段索引:MongoDB支持在文档的单个字段上创建用户定义的升序/降序索引,成为单字段索引,对于单个字段索引和排序操作,索引键的排序顺序并不重要,因为MongoDB可以在任何方向上遍历索引
3)复合索引:MongoDB还支持多个字段的用户定义索引,即复合索引。复合索引中列出的字段顺序具有重要意义,例如:如果复合索引由(userid:1,score:-1)组成,则索引首先按userid正序排序,然后在每个userid的值内,再在score倒序排序。
4)其他索引
地理空间索引:为了支持对地理空间坐标数据的有效查询,MongoDB提供了两种特殊的索引:返回结果时使用平面几何的二维索引和返回结果时使用球面几何的二维球面索引
文本索引:MongoDB提供了一种文本索引类型,支持在集合中搜索字符串内容。这些文本索引不存储特定于语言的停止词(例如"the","a","or"),而将集合中的词作为词干,只存储根词
哈希索引:为了支持基于散列的分片,MongoDB提供了散列索引类型,它对字段值的散列进行索引。这些索引在其范围内的值分布更加随机,但只支持相等匹配,不支持基于范围的查询
4.10)索引的管理操作
返回一个集合中的所有索引的数组:db.collection.getIndexes()
在集合上创建索引:db.collection.createIndex(keys,options)
keys:指定建立索引的字段
options:可选项,unique(建立的索引是否唯一,指定为true创建唯一索引,默认值为false),name(指定为索引的名称)
1)单字段索引示例:对userid字段建立索引
db.comment.createIndex({userid:1})
db.comment.getIndexes()
2)复合索引:对userid和nickname同时建立复合(Compound)索引:
db.comment.createIndex({userid:1,nickname:-1})
db.comment.getIndexes()
3)索引的移除
3.1)指定索引的移除:db.collections.dropIndex(index)
例如:删除comment集合中userid字段上的升序索引:
db.comment.dropIndex({userid:1})
3.2)移除所有索引:db.collection.dropIndexes()
删除spit集合中所有索引:db.comment.dropIndexes()
4.11)索引的使用
执行计划:分析查询性能通常使用执行计划(解释计划,Explain Plan)来查看查询的情况,如查询耗费的时间,是否基于索引查询等
语法:db.collection.find(query,option).explain(options)
例如:查看根据userid查询数据的情况
db.comment.find({userid:"1003"}).explain()
1)涵盖的查询
当查询条件和查询的投影仅包含索引字段时,MongoDB直接从索引返回结果,而不扫描任何文档或将文档带入内存,这些覆盖的查询可以非常有效
db.comment.find({userid:"1003"},{userid:1,_id:0})
5.0)集群和安全性
1)简介:MongoDB中的副本集是一组维护相同数据集的mongodb服务,副本集可提供冗余和高可用性,是所有生产部署的基础
1.冗余和数据可用性 2.MongoDB中的申请 3.主从复制和副本集区别
2)副本集的两种类型三种角色
两种类型:
主节点(primary)类型:数据操作的主要连接点,可读写
次要(辅助,从)节点类型:数据冗余备份节点,可以读或选举
三种角色:
主要成员(primary):主要接受所有写操作,就是主节点
副本成员(Replicate):从主节点通过复制操作以维护相同的数据集,即备份数据,不可写操作,但可以读操作(需要配置),是默认的一种从节点类型
仲裁者(Arbiter):不保留任何数据的副本,只有投票选举的作用,当然也可以将仲裁服务器维护为副本集的一副本,即副本成员同时也可以是仲裁者,也是一种从节点类型
3)副本集架构目标
一主一副本一仲裁
3.1)第一步:创建主节点
建立存放数据和日志的目录
# 主节点
mkdir -p /mongodb/replica_sets/myrs_27017/log
mkdir -p /mongodb/replica_sets/myrs_27017/data/db
# 新建或修改配置文件:
vim /mongodb/replica_sets/myrs_27017/mongod.conf
#配置文件内容如下:
systemLog:
# Mongodb发送所有日志输出的目标指定为文件
destination: file
# Mongod日志记录路径
path: "/mongodb/replica_sets/myrs_27017/log/mongod.log"
# 当mongod实例重启时,会将新条目附加到现有日志文件的末尾
logAppend: true
storage:
# mongod实例存储其数据的目录,storage.dbPath设置仅适用于mongod
dbPath: "/mongodb/replica_sets/myrs_27017/data/db"
journal:
# 启用或禁用持久性日志以确保数据文件保持有效和可恢复
enabled: true
processManagement:
# 启用在后台运行mongod进程的守护进程模式
fork: true
# 指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID
pidFilePath: "/mongodb/replica_sets/myrs_27017/log/mongod.pid"
net:
# 服务实例绑定的IP,默认是localhost
bindIp: 192.168.228.128
# 绑定的端口,默认是27017
port: 27017
replication:
# 副本集的名称
replSetName: myrs
#启动节点服务:/usr/local/mongodb/bin/mongod -f /mongodb/replica_sets/myrs_27017/mongod.conf
3.2)第二步:创建副本节点
# 建立存放数据和日志的目录
mkdir -p /mongodb/replica_sets/myrs_27018/log
mkdir -p /mongodb/replica_sets/myrs_27018/data/db
# 新建或修改配置文件:
vim /mongodb/replica_sets/myrs_27018/mongod.conf
#配置文件内容如下:
systemLog:
# Mongodb发送所有日志输出的目标指定为文件
destination: file
# Mongod日志记录路径
path: "/mongodb/replica_sets/myrs_27018/log/mongod.log"
# 当mongod实例重启时,会将新条目附加到现有日志文件的末尾
logAppend: true
storage:
# mongod实例存储其数据的目录,storage.dbPath设置仅适用于mongod
dbPath: "/mongodb/replica_sets/myrs_27018/data/db"
journal:
# 启用或禁用持久性日志以确保数据文件保持有效和可恢复
enabled: true
processManagement:
# 启用在后台运行mongod进程的守护进程模式
fork: true
# 指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID
pidFilePath: "/mongodb/replica_sets/myrs_27018/log/mongod.pid"
net:
# 服务实例绑定的IP,默认是localhost
bindIp: 192.168.228.128
# 绑定的端口,默认是27018
port: 27018
replication:
# 副本集的名称
replSetName: myrs
#启动节点服务:/usr/local/mongodb/bin/mongod -f /mongodb/replica_sets/myrs_27018/mongod.conf
3.3)第三步:创建仲裁节点
# 建立存放数据和日志的目录
mkdir -p /mongodb/replica_sets/myrs_27019/log
mkdir -p /mongodb/replica_sets/myrs_27019/data/db
# 新建或修改配置文件:
vim /mongodb/replica_sets/myrs_27019/mongod.conf
#配置文件内容如下:
systemLog:
# Mongodb发送所有日志输出的目标指定为文件
destination: file
# Mongod日志记录路径
path: "/mongodb/replica_sets/myrs_27019/log/mongod.log"
# 当mongod实例重启时,会将新条目附加到现有日志文件的末尾
logAppend: true
storage:
# mongod实例存储其数据的目录,storage.dbPath设置仅适用于mongod
dbPath: "/mongodb/replica_sets/myrs_27019/data/db"
journal:
# 启用或禁用持久性日志以确保数据文件保持有效和可恢复
enabled: true
processManagement:
# 启用在后台运行mongod进程的守护进程模式
fork: true
# 指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID
pidFilePath: "/mongodb/replica_sets/myrs_27019/log/mongod.pid"
net:
# 服务实例绑定的IP,默认是localhost
bindIp: 192.168.228.128
# 绑定的端口,默认是27019
port: 27019
replication:
# 副本集的名称
replSetName: myrs
#启动节点服务:/usr/local/mongodb/bin/mongod -f /mongodb/replica_sets/myrs_27019/mongod.conf
# 查看mongo的进程服务:ps -ef |grep mongo
3.4)第四步:
1)初始化配置副本集和主节点
使用客户端命令连接任意一个节点,但这里尽量要连接主节点(27017节点):
/usr/local/mongodb/bin/mongo --host=192.168.228.128 --port=27017
结果,连接上之后,很多命令无法使用,比如show dbs等,必须初始化副本集才行
使用默认的配置来初始化新的副本集:
rs.initiate()
提示:
3.4.1)”ok“的值为1,说明创建成功
3.4.2)命令行操作符发生变化,变成了一个从节点角色,此时默认不能读写,稍等片刻,回车,变成主节点
2)添加副本从节点
在主节点添加从节点,将其他成员加入到副本集
语法:rs.add(host,arbiteronly)
示例:将27018的副本节点添加到副本集中
rs.add("192.168.228.128:27018")
说明:"ok":1,说明添加成功
3):添加仲裁从节点
语法:rs.addArb(host)
示例:将27019的仲裁节点添加到副本集中
rs.addArb("192.168.228.128:27019")
说明:"ok":1,说明添加成功
3.5)第五步:查看副本集的配置内容
语法:rs.config()
说明:
1)"_id":"myrs":副本集的配置数据存储的主键值,默认就是副本集的名字
2)"members":副本集成员数组,此时只有一个:"host":"192.168.288.128:27017",该成员不是仲裁节点:"arbiteronly":false,优先级(权重值):"priority":1
3)"settings":副本集的参数配置
3.6)第六步:查看副本集状态
语法:rs.status()
查看"members":副本集成员数组,此时有主节点,副本从节点,仲裁节点三个
3.7)副本集的数据读写操作
目标:测试三个不同角色的节点的数据读写情况
3.7.1)登录主节点27017,写入和读取数据:
/usr/local/mongodb/bin/mongo --host=192.168.228.128 --port=27017
创建库:
use articledb
插入数据:
db.comment.insert({"articleid":"10000","content":"今天天气真好,阳光明媚","userid":"1001",
"nickname":"Rose","createdatetime":new Date(),"likenum":NumberInt(10),"state":null})
查看数据:
db.comment.find()
3.7.2)登录从节点27018
/usr/local/mongodb/bin/mongo --host=192.168.228.128 --port=27018
查看库:
show dbs
发现不能读取集合的数据,当前从节点只是一个备份,不是奴隶节点,无法读取数据,写当前更不行。
因为默认情况下,从节点是没有读写权限的,可以增加读的权限,但需要进行设置。设置为奴隶节点,允许在从成员上运行读的操作
语法:rs.secondaryOk()
切换库:
use articledb
查看文档内容:
db.comment.find()
如果要取消奴隶节点的读权限:
语法:rs.secondaryOk(false)
查看文档内容:
db.comment.find()
3.7.3)登录仲裁节点27019
仲裁者节点,不存放任何业务数据的,可以登录查看
/usr/local/mongodb/bin/mongo --host=192.168.228.128 --port=27019
3.8)主节点的选举原则
MongoDB在副本集中,会自动进行主节点的选举,主节点选举的触发条件
1.主节点故障
2.主节点网络不可达(默认心跳信息为10秒)
3.人工干预
选举规则是根据票数来决定谁获胜:
票数最高:且获得了"大多数"成员的投票支持的节点获胜
若票数相同:且都获得了"大多数"成员的投票支持的,数据新的节点获胜
3.8.1)故障测试
1.副本节点故障测试:
关闭27018副本节点:发现,主节点和仲裁节点对27018的心跳失败,因为主节点还在,所以,没有触发投票选举。
如果此时,在主节点写入数据,在启动从节点,会发现,主节点写入的数据,会自动同步给从节点
2.主节点故障测试:
关闭27017节点:发现,从节点和仲裁节点对27017的心跳失败,当失败超过10秒,此时因为没有主节点,会自动发起投票。
而副本节点只有27018,因此,候选人只有一个就是27018,开始投票。
27019向27018投了一票,27018本身自带一票,因此共两票,超过了"大多数"
27019是仲裁节点,没有选举权,27018不能向其投票,其票数是0
最终结果,27018成为主节点,具备读写功能
在27018写入数据查看。
再启动27017节点,发现27017变成了从节点,27018仍保持主节点
登录27017节点,发现是从节点了,数据自动从27018同步
从而实现了高可用
3.仲裁节点和主节点故障:
先关掉仲裁节点27019
关掉现在的主节点27018
登录27017后,发现27017仍然是从节点,副本集中没有主节点了,导致此时,副本集是只读状态,无法写入。
为啥不选举了?因为27017的票数,没有获得大多数,即没有大于等于2,它只有默认的一票(优先级是1)
如果要触发选举,随便加入一个成员即可
如果只加入27019仲裁节点成员,则主节点一定是27017,因为没得选了,仲裁节点不参与选举,但参与投票
如果只加入27018节点,会发起选举,因为27017和27018都是两票,则按照谁数据新,谁当主节点
4.仲裁节点和从节点故障:
先关掉仲裁节点27019
关掉现在的副本节点27018
10秒后,27017主节点自动降级为副本节点(服务降级)
副本集不可写数据了,已经故障了