1.应用场景
(1)数据量大
(2)写入操作频繁(读写都很频繁)
(3)价值较低的数据,对事务性要求不高
什么时候选择 MongoDB
在架构选型上,除了上述的三个特点外,如果你还犹豫是否要选择它?可以考虑以下的一些问题:
如果上述有1个符合,可以考虑 MongoDB,2个及以上的符合,选择 MongoDB 绝不会后悔。
2.简介
MongoDB是一个开源、高性能、无模式的文档型数据库,当初的设计就是用于简化开发和方便扩展,是NoSQL数据库产品中的一种。是最像关系型数据库(MySQL)的非关系型数据库。
它支持的数据结构非常松散,是一种类似于 JSON 的 格式叫BSON,所以它既可以存储比较复杂的数据类型,又相当的灵活。
MongoDB中的记录是一个文档,它是一个由字段和值对(fifield:value)组成的数据结构。MongoDB文档类似于JSON对象,即一个文档认为就是一个对象。字段的数据类型是字符型,它的值除了使用基本的一些类型外,还可以包括其他文档、普通数组和文档数组。
3.体系结构
MySQL和MongoDB对比
?
4.数据模型
MongoDB的最小存储单位就是文档(document)对象。文档(document)对象对应于关系型数据库的行。数据在MongoDB中以BSON(Binary-JSON)文档的格式存储在磁盘上。
BSON(Binary Serialized Document Format)是一种类json的一种二进制形式的存储格式,简称Binary JSON。BSON和JSON一样,支持内嵌的文档对象和数组对象,但是BSON有JSON没有的一些数据类型,如Date和BinData类型。
BSON采用了类似于 C 语言结构体的名称、对表示方法,支持内嵌的文档对象和数组对象,具有轻量性、可遍历性、高效性的三个特点,可以有效描述非结构化数据和结构化数据。这种格式的优点是灵活性高,但它的缺点是空间利用率不是很理想。
Bson中,除了基本的JSON类型:string,integer,boolean,double,null,array和object,mongo还使用了特殊的数据类型。这些类型包括date,object id,binary data,regular expression 和code。每一个驱动都以特定语言的方式实现了这些类型,查看你的驱动的文档来获取详细信息。
BSON数据类型参考列表:
提示:
shell默认使用64位浮点型数值。{“x”:3.14}或{“x”:3}。对于整型值,可以使用NumberInt(4字节符号整数)或NumberLong(8字节符号整数),{“x”:NumberInt(“3”)}{“x”:NumberLong(“3”)}
5.特点
MongoDB主要有如下特点:
(1)高性能:
MongoDB提供高性能的数据持久性。特别是,对嵌入式数据模型的支持减少了数据库系统上的I/O活动。索引支持更快的查询,并且可以包含来自嵌入式文档和数组的键。(文本索引解决搜索的需求、TTL索引解决历史数据自动过期的需求、地理位置索引可用于构建各种 O2O 应用)
mmapv1、wiredtiger、mongorocks(rocksdb)、in-memory 等多引擎支持满足各种场景需求。
Gridfs解决文件存储的需求。
(2)高可用性:
MongoDB的复制工具称为副本集(replica set),它可提供自动故障转移和数据冗余。
(3)高扩展性:
MongoDB提供了水平可扩展性作为其核心功能的一部分。分片将数据分布在一组集群的机器上。(海量数据存储,服务能力水平扩展),从3.4开始,MongoDB支持基于片键创建数据区域。在一个平衡的集群中,MongoDB将一个区域所覆盖的读写只定向到该区域内的那些
片。
(4)丰富的查询支持:
MongoDB支持丰富的查询语言,支持读和写操作(CRUD),比如数据聚合、文本搜索和地理空间查询等。
(5)其他特点:如无模式(动态模式)、灵活的文档模型、
6. 部署
6.1 Shell连接
在命令提示符输入以下shell命令即可完成登陆
mongo
或
mongo --host=127.0.0.1 --port=27017
查看已经有的数据库
>show databases
退出mongodb
exit
更多参数可以通过帮助查看:
mongo --help
提示:
MongoDB javascript shell是一个基于javascript的解释器,故是支持js程序的。
6.2 Compass-图形化界面客户端
.exe文件,配置pi和端口后连接
6.3 Linux系统中的安装启动和连接
systemLog:
#MongoDB发送所有日志输出的目标指定为文件
#The path of the log file to which mongod or mongos should send all diagnostic logging information
destination: file
#mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径
path: "/opt/software/mongodb-linux-x86_64-4.0.10/single/log/mongod.log"
#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。
logAppend: true
storage:
#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。
dbPath: "/opt/software/mongodb-linux-x86_64-4.0.10/single/data/db"
journal:
#启用或禁用持久性日志以确保数据文件保持有效和可恢复。
enabled: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
net:
#服务实例绑定的IP,默认是localhost
bindIp: localhost,172.26.49.15
#bindIp
#绑定的端口,默认是27017
port: 27017
mongod -f /mongodb/single/mongod.conf
注意:
如果启动后不是 successfully ,则是启动失败了。原因基本上就是配置文件有问题。
ps -ef |grep mongod
- 分别使用mongo命令和compass工具来连接测试。
????????提示:如果远程连接不上,需要配置防火墙放行,或直接关闭linux防火墙
#查看防火墙状态
systemctl status firewalld
#临时关闭防火墙
systemctl stop firewalld
#开机禁止启动防火墙
systemctl disable firewalld
- 如果一旦是因为数据损坏,则需要进行如下操作(了解):
- 删除lock文件:
- rm -f /mongodb/single/data/db/*.lock
- 修复数据:
- mongod --repair --dbpath=/mongodb/single/data/db
- 标准关闭方法(数据不容易出错,但是很麻烦
- 目标:通过mongo客户端中的shutdownServer命令来关闭服务
- 主要的操作步骤参考如下:
//客户端登录服务,注意,这里通过localhost登录,如果需要远程登录,必须先登录认证才行。
mongo --port 27017
//#切换到admin库
use admin
//关闭服务
db.shutdownServer()
6.5 需要配置环境变量
7.常用命令
7.1 案例需求
存放文章评论的数据存放到MongoDB中,数据结构参考如下:
数据库:articledb
7.2 数据库操作
7.2.1 选择和创建数据库
选择和创建数据库的语法格式:
use 数据库名称
如果数据库不存在则自动创建,例如,以下语句创建 spitdb 数据库:
use articledb
查看有权限查看的所有的数据库命令
show dbs
或
show databases
注意:在 MongoDB 中,集合只有在内容插入后才会创建! 就是说,创建集合(数据表)后要再插入一个文档(记录),集合才会真正创建。
查看当前正在使用的数据库命令
db
MongoDB 中默认的数据库为 test,如果你没有选择数据库,集合将存放在 test 数据库中。
有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。
-
admin: 从权限的角度来看,这是"root"数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。 -
local: 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合 -
config: 当Mongo用于分片设置时,confifig数据库在内部使用,用于保存分片的相关信息。
7.2.2 数据库的删除
MongoDB 删除数据库的语法格式如下:
db.dropDatabase()
提示:主要用来删除已经持久化的数据库
7.3 集合操作
集合,类似关系型数据库中的表。
可以显示的创建,也可以隐式的创建。
7.3.1 集合的显式创建(了解)
基本语法格式:
db.createCollection(name)
参数说明:
例如:创建一个名为 mycollection 的普通集合。
db.createCollection("mycollection")
查看当前库中的表:show tables命令
show collections
或
show tables
7.3.2 集合的隐式创建
当向一个集合中插入一个文档的时候,如果集合不存在,则会自动创建集合。
详见 文档的插入 章节。
提示:通常我们使用隐式创建文档即可。
7.3.3 集合的删除
集合删除语法格式如下:
db.collection.drop()
或
db.集合.drop()
返回值
如果成功删除选定集合,则 drop() 方法返回 true,否则返回 false。
例如:要删除mycollection集合
db.mycollection.drop()
7.4 文档基本CRUD
文档(document)的数据结构和 JSON 基本一样。
所有存储在集合中的数据都是 BSON 格式。
7.4.1 文档的基本插入
db.collection.insert(
<document or array of documents>,
{
writeConcern: <document>,
ordered: <boolean>
}
)
参数 :
【示例】
要向comment的集合(表)中插入一条测试数据:
db.comment.insert(
{
"articleid":"100000",
"content":"今天天气真好,阳光明 媚",
"userid":"1001","nickname":"Rose",
"createdatetime":new Date(),
"likenum":NumberInt(10),
"state":null
}
)
提示 :
WriteResult({ "nInserted" : 1 })
注意 :
语法 :
db.collection.insertMany(
[
<document 1> , <document 2>, ... ],
{
writeConcern: <document>,
ordered: <boolean>
}
)
参数:
批量插入多条文章评论:
db.comment.insertMany([
{"_id":"1","articleid":"100001","content":"我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我 他。","userid":"1002","nickname":"相忘于江湖","createdatetime":new Date("2019-08- 05T22:08:15.522Z"),"likenum":NumberInt(1000),"state":"1"}, {"_id":"2","articleid":"100001","content":"我夏天空腹喝凉开水,冬天喝温开水","userid":"1005","nickname":"伊人憔 悴","createdatetime":new Date("2019-08-05T23:58:51.485Z"),"likenum":NumberInt(888),"state":"1"}, {"_id":"3","articleid":"100001","content":"我一直喝凉开水,冬天夏天都喝。","userid":"1004","nickname":"杰克船 长","createdatetime":new Date("2019-08-06T01:05:06.321Z"),"likenum":NumberInt(666),"state":"1"}, {"_id":"4","articleid":"100001","content":"专家说不能空腹吃饭,影响健康。","userid":"1003","nickname":"凯 撒","createdatetime":new Date("2019-08-06T08:18:35.288Z"),"likenum":NumberInt(2000),"state":"1"}, {"_id":"5","articleid":"100001","content":"研究表明,刚烧开的水千万不能喝,因为烫 嘴。","userid":"1003","nickname":"凯撒","createdatetime":new Date("2019-08- 06T11:01:02.521Z"),"likenum":NumberInt(3000),"state":"1"}
]);
提示:
插入时指定了 _id ,则主键就是该值。
如果某条数据插入失败,将会终止插入,但已经插入成功的数据不会回滚掉。
因为批量插入由于数据较多容易出现失败,因此,可以使用try catch进行异常捕捉处理,测试的时候可以不处理。如(了解):
try {
db.comment.insertMany([ {"_id":"1","articleid":"100001","content":"我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我 他。","userid":"1002","nickname":"相忘于江湖","createdatetime":new Date("2019-08- 05T22:08:15.522Z"),"likenum":NumberInt(1000),"state":"1"}, {"_id":"2","articleid":"100001","content":"我夏天空腹喝凉开水,冬天喝温开水","userid":"1005","nickname":"伊人憔 悴","createdatetime":new Date("2019-08-05T23:58:51.485Z"),"likenum":NumberInt(888),"state":"1"}, {"_id":"3","articleid":"100001","content":"我一直喝凉开水,冬天夏天都喝。","userid":"1004","nickname":"杰克船 长","createdatetime":new Date("2019-08-06T01:05:06.321Z"),"likenum":NumberInt(666),"state":"1"}, {"_id":"4","articleid":"100001","content":"专家说不能空腹吃饭,影响健康。","userid":"1003","nickname":"凯 撒","createdatetime":new Date("2019-08-06T08:18:35.288Z"),"likenum":NumberInt(2000),"state":"1"}, {"_id":"5","articleid":"100001","content":"研究表明,刚烧开的水千万不能喝,因为烫 嘴。","userid":"1003","nickname":"凯撒","createdatetime":new Date("2019-08- 06T11:01:02.521Z"),"likenum":NumberInt(3000),"state":"1"} ]); } catch (e) { print (e);
}
7.4.2 文档的基本查询
查询数据的语法格式如下:
db.collection.find(<query>, [projection])
参数:
【示例】
db.comment.find()
或
db.comment.find({})
这里你会发现每条文档会有一个叫_id的字段,这个相当于我们原来关系数据库中表的主键,当你在插入文档记录时没有指定该字段,MongoDB会自动创建,其类型是ObjectID类型。
如果我们在插入文档记录时指定该字段也可以,其类型可以是ObjectID类型,也可以是MongoDB支持的任意类型。
如果我想按一定条件来查询,比如我想查询userid为1003的记录,怎么办?很简单!只 要在fifind()中添加参数即可,参数也是json格式,如下:
db.comment.find({userid:'1003'})
如果你只需要返回符合条件的第一条数据,我们可以使用fifindOne命令来实现,语法和fifind一样。如:查询用户编号是1003的记录,但只最多返回符合条件的第一条记录:
db.comment.findOne({userid:'1003'})
如果要查询结果返回部分字段,则需要使用投影查询(不显示所有字段,只显示指定的字段)。如:查询结果只显示 _id、userid、nickname :
>db.comment.find({userid:"1003"},{userid:1,nickname:1})
{ "_id" : "4", "userid" : "1003", "nickname" : "凯撒" }
{ "_id" : "5", "userid" : "1003", "nickname" : "凯撒" }
默认 _id 会显示。
如:查询结果只显示 、userid、nickname ,不显示 _id :
>db.comment.find({userid:"1003"},{userid:1,nickname:1,_id:0})
{ "userid" : "1003", "nickname" : "凯撒" }
{ "userid" : "1003", "nickname" : "凯撒" }
再例如:查询所有数据,但只显示 _id、userid、nickname :
>db.comment.find({},{userid:1,nickname:1})
7.4.3 文档的更新
更新文档的语法:
db.collection.update(query, update, options)
//或
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ],
hint: <document|string> // Available starting in MongoDB 4.2
}
)
参数:
提示:
主要关注前四个参数即可。
【示例】
如果我们想修改_id为1的记录,点赞量为1001,输入以下语句:
db.comment.update({_id:"1"},{likenum:NumberInt(1001)})
执行后,我们会发现,这条文档除了likenum字段其它字段都不见了?
为了解决这个问题,我们需要使用修改器$set来实现,命令如下:
我们想修改_id为2的记录,浏览量为889,输入以下语句:
db.comment.update({_id:"2"},{$set:{likenum:NumberInt(889)}})
这样就OK啦。
更新所有用户为 1003 的用户的昵称为 凯撒大帝 。
//默认只修改第一条数据
db.comment.update({userid:"1003"},{$set:{nickname:"凯撒2"}})
//修改所有符合条件的数据
db.comment.update({userid:"1003"},{$set:{nickname:"凯撒大帝"}},{multi:true})
提示:如果不加后面的参数,则只更新符合条件的第一条记录
如果我们想实现对某列值在原有值的基础上进行增加或减少,可以使用 $inc 运算符来实现。
需求:对3号数据的点赞数,每次递增1
db.comment.update({_id:"3"},{$inc:{likenum:NumberInt(1)}})
7.4.4 删除文档
删除文档的语法结构:
db.集合名称.remove(条件)
以下语句可以将数据全部删除,请慎用
db.comment.remove({})
如果删除_id=1的记录,输入以下语句
db.comment.remove({_id:"1"})
7.5 文档的分页查询
7.5.1 统计查询
统计查询使用count()方法,语法如下:
db.collection.count(query, options)
参数:
提示:
可选项暂时不使用。
【示例】
统计comment集合的所有的记录数:
db.comment.count()
例如:统计userid为1003的记录条数
db.comment.count({userid:"1003"})
提示:
默认情况下 count() 方法返回符合条件的全部记录条数。
7.5.2 分页列表查询
可以使用limit()方法来读取指定数量的数据,使用skip()方法来跳过指定数量的数据。
基本语法如下所示:
>db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)
如果你想返回指定条数的记录,可以在fifind方法后调用limit来返回结果(TopN),默认值20,例如:
db.comment.find().limit(3)
skip方法同样接受一个数字参数作为跳过的记录条数。(前N个不要),默认值是0
db.comment.find().skip(3)
分页查询:需求:每页2个,第二页开始:跳过前两条数据,接着值显示3和4条数据
//第一页
db.comment.find().skip(0).limit(2)
//第二页
db.comment.find().skip(2).limit(2)
//第三页
db.comment.find().skip(4).limit(2)
7.5.3 排序查询
sort() 方法对数据进行排序,sort() 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列。
语法如下所示:
db.COLLECTION_NAME.find().sort({KEY:1})
或
db.集合名称.find().sort(排序方式)
例如:
对userid降序排列,并对访问量进行升序排列
db.comment.find().sort({userid:-1,likenum:1})
提示:
skip(), limilt(), sort()三个放在一起执行的时候,执行的顺序是先 sort(), 然后是 skip(),最后是显示的 limit(),和命令编写顺序无关。
7.6 文档的更多查询
7.6.1 正则的复杂条件查询
MongoDB的模糊查询是通过正则表达式的方式实现的。格式为:
db.collection.find({field:/正则表达式/})
或
db.集合.find({字段:/正则表达式/})
提示:正则表达式是js的语法,直接量的写法。
例如,我要查询评论内容包含“开水”的所有文档,代码如下:
db.comment.find({content:/开水/})
如果要查询评论的内容中以“专家”开头的,代码如下:
db.comment.find({content:/^专家/})
7.6.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)}})
7.6.3 包含查询
包含使用$in操作符。 示例:查询评论的集合中userid字段包含1003或1004的文档
db.comment.find({userid:{$in:["1003","1004"]}})
不包含使用$nin操作符。 示例:查询评论集合中userid字段不包含1003和1004的文档
db.comment.find({userid:{$nin:["1003","1004"]}})
7.6.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:1000} }]})
7.7 常用命令小结
选择切换数据库:use articledb?
插入数据:db.comment.insert({bson数据})?
查询所有数据:db.comment.find();?
条件查询数据:db.comment.find({条件})?
查询符合条件的第一条记录:db.comment.findOne({条件})?
查询符合条件的前几条记录:db.comment.find({条件}).limit(条数)?
查询符合条件的跳过的记录:db.comment.find({条件}).skip(条数)?
修改数据:db.comment.update({条件},{修改后的数据}) 或db.comment.update({条件},{$set:{要修改部分的字段:数据})?
修改数据并自增某字段值:db.comment.update({条件},{$inc:{自增的字段:步进值}})?
删除数据:db.comment.remove({条件})?
统计查询:db.comment.count({条件})?
模糊查询:db.comment.find({字段名:/正则表达式/})?
条件比较运算:db.comment.find({字段名:{$gt:值}})?
包含查询:db.comment.find({字段名:{$in:[值1,值2]}})或db.comment.find({字段名:{$nin:[值1,值2]}})?
条件连接查询:db.comment.find({$and:[{条件1},{条件2}]})或db.comment.find({$or:[{条件1},{条件2}]})
8.索引-Index
8.1 概述
索引支持在MongoDB中高效地执行查询。如果没有索引,MongoDB必须执行全集合扫描,即扫描集合中的每个文档,以选择与查询语句匹配的文档。这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
如果查询存在适当的索引,MongoDB可以使用该索引限制必须检查的文档数。
索引是特殊的数据结构,它以易于遍历的形式存储集合数据集的一小部分。索引存储特定字段或一组字段的值,按字段值排序。索引项的排序支持有效的相等匹配和基于范围的查询操作。此外,MongoDB还可以使用索引中的排序返回排序结果。
了解:
MongoDB索引使用B树数据结构(确切的说是B-Tree,MySQL是B+Tree)
8.2 索引的类型
8.2.1 单字段索引
MongoDB支持在文档的单个字段上创建用户定义的升序/降序索引,称为单字段索引(Single Field Index)。对于单个字段索引和排序操作,索引键的排序顺序(即升序或降序)并不重要,因为MongoDB可以在任何方向上遍历索引。
8.2.2 复合索引
MongoDB还支持多个字段的用户定义索引,即复合索引(Compound Index)。
复合索引中列出的字段顺序具有重要意义。例如,如果复合索引由 { userid: 1, score: -1 } 组成,则索引首先按userid正序排序,然后在每个userid的值内,再在按score倒序排序。
8.2.3 其他索引
地理空间索引(Geospatial Index)、文本索引(Text Indexes)、哈希索引(Hashed Indexes)
-
地理空间索引(Geospatial Index) 为了支持对地理空间坐标数据的有效查询,MongoDB提供了两种特殊的索引:返回结果时使用平面几何的二维索引和返回结果时使用球面几何的二维球面索引。 -
文本索引(Text Indexes) MongoDB提供了一种文本索引类型,支持在集合中搜索字符串内容。这些文本索引不存储特定于语言的停止词(例如“the”、“a”、“or”),而将集合中的词作为词干,只存储根词。 -
哈希索引(Hashed Indexes) 为了支持基于散列的分片,MongoDB提供了散列索引类型,它对字段值的散列进行索引。这些索引在其范围内的值分布更加随机,但只支持相等匹配,不支持基于范围的查询。
8.3 索引的管理操作
8.3.1 索引的查看
说明:
返回一个集合中的所有索引的数组。
语法:
db.collection.getIndexes()
提示:该语法命令运行要求是MongoDB 3.0+
【示例】
查看comment集合中所有的索引情况
> db.comment.getIndexes()
[
{
"v" : 2,
"key" : {"_id" : 1 }, # 1 表示升序
"name" : "_id_",
"ns" : "articledb.comment"
}
]
结果中显示的是默认 _id 索引
默认_id索引:
MongoDB在创建集合的过程中,在 id 字段上创建一个唯一的索引,默认名字为 _id ,该索引可防止客户端插入两个具有相同值的文档,您不能在_id字段上删除此索引。
注意:该索引是唯一索引,因此值不能重复,即 _id 值不能重复的。在分片集群中,通常使用 _id 作为片键。
8.3.1 索引的创建
说明:
在集合上创建索引。
语法:
db.collection.createIndex(keys, options)
参数:
?options(更多选项)列表:
提示:
注意在 3.0.0 版本前创建索引方法为 db.collection.ensureIndex() ,之后的版本使用了 db.collection.createIndex() 方法,
ensureIndex() 还能用,但只是 createIndex() 的别名。
【示例】
> db.comment.createIndex({userid:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
参数1:按升序创建索引
可以查看一下:
> db.comment.getIndexes()
[
{
"v" : 2,
"key" : {"_id" : 1 },
"name" : "_id_",
"ns" : "articledb.comment"
},
{
"v" : 2,
"key" : {"userid" : 1 },
"name" : "userid_1",
"ns" : "articledb.comment"
}
]
索引名字为`userid_1
compass查看
- ?复合索引:对 userid 和 nickname 同时建立复合(Compound)索引:
> db.comment.createIndex({userid:1,nickname:-1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"numIndexesAfter" : 3,
"ok" : 1
}
查看一下索引:
> db.comment.getIndexes()
[
{
"v" : 2,
"key" : {"_id" : 1 },
"name" : "_id_",
"ns" : "articledb.comment"
},
{
"v" : 2,
"key" : {"userid" : 1 },
"name" : "userid_1",
"ns" : "articledb.comment"
},
{
"v" : 2,
"key" : {"userid" : 1, "nickname" : -1 },
"name" : "userid_1_nickname_-1",
"ns" : "articledb.comment"
}
]
compass中:
8.3.2 索引的移除
说明:可以移除指定的索引,或移除所有索引
一、指定索引的移除
语法
db.collection.dropIndex(index)
参数:
【示例】
删除 comment 集合中 userid 字段上的升序索引:
> db.comment.dropIndex({userid:1})
{
"nIndexesWas" : 3,
"ok" : 1
}
查看已经删除了。
所有索引的移除
语法:
db.collection.dropIndexes()
【示例】
删除 spit 集合中所有索引。
> db.comment.dropIndexes()
{
"nIndexesWas" : 2,
"msg" : "non-_id indexes dropped for collection",
"ok" : 1
}
提示: _id 的字段的索引是无法删除的,只能删除非 _id 字段的索引。
8.4 索引的使用
8.4.1 执行计划
分析查询性能(Analyze Query Performance)通常使用执行计划(解释计划、Explain Plan)来查看查询的情况,如查询耗费的时间、是否基于索引查询等。
那么,通常,我们想知道,建立的索引是否有效,效果如何,都需要通过执行计划查看。
语法:
db.collection.find(query,options).explain(options)
【示例】
查看根据userid查询数据的情况:
?关键点看: "stage" : "COLLSCAN", 表示全集合扫描
?下面对userid建立索引
> db.comment.createIndex({userid:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2, "ok" : 1
}
再次查看执行计划:
关键点看: "stage" : "IXSCAN" ,基于索引的扫描
compass查看:
8.4.2 涵盖的查询
Covered Queries
当查询条件和查询的投影仅包含索引字段时,MongoDB直接从索引返回结果,而不扫描任何文档或将文档带入内存。 这些覆盖的查询可以
非常有效。
更多:Query Optimization — MongoDB Manual
Compass中:
9.文章评论
9.1 需求分析
某头条的文章评论业务如下:
文章示例参考:早晨空腹喝水,是对还是错?今日头条
需要实现以下功能
-
基本增删改查API -
根据文章id查询评论 -
评论点赞
9.2 表结构分析
数据库:articledb
9.3 技术选型
9.3.1 mongodb-driver(了解)
mongodb-driver是mongo官方推出的java连接mongoDB的驱动包,相当于JDBC驱动。我们通过一个入门的案例来了解mongodb-driver的基本使用。
官方驱动说明和下载:MongoDB Java Drivers
官方驱动示例文档:Quick Start
9.3.2 SpringDataMongoDB
SpringData家族成员之一,用于操作MongoDB的持久层框架,封装了底层的mongodb-driver。
官网主页: Redirecting…
我们十次方项目的吐槽微服务就采用SpringDataMongoDB框架
9.4 文章微服务模块的搭建
(1) 搭建项目工程article,pom.xml引入依赖:
<parent>
<groupId>org.springframework.boot</groupId>
<version>2.1.6.RELEASE</version>
<artifactId>spring-boot-starter-parent</artifactId>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
</dependencies>
?(2) 创建application.yml?
spring:
data:
mongodb:
host: 116.62.71.234
database: articledb
port: 27017
(3) 创建启动类
@SpringBootApplication
public class ArticleApplication {
public static void main(String[] args) {
SpringApplication.run(ArticleApplication.class, args);
}
}
(4) 启动项目,看是否能正常启动,控制台没有错误。
9.5 文章评论实体类的编写
创建实体类 创建包cn.itcast.article,包下建包po用于存放实体类,创建实体类
@CompoundIndex(def = "{userid:1,nickname:-1}")
@Document(collection = "comment") //@Document(collection="mongodb 对应 collection 名")
@Data
public class Comment implements Serializable {
@Id //key
private String id;
@Field("content") //该属性对应mongodb的字段的名字,如果一致,则无需该注解
private String content; //吐槽内容
private Date publishtime;
@Indexed
private String userid;
private String nickname;
private LocalDateTime createdatetime;
private Integer likenum;
private Integer replynum;
private String state;
private String parentId;
private String articleId;
@Override
public String toString() {
return "Comment{" +
"id='" + id + '\'' +
", content='" + content + '\'' +
", publishtime=" + publishtime +
", userid='" + userid + '\'' +
", nickname='" + nickname + '\'' +
", createdatetime=" + createdatetime +
", likenum=" + likenum +
", replynum=" + replynum +
", state='" + state + '\'' +
", parentId='" + parentId + '\'' +
", articleId='" + articleId + '\'' +
'}';
}
}
创建复合索引
@CompoundIndex(def = "{userid:1,nickname:-1}")
指示文档主键
@Id
private String id;
对应不同名映射
@Field("content") //该属性对应mongodb的字段的名字,如果一致,则无需该注解
private String content; //吐槽内容
创建索引
@Indexed
private String userid;
建议用控制台创建索引,而不是代码.
说明:
索引可以大大提升查询效率,一般在查询字段上添加索引,索引的添加可以通过Mongo的命令来添加,也可以在Java的实体类中通过注解添加。
1)单字段索引注解@Indexed
org.springframework.data.mongodb.core.index.Indexed.class
声明该字段需要索引,建索引可以大大的提高查询效率。
Mongo命令参考:
db.comment.createIndex({"userid":1})
2)复合索引注解@CompoundIndex
org.springframework.data.mongodb.core.index.CompoundIndex.class
复合索引的声明,建复合索引可以有效地提高多字段的查询效率。
Mongo命令参考:
db.comment.createIndex({"userid":1,"nickname":-1})
9.6 文章评论的基本增删改查
(1)创建数据访问接口 cn.itcast.article包下创建dao包,包下创建接口
public interface CommentRepository extends MongoRepository<Comment,String> {}
(2)创建业务逻辑类 cn.itcast.article包下创建service包,包下创建类
@Service
public class CommentService {
@Autowired
private CommentRepository commentRepository;
public void saveComment(Comment comment){
commentRepository.save(comment);
}
public void updateComment(Comment comment){
commentRepository.save(comment);
}
public void deleteCommentById(String id){
commentRepository.deleteById(id);
}
public List<Comment> findComment(){
return commentRepository.findAll();
}
public Comment findCommentById(String id){
return commentRepository.findById(id).get();
}
}
(3)新建Junit测试类,测试保存和查询所有:
@RunWith(SpringRunner.class)
@SpringBootTest
public class CommentServiceTest {
@Autowired
private CommentService commentService;
@Test
public void testFindCommentList(){
List<Comment> commentList = commentService.findCommentList();
System.out.println(commentList);
}
@Test
public void testFindCommentById(){
Comment comment = commentService.findCommentById("2");
System.out.println(comment);
}
}
9.7 根据上级ID查询文章评论的分页列表
(1)CommentRepository新增方法定义
public interface CommentRepository extends MongoRepository<Comment,String> {
//根据父id,查询子评论的分页列表
Page<Comment> findByParentid(String parentid, Pageable pageable);
}
方法名固定 findBy后面为定义Comment实体类的字段名
(2)CommentService新增方法
/**
* 根据父id查询分页列表
* @param parentid
* @param page
* @param size
* @return
*/
public Page<Comment> findCommentListPageByParentid(String parentid,int page,int size){
return commentRepository.findByParentid(parentid, PageRequest.of(page-1, size));
}
(3)junit测试用例:
@Test
public void testFindCommentByParentid(){
Page<Comment> page = commentService.findCommentListPageByParentid("3", 1, 2);
System.out.println(page.getTotalElements());
System.out.println(page.getContent());
}
(4)测试
9.8 MongoTemplate实现评论点赞
我们看一下以下点赞的临时示例代码: CommentService 新增updateThumbup方法
public void updateCommentThumbupToIncrementingOld(String id)P{
Comment comment = CommentRepository.findByid(id).get();
comment.setLikenum(comment.getLikenum()+1);
CommentRepository.save(comment);
}
以上方法虽然实现起来比较简单,但是执行效率并不高,因为我只需要将点赞数加1就可以了,没必要查询出所有字段修改后再更新所有字段。(蝴蝶效应)
我们可以使用MongoTemplate类来实现对某列的操作。
(1)修改CommentService
@Autowired
private MongoTemplate mongoTemplate;
public void updateCommentLikenum(String id){
//查询条件
Query query = Query.query(Criteria.where("_id").is(id));
//更新条件
Update update = new Update();
update.inc("likenum");
mongoTemplate.updateFirst(query, update, Comment.class);
}
(2)测试用例:
@Test
public void testUpdateCommentLikenum(){
commentService.updateCommentLikenum("3");
}
cn.itcast.article.service.CommentServiceTest
执行测试用例后,发现点赞数+1了:
10. 副本集-Replica Set
10.1简介
MongoDB中的副本集(Replica Set)是一组维护相同数据集的mongod服务。 副本集可提供冗余和高可用性,是所有生产部署的基础。
也可以说,副本集类似于有自动故障恢复功能的主从集群。通俗的讲就是用多台机器进行同一数据的异步同步,从而使多台机器拥有同一数据的多个副本,并且当主库当掉时在不需要用户干预的情况下自动切换其他备份服务器做主库。而且还可以利用副本服务器做只读服务器,实现读写分离,提高负载。
(1)冗余和数据可用性
复制提供冗余并提高数据可用性。 通过在不同数据库服务器上提供多个数据副本,复制可提供一定级别的容错功能,以防止丢失单个数据库服务器。
在某些情况下,复制可以提供增加的读取性能,因为客户端可以将读取操作发送到不同的服务上, 在不同数据中心维护数据副本可以增加分布式应用程序的数据位置和可用性。 您还可以为专用目的维护其他副本,例如灾难恢复,报告或备份。
(2)MongoDB中的复制
副本集是一组维护相同数据集的mongod实例。 副本集包含多个数据承载节点和可选的一个仲裁节点。在承载数据的节点中,一个且仅一个成员被视为主节点,而其他节点被视为次要(从)节点。
主节点接收所有写操作。 副本集只能有一个主要能够确认具有{w:“most”}写入关注的写入; 虽然在某些情况下,另一个mongod实例可能暂时认为自己也是主要的。主要记录其操作日志中的数据集的所有更改,即oplog。
辅助(副本)节点复制主节点的oplog并将操作应用于其数据集,以使辅助节点的数据集反映主节点的数据集。 如果主要人员不在,则符合条件的中学将举行选举以选出新的主要人员。
(3)主从复制和副本集区别
主从集群和副本集最大的区别就是副本集没有固定的“主节点”;整个集群会选出一个“主节点”,当其挂掉后,又在剩下的从节点中选中其他节点为“主节点”,副本集总有一个活跃点(主、primary)和一个或多个备份节点(从、secondary)。
10.2 副本集的三个角色
副本集有两种类型三种角色
两种类型:
三种角色:
-
主要成员(Primary):主要接收所有写操作。就是主节点。 -
副本成员(Replicate):从主节点通过复制操作以维护相同的数据集,即备份数据,不可写操作,但可以读操作(但需要配置)。是默认的一种从节点类型。 -
仲裁者(Arbiter):不保留任何数据的副本,只具有投票选举作用。当然也可以将仲裁服务器维护为副本集的一部分,即副本成员同时也可以是仲裁者。也是一种从节点类型
关于仲裁者的额外说明:
-
您可以将额外的mongod实例添加到副本集作为仲裁者。 仲裁者不维护数据集。 仲裁者的目的是通过响应其他副本集成员的心跳和选举请求来维护副本集中的仲裁。 因为它们不存储数据集,所以仲裁器可以是提供副本集仲裁功能的好方法,其资源成本比具有数据集的全功能副本集成员更便宜。 -
如果您的副本集具有偶数个成员,请添加仲裁者以获得主要选举中的“大多数”投票。 仲裁者不需要专用硬件。(凑数的) -
仲裁者将永远是仲裁者,而主要人员可能会退出并成为次要人员,而次要人员可能成为选举期间的主要人员。 -
如果你的副本+主节点的个数是偶数,建议加一个仲裁者,形成奇数,容易满足大多数的投票。 -
如果你的副本+主节点的个数是奇数,可以不加仲裁者。
10.3 创建三个mongod实例
10.4 初始化配置副本集和主节点
三个mongod实例虽然初始化好了,但是并没有什么关系,需要在主节点上配置副本集,并且将其他节点加入进来.
rs.conf(configuration)
或
rs.config() 是该方法的别名。
rs.status()
rs.add(host, arbiterOnly)
????????????????arbiterOnly : 为true时,添加的为仲裁节点
rs.slaveOk()
#或
rs.slaveOk(true)
rs.slaveOk(false)
10.5 主节点选举原则
MongoDB在副本集中,会自动进行主节点的选举,主节点选举的触发条件:
1) 主节点故障
2) 主节点网络不可达(默认心跳信息为10秒)
3) 人工干预(rs.stepDown(600))
一旦触发选举,就要根据一定规则来选主节点。
选举规则是根据票数来决定谁获胜:
-
票数最高,且获得了“大多数”成员的投票支持的节点获胜 “大多数”的定义为:假设复制集内投票成员数量为N,则大多数为 N/2 + 1。例如:3个投票成员,则大多数的值是2。当复制集内存活成员数量不足大多数时,整个复制集将无法选举出Primary,复制集将无法提供写服务,处于只读状态。若票数相同,且都获得了“大多数”成员的投票支持 -
若票数相同,且都获得了“大多数”成员的投票支持的,数据新的节点获胜。数据的新旧是通过操作日志oplog来对比的。
在获得票数的时候,优先级(priority)参数影响重大。
可以通过设置优先级(priority)来设置额外票数。优先级即权重,取值为0-1000,相当于可额外增加0-1000的票数,优先级的值越大,就越可能获得多数成员的投票(votes)数。指定较高的值可使成员更有资格成为主要成员,更低的值可使成员更不符合条件。
【了解】修改优先级
比如,下面提升从节点的优先级:
1)先将配置导入cfg变量
myrs:SECONDARY> cfg=rs.conf()
2)然后修改值(ID号默认从0开始):
myrs:SECONDARY> cfg.members[1].priority=2
2
3)重新加载配置
myrs:SECONDARY> rs.reconfig(cfg) { "ok" : 1 }
稍等片刻会重新开始选举
10.6 故障测试
10.6.1 副本节点故障测试
关闭27018副本节点:发现,主节点和仲裁节点对27018的心跳失败。因为主节点还在,因此,没有触发投票选举,如果此时,在主节点写入数据。再启动从节点,会发现,主节点写入的数据,会自动同步给从节点
10.6.2 主节点故障测试
关闭27017节点,发现,从节点和仲裁节点对27017的心跳失败,当失败超过10秒,此时因为没有主节点了,会自动发起投票。而副本节点只有27018,因此,候选人只有一个就是27018,开始投票.27019向27018投了一票,27018本身自带一票,因此共两票,超过了“大多数”,27019是仲裁节点,没有选举权,27018不向其投票,其票数是0,最终结果,27018成为主节点。具备读写功能。在27018写入数据查看。
再启动27017节点,发现27017变成了从节点,27018仍保持主节点。登录27017节点,发现是从节点了,数据自动从27018同步。从而实现了高可用。
10.6.3 仲裁节点和主节点故障
先关掉仲裁节点27019
关掉现在的主节点27018
登录27017后,发现,27017仍然是从节点,副本集中没有主节点了,导致此时,副本集是只读状态,无法写入。
为啥不选举了?因为27017的票数,没有获得大多数,即没有大于等于2,它只有默认的一票(优先级是1)
如果要触发选举,随便加入一个成员即可。
10.6.4
先关掉仲裁节点27019
关掉现在的副本节点27018
10秒后,27017主节点自动降级为副本节点。(服务降级)
副本集不可写数据了,已经故障了
10.7 Compass连接副本集
var config = rs.config();
config.members[0].host="180.76.159.126:27017";rs.reconfig(config)
10.8 SpringDataMongoDB 连接副本集
spring:
data:
mongodb:
uri:
mongodb://180.76.159.126:27017,180.76.159.126:27018,180.76.159.126:27019/article db?connect=replicaSet&slaveOk=true&replicaSet=myrs
11.分片集群-Sharded Cluster
11.1 分片概念
分片(sharding)是一种跨多台机器分布数据的方法, MongoDB使用分片来支持具有非常大的数据集和高吞吐量操作的部署。
换句话说:分片(sharding)是指将数据拆分,将其分散存在不同的机器上的过程。有时也用分区(partitioning)来表示这个概念。将数据分散到不同的机器上,不需要功能强大的大型计算机就可以储存更多的数据,处理更多的负载
换句话说:分片(sharding)是指将数据拆分,将其分散存在不同的机器上的过程。有时也用分区(partitioning)来表示这个概念。将数据分散到不同的机器上,不需要功能强大的大型计算机就可以储存更多的数据,处理更多的负载。
有两种解决系统增长的方法:垂直扩展和水平扩展。
垂直扩展意味着增加单个服务器的容量,例如使用更强大的CPU,添加更多RAM或增加存储空间量。可用技术的局限性可能会限制单个机器对于给定工作负载而言足够强大。此外,基于云的提供商基于可用的硬件配置具有硬性上限。结果,垂直缩放有实际的最大值。
水平扩展意味着划分系统数据集并加载多个服务器,添加其他服务器以根据需要增加容量。虽然单个机
器的总体速度或容量可能不高,但每台机器处理整个工作负载的子集,可能提供比单个高速大容量服务器更高的效率。扩展部署容量只需要根据需要添加额外的服务器,这可能比单个机器的高端硬件的总体成本更低。权衡是基础架构和部署维护的复杂性增加。
MongoDB支持通过分片进行水平扩展。
11.2 分片集群包含的组件
MongoDB分片群集包含以下组件:
-
分片(存储):每个分片包含分片数据的子集。 每个分片都可以部署为副本集。 -
mongos(路由):mongos充当查询路由器,在客户端应用程序和分片集群之间提供接口。 -
configservers(“调度”的配置):配置服务器存储群集的元数据和配置设置。 从MongoDB 3.4开始,必须将配置服务器部署为副本集(CSRS)。
下图描述了分片集群中组件的交互:
MongoDB在集合级别对数据进行分片,将集合数据分布在集群中的分片上。
11.3 分片集群架构目标
两个分片节点副本集(3+3)+一个配置节点副本集(3)+两个路由节点(2),共11个服务节点。
11.4 分片(存储)节点副本集的创建
所有的的配置文件都直接放到 sharded_cluster 的相应的子目录下面,默认配置文件名字:mongod.conf
-
创建第一套副本集 -
创建第二套副本集 -
配置节点副本集的创建 -
路由节点的创建和操作
-
此时,写不进去数据,如果写数据会报错:
-
在路由节点上进行分片配置操作
11.5 SpringDataMongDB连接分片集群**
Java客户端常用的是SpringDataMongoDB,其连接的是mongs路由,配置和单机mongod的配置是一样的。
多个路由的时候的SpringDataMongoDB的客户端配置参考如下:
spring:
data:
mongodb:
uri: mongodb://180.76.159.126:27017,180.76.159.126:27117/articledb
连接的地址为两个路由节点,默认有负载均衡策略.
12.安全认证-单实例
默认情况下,MongoDB实例启动运行时是没有启用用户访问权限控制的,也就是说,在实例本机服务器上都可以随意连接到实例进行各种操作,MongoDB不会对连接客户端进行用户验证,这是非常危险的。
mongodb官网上说,为了能保障mongodb的安全可以做以下几个步骤:
1)使用新的端口,默认的27017端口如果一旦知道了ip就能连接上,不太安全。
2)设置mongodb的网络环境,最好将mongodb部署到公司服务器内网,这样外网是访问不到的。公司内部访问使用vpn等
3)开启安全认证。认证要同时设置服务器之间的内部认证方式,同时要设置客户端连接到集群的账号密码认证方式。
在开始之前需要了解一下概念
-
角色 : 在MongoDB中通过角色对用户授予相应数据库资源的操作权限,每个角色当中的权限可以显式指定,也可以通过继承其他角色的权限,或者两都都存在的权限。 -
权限 : 权限由指定的数据库资源(resource)以及允
-
资源(resource)包括:数据库、集合、部分集合和集群; -
操作(action)包括:对资源进行的增、删、改、查(CRUD)操作。许在指定资源上进行的操作(action)组成。
常用的内置角色:
-
数据库用户角色:read、readWrite; -
所有数据库用户角色:readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase -
数据库管理角色:dbAdmin、dbOwner、userAdmin; -
集群管理角色:clusterAdmin、clusterManager、clusterMonitor、hostManager; -
备份恢复角色:backup、restore; -
超级用户角色:root -
内部角色:system
角色说明 :
12.1 单实例环境
目标:对单实例的MongoDB服务开启安全认证,这里的单实例指的是未开启副本集或分片的MongoDB实例。
12.1.1 添加用户和权限
-
先按照普通无授权认证的配置,来配置服务端的配置文件 /mongodb/single/mongod.conf : -
按之前未开启认证的方式(不添加 --auth 参数)来启动MongoDB服务: -
使用Mongo客户端登录: -
创建两个管理员用户,一个是系统的超级管理员 myroot ,一个是admin库的管理用户myadmin :?
use admin
- 创建系统超级用户 myroot,设置密码123456,设置角色root?
db.createUser({user:"myroot",pwd:"myroot",roles:[ { "role" : "root", "db" : "admin" } ]})
或
db.createUser({user:"myroot",pwd:"123456",roles:["root"]})
- 创建专门用来管理admin库的账号myadmin,只用来作为用户权限的管理
db.createUser({user:"myadmin",pwd:"myadmin",roles: [{role:"userAdminAnyDatabase",db:"admin"}]})
db.system.users.find()
db.dropUser("myadmin")
db.changeUserPassword("myroot", "123456")
-
本案例创建了两个用户,分别对应超管和专门用来管理用户的角色,事实上,你只需要一个用户即可。如果你对安全要求很高,防止超管泄漏,则不要创建超管用户。 -
和其它数据库(MySQL)一样,权限的管理都差不多一样,也是将用户和权限信息保存到数据库对应的表中。Mongodb存储所有的用户信息在admin 数据库的集合system.users中,保存用户名、密码和数据库信息。 -
如果不指定数据库,则创建的指定的权限的用户在所有的数据库上有效,如 {role: "userAdminAnyDatabase", db:""}
测试添加的用户是否正确
use admin
db.auth("myroot","12345")
db.auth("myroot","123456")
创建普通用户
创建普通用户可以在没有开启认证的时候添加,也可以在开启认证之后添加,但开启认证之后,必须使用有操作admin库的用户登录认证后才能操作。底层都是将用户信息保存在了admin数据库的集合system.users中。
12.1.2 服务端开启认证和客户端连接登录
-
(1)关闭已经启动的服务 -
(2)以开启认证的方式启动服务
security: #开启授权认证
authorization: enabled
- (3)开启了认证的情况下的客户端登录
-
db.auth("myroot","myroot") 12.1.3 SpringDataMongoDB 连接认证 使用用户名和密码连接到 MongoDB 服务器,你必须使用'username:password@hostname/dbname' 格式,'username'为用户名,'password' 为密码。 目标:使用用户bobo使用密码 123456 连接到MongoDB 服务上。 application.yml:
spring:
data:
mongodb:
uri: mongodb://bobo:123456@116.62.71.234:27017/articledb
13.安全认证-副本集环境
对于搭建好的mongodb副本集,为了安全,启动安全认证,使用账号密码登录。
副本集环境使用之前搭建好的,架构如下:
对副本集执行访问控制需要配置两个方面:
1)副本集和共享集群的各个节点成员之间使用内部身份验证,可以使用密钥文件或x.509证书。密钥文件比较简单,本文使用密钥文件,官方推荐如果是测试环境可以使用密钥文件,但是正式环境,官方推荐x.509证书。原理就是,集群中每一个实例彼此连接的时候都检验彼此使用的证书的内容是否相同。只有证书相同的实例彼此才可以访问
2)使用客户端连接到mongodb集群时,开启访问授权。对于集群外部的访问。如通过可视化客户端,或者通过代码连接的时候,需要开启授权。
在keyfifile身份验证中,副本集中的每个mongod实例都使用keyfifile的内容作为共享密码,只有具有正确密钥文件的mongod或者mongos实例可以连接到副本集。密钥文件的内容必须在6到1024个字符之间,并且在unix/linux系统中文件所有者必须有对文件至少有读的权限。
13.1 通过主节点添加一个管理员帐号
只需要在主节点上添加用户,副本集会自动同步。
开启认证之前,创建超管用户:myroot,密码:123456
use admin
db.createUser({user:"myroot",pwd:"123456",roles:["root"]})
该步骤也可以在开启认证之后,但需要通过localhost登录才允许添加用户,用户数据也会自动同步到副本集。
后续再创建其他用户,都可以使用该超管用户创建
13.2 创建副本集认证的key文件
第一步:生成一个key文件到当前文件夹中。
可以使用任何方法生成密钥文件。例如,以下操作使用openssl生成密码文件,然后使用chmod来更改文件权限,仅为文件所有者提供读取权限
[root@bobohost ~] openssl rand -base64 90 -out ./mongo.keyfile
[root@bobohost ~] chmod 400 ./mongo.keyfile
[root@bobohost ~] ll mongo.keyfile
提示:
所有副本集节点都必须要用同一份keyfile,一般是在一台机器上生成,然后拷贝到其他机器上,且必须有读的权限,否则将来会报错: permissions on /mongodb/replica_sets/myrs_27017/mongo.keyfile are too open
一定要保证密钥文件一致,文件位置随便。但是为了方便查找,建议每台机器都放到一个固定的位置,都放到和配置文件一起的目录中。
13.3 修改配置文件指定keyfile
分别编辑几个服务的mongod.conf文件,添加相关内容:
/mongodb/replica_sets/myrs_27017/mongod.conf
security:
#KeyFile鉴权文件
keyFile: /mongodb/replica_sets/myrs_27017/mongo.keyfile
#开启认证方式运行
authorization: enabled
三个节点的keyfile文件内容必须一致
13.4 重新启动副本集
13.5 在主节点上添加普通账号
use admin
db.auth("myroot","123456")
use articledb
db.createUser({user: "bobo", pwd: "123456", roles: ["readWrite"]})
13.6 SpringDataMongoDB连接副本集
使用用户名和密码连接到 MongoDB 服务器,你必须使用'username:password@hostname/dbname' 格式,'username'为用户名,
'password' 为密码。
目标:使用用户bobo使用密码 123456 连接到MongoDB 服务上。
application.yml:
spring:
data:
mongondb:
uri:
mongodb://bobo:123456@180.76.159.126:27017,180.76.159.126:27018,180.76.159.126:2 7019/articledb? connect=replicaSet&slaveOk=true&replicaSet=myrs
14 安全认证-分片集群
分片集群环境下的安全认证和副本集环境下基本上一样。只需要把keyfile文件布置到所有节点,路由节点,配置节点,分片节点.
15 mongoTempleate 补
mongoTemplate - 简书
15.1 可空的动态查询
//动态SQL
public List<Comment> selectComment(String content,Integer minLikenum){
Query query = new Query(Criteria.where("content").
regex(".*"+content+".*"));
//多条件 判断是否为空
if( minLikenum != null){
query.addCriteria(Criteria.where("likenum").gt(minLikenum));
}
List<Comment> comments = mongoTemplate.find(query, Comment.class);
return comments;
}
//动态SQl
@org.junit.Test
public void testDynamicSQl(){
List<Comment> comments = commentService.selectComment("开水", null);
for (Comment comment : comments) {
System.out.println(comment);
}
}
|