1. MongoDB
MongoDB它支持的数据结构非常松散,是一种类JSON的格式 BSON 它即可以存储比较复杂的数据类型 又相当的灵活
MongoDB中的记录是一个文档 它是一个键值对数据结构 MongoDB文档类似于JSON对象 即一个文档认为就是一个对象
2. 体系结构

3. 数据模型
MongoDB的最小存储单位就是文档(document)对象。文档(document)对象对应于关系型数据库的行。数据在MongoDB中以BSON(Binary-JSON)文档的格式存储在磁盘上。

4. 单机部署
https://www.mongodb.com/try/download/community
MongoDB的版本命名规范如:x.y.z;
y为奇数时表示当前版本为开发版,如:1.5.2、4.1.13;
y为偶数时表示当前版本为稳定版,如:1.6.3、4.0.10;
z是修正版本号,数字越大越好。
4.1. windows启动
解压到本地
在MongoDB目录下创建 data
在data文件夹下 分别创建 db 和 logs 文件夹
进入bin目录下命令行启动
mongod --dbpath=D:\compile\mongodb-win32-x86_64-2012plus-4.2.16\data\db --logpath=D:\compile\mongodb-win32-x86_64-2012plus-4.2.16\data\logs\mongolog.log --logappend
MongoDB默认端口为27017 如果要指定端口可以通过—port来设置
mongod --dbpath=D:\compile\mongodb-win32-x86_64-2012plus-4.2.16\data\db --logpath=D:\compile\mongodb-win32-x86_64-2012plus-4.2.16\data\logs\mongolog.log --logappend --port 27018
4.1.1. 使用配置文件启动
在MongoDB目录下conf目录
进入conf目录创建mongod.conf
storage:dbPath: D:\compile\mongodb-win32-x86_64-2012plus-4.2.16\data\dbsystemLog:destination: filepath: D:\compile\mongodb-win32-x86_64-2012plus-4.2.16\data\logs\mongolog.loglogAppend: true
- 在bin目录下命令行启动
mongod -f ../config/mongod.conf#或者mongod --config ../config/mongod.conf
4.1.2. 连接数据库
- 在bin目录下命令行启动
mongo#或者mongo --host=127.0.0.1 --port=27017
show dbs; #查询数据库exit; #退出
4.1.3. 安装为服务项
在bin目录下执行
mongod --dbpath "D:\compile\mongodb-win32-x86_64-2012plus-4.2.16\data\db" --logpath "D:\compile\mongodb-win32-x86_64-2012plus-4.2.16\data\logs\mongolog.log" --serviceName "mongodb" --serviceDisplayName "mongodb" --installnet start mongodb
4.2. Linux启动
解压安装
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.2.15.tgztar -zxvf mongodb-linux-x86_64-rhel70-4.2.15.tgzmv mongodb-linux-x86_64-rhel70-4.2.15 /usr/local/mongodbmkdir -p /mongodb/single/data/db #数据目录mkdir -p /mongodb/single/log #日志文件vim /mongodb/single/mongod.conf
# 配置内容systemLog:# MongoDB发送所有日志输出的目标指定为文件# The path of the log file to which mongod or mongos should send all diagnostic logging informationdestination: file# mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径path: "/mongodb/single/log/mongod.log"# 当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。logAppend: truestorage:# mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。# The directory where the mongod instance stores its data.Default Value is "/data/db".dbPath: "/mongodb/single/data/db"journal:# 启用或禁用持久性日志以确保数据文件保持有效和可恢复。enabled: trueprocessManagement:# 启用在后台运行mongos或mongod进程的守护进程模式。fork: truenet:# 服务实例绑定的IP,默认是localhostbindIp: localhost,192.168.130.212 #ip为当前服务器局域网ip地址 不设置外部无法通过ip访问# bindIp# 绑定的端口,默认是27017port: 27017
启动
/usr/local/mongodb/bin/mongod -f /mongodb/single/mongod.conf#/usr/local/mongodb/bin/mongod --config /mongodb/single/mongod.conf#查看防火墙状态systemctl status firewalld#临时关闭防火墙systemctl stop firewalld#开机禁止启动防火墙systemctl disable firewalld
如果一旦数据损坏 导致表死锁
rm -f /mongodb/single/data/db/*.lock #删除锁文件/usr/local/mongdb/bin/mongod --repair
4.2.1. 配置环境变量
vi /etc/profile#配置内容export MONGODB_HOME=/usr/local/mongodbexport PATH=$PATH:$MONGODB_HOME/bin
4.2.2. 配置为服务加入启动
cd /lib/systemd/systemvi mongodb.service#配置内容[Unit]Description=mongodbAfter=network.target remote-fs.target nss-lookup.target[Service]Type=forkingExecStart=/usr/local/mongodb/bin/mongod --config /mongodb/single/mongodb.confExecReload=/bin/kill -s HUP $MAINPIDExecStop=/usr/local/mongodb/bin/mongod --shutdown --config /mongodb/single/mongodb.confPrivateTmp=true[Install]WantedBy=multi-user.target
#echo "/usr/local/mongodb/bin/mongod --dbpath=/data/db --fork --bind_ip=0.0.0.0 --port 27017 --logpath=/data/db/log --logappend --auth" >> /etc/rc.localchmod 754 mongodb.service
#启动服务systemctl start mongodb.service#关闭服务systemctl stop mongodb.service#开机启动systemctl enable mongodb.service#关闭开启启动systemctl disable mongodb.service
4.2.3. 标准的关闭方法
mongouse admindb.shutdownServer()
进程id关闭法
ps -ef | grep mongokill -2 pid
5. 数据库
5.1. 选择和创建数据库
#use 数据库名称use articledb #如果没有此数据库则自动创建#如果新的数据库没有插入数据 showdbs是无法查看到新创建的数据库 因为此时数据库存储在内存当中 并未持久化
5.2. 查询当前有权限查看的数据
show dbs#或者show databases
5.3. 查看当前正在使用的数据库名称
db
5.4. 数据库命名规范
数据库名可以是满足以下条件的任意UTF-8字符串。
- 不能是空字符串( “”)。
- 不得含有 ‘ ‘(空格)、.、$、/、\和\0 (空字符)。
- 应全部小写。
- 最多 64字节。
有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。
5.5. 默认三个数据库作用
- admin : 从权限的角度来看,这是”root”数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
- local: 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
- config : 当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。
5.6. 数据库删除
db.dropDatabase() #删除当前使用的库
6. 集合
集合,类似于关系型数据中的表
6.1. 显式创建
#db.createCollection(集合名)db.createCollection("my") #成功返回1
6.2. 隐式创建
当向一个集合中插入文档时 如果集合不存在 则自动创建集合
通常我们使用隐式创建文档即可
6.3. 查询集合
show collections #查询当前库的所有集合
6.4. 集合删除
#db.集合名.drop()db.my.drop() #成功则返回true
7. 文档CRUD
文档的数据结构和JSON基本一样
索引存储在集合中数据都是 BSON 格式
7.1. 插入
通过insert() 或者 save() 方法向集合中插入文档
7.1.1. 单问插入
#单文档插入db.collection.insert(<document or array of documents>,{writeConcern: <document>,ordered: <boolean>})

#db.集合名.inset({""})#单文档插入db.comment.insert({"articleid":"100000","content":"今天天气真好,阳光明媚","userid":"1001","nickname":"Rose","createdatetime":new Date(),"likenum":NumberInt(10),"state":null}) #返回1则插入成功
7.1.2. 批量插入
#批量插入db.collection.insertMany([ <document 1> , <document 2>, ... ],{writeConcern: <document>,ordered: <boolean>})#db.集合名.insertMany([{""},{""}])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"}]);
7.1.3. try catch
我们通过批量插入时由于数据较多可能会出现失败 我们可以通过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.2. 查询
查询当前数据库中的文档
7.2.1. 查询所有文档
#db.collection.find(<query>, [projection])db.comment.find()

7.2.2. 条件查询
查询文档中包含此文档内容的所有文档
#db.collection.find({键:"内容"})db.comment.find({articleid:"100001"})
7.2.3. 只返回第一个结果
#db.collection.findOne({键:"内容"})db.comment.findOne({articleid:"100001"})
7.2.4. 投影查询
查询出来的文档 只显示需要的键值对 相对应sql中的查询结果只显示指定列
find传参时给出指定的键字段 1为查询此键 0为过滤此键 默认自动包含查询_id键
#db.collection.find({键:"内容"},{键:1,键:0})db.comment.find({articleid:"100001"},{content:1}) #查询包含100001文档中的 content键内容db.comment.find({articleid:"100001"},{content:1,_id:0}) #默认查询包含_id 可以通过0过滤
7.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})

7.3.1. 覆盖修改
修改指定文档 并覆盖更新整个文档 即修改为当前语句中的字段 原先字段不保留
db.comment.update({_id:"1"},{likenum:NumberInt(1001)}) #可以理解为删除原文档并插入此文档
7.3.2. 局部修改
我们可以通过修改器$set来修改指定键的内容 并保留原文档其他内容
#默认只修改符合条件的第一条数据db.comment.update({_id:"2"},{$set:{likenum:NumberInt(778)}})#修改所有符合条件的数据需要加上参数{multi:true}db.comment.update({userid:"1003"},{$set:{nickname:"张三"}},{multi:true})
7.3.3. 列值增长修改
通过$inc运算符实现 可以对该键的值进行递增 递减
db.comment.update({_id:"2"},{$inc:{likenum:NumberInt(1)}})
7.4. 删除
7.4.1. 条件删除
删除符合条件的文档
#db.集合.remove({条件})db.comment.remove({_id:"1"}) #返回值为删除多少条数据
7.4.2. 删除全部
db.comment.remove({})
8. 文档的分页查询
8.1. 统计查询
使用count()方法
db.collection.count(query, options)

8.1.1. 统计当前集合所有文档数
db.comment.count() #返回值是文档数
8.1.2. 按条件查询
db.comment.count({userid:"1003"})
8.2. 分页列表查询
通过limit()方法 读取指定数量的数据 使用skip()方法跳过指定数量的数据
#db.collection.find().limit(number).skip(number)db.comment.find().limit(3) #查询前3条的数据db.comment.find().skip(3) #跳过前3条的数据db.comment.find().skip(0).limit(2) #第一页db.comment.find().skip(2).limit(2) #第二页
8.3. 排序查询
sort()方法对数据进行排序 使用1和-1来指定升序和降序 sort({键:1}) 默认以id进行升序
db.comment.find().sort({userid:-1,likenum:1}) #对userid的内容进行降序 再对likenum的内容进行升序
8.4. 分页和排序查询的顺序
skip(), limilt(), sort()三个放在一起执行的时候,执行的顺序是先 sort(), 然后是 skip(),最后是显示的 limit(),和命令编写顺序无关。
9. 高级查询
9.1. 正则条件查询
#db.集合.find({键:/正则表达式/})db.comment.find({content:/^专家/})
9.2. 比较查询
db.集合名称.find({ "field" : { $gt: value }}) # 大于: field > valuedb.集合名称.find({ "field" : { $lt: value }}) # 小于: field < valuedb.集合名称.find({ "field" : { $gte: value }}) # 大于等于: field >= valuedb.集合名称.find({ "field" : { $lte: value }}) # 小于等于: field <= valuedb.集合名称.find({ "field" : { $ne: value }}) # 不等于: field != value#例子db.comment.find({likenum:{$gt:NumberInt(700)}})
9.3. 包含查询
使用$in 查询包含指定内容的文档
db.comment.find({userid:{$in:["1003","1004"]})
9.4. 不包含查询
使用$nin 查询不包含指定内容的文档
db.comment.find({userid:{$nin:["1003","1004"]})
9.5. 条件连接查询
前面我们通过$set查询单条件的文档 如果为多条件则需要使用 $and进行查询
db.comment.find({$and:[{likenum:{$gte:NumberInt(700)}},{likenum:{$lt:NumberInt(2000)}}]}) # 查询likenum 大于等于700 并且小于2000的文档
9.5.1. 或者连接
使用$or进行查询
db.comment.find({$or:[{likenum:{$gte:NumberInt(700)}},{likenum:{$lt:NumberInt(2000)}}]}) # 查询likenum 大于等于700 并且小于2000的文档
10. 索引
MongoDB没有下索引下 是进行全集合扫描 如果数据量较大时查询效率非常低
MongoDB索引使用B树数据结构(B-Tree,MySQL是B+Tree)
10.1. 单字段索引
MongoDB支持在文档的单个字段上创建用户定义的升序/降序索引 称为单字段索引
对于单个字段索引和排序操作,索引键的排序顺序(即升序或降序)并不重要,因为MongoDB可以在任何方向上遍历索引。

10.2. 复合索引
复合索引中列出的字段顺序具有重要意义。例如,如果复合索引由 { userid: 1, score: -1 } 组成,则索引首先按userid正序排序,然后
在每个userid的值内,再在按score倒序排序。

10.3. 其他索引
地理空间索引(Geospatial Index)
为了支持对地理空间坐标数据的有效查询,MongoDB提供了两种特殊的索引:返回结果时使用平面几何的二维索引和返回结果时使用球面
几何的二维球面索引。文本索引(Text Indexes)
MongoDB提供了一种文本索引类型,支持在集合中搜索字符串内容。这些文本索引不存储特定于语言的停止词(例如“the”、“a”、“or”),
而将集合中的词作为词干,只存储根词。哈希索引(Hashed Indexes)
为了支持基于散列的分片,MongoDB提供了散列索引类型,它对字段值的散列进行索引。这些索引在其范围内的值分布更加随机,但只支
持相等匹配,不支持基于范围的查询。
10.4. 索引管理
10.4.1. 查看索引
返回一个集合中的所有索引的数组 _id主键自带索引为升序
db.comment.getIndexes()
10.4.2. 创建索引

#db.collection.createIndex(keys, options)db.comment.createIndex({userid:1}) # 给userid创建索引 为升序 索引名默认为键名后加上_(1或者-1)db.comment.createIndex({userid:1,nickname:-1}) #复合索引
10.4.3. 删除索引
如果要删除文本索引只能通过索引名删除
_id索引无法删除
#db.collection.dropIndex(索引名)db.comment.dropIndex() #删除所有索引db.comment.dropIndex("userid_1_nickname_-1") #删除指定索引db.comment.dropIndex({userid:1}) #根据创建索引条件删除索引
10.5. 索引的使用
分析查询性能(Analyze Query Performance)通常使用执行计划(解释计划、Explain Plan)来查看查询的情况,如查询耗费的时间、是否基于索引查询等。
那么,通常,我们想知道,建立的索引是否有效,效果如何,都需要通过执行计划查看。
#db.collection.find(query,options).explain(options)db.comment.find({uderid:"1003"}).explain()
关键点看: "stage" : "COLLSCAN", 表示全集合扫描
`"stage"` : `"IXSCAN"` ,基于索引的扫描
10.6. 涵盖查询
Covered Queries
当查询条件和查询的投影仅包含索引字段时,MongoDB直接从索引返回结果,而不扫描任何文档或将文档带入内存。 这些覆盖的查询可以非常有效。 其实就是查询符合查询条件 索引字段的值

11. Java连接MongoDB
- mongodb-driver 是mongo官方推出的java连接mongoDB的驱动包,相当于JDBC驱动。我们通过一个入门的案例来了解mongodb-driver
的基本使用。
官方驱动说明和下载: http://mongodb.github.io/mongo-java-driver/
官方驱动示例文档:http://mongodb.github.io/mongo-java-driver/3.8/driver/getting-started/quick-start/ - SpringDataMongoDB SpringData家族成员之一,用于操作MongoDB的持久层框架,封装了底层的mongodb-driver。
官网主页: https://projects.spring.io/spring-data-mongodb/
12. 评论案例
12.1. 模块搭建
- 继承spring-boot项目 导入springdataMongoDB坐标
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.6.RELEASE</version><relativePath/> <!-- lookup parent from repository --></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>
- 创建application.yml
spring:data:mongodb:host: 192.168.130.212 # 主机地址database: articledb # 数据库port: 27017 # 默认端口是27017#也可以使用uri连接#uri: mongodb://192.168.40.134:27017/articledb

12.2. 实体类
package com.itcatst.article.po;import org.springframework.data.annotation.Id;import org.springframework.data.mongodb.core.index.CompoundIndex;import org.springframework.data.mongodb.core.index.Indexed;import org.springframework.data.mongodb.core.mapping.Document;import org.springframework.data.mongodb.core.mapping.Field;import java.io.Serializable;import java.time.LocalDateTime;import java.util.Date;/*** 文章评论实体类*///把一个java类声明为mongodb的文档,可以通过collection参数指定这个类对应的文档。//@Document(collection="mongodb 对应 collection 名")// 若未加 @Document ,该 bean save 到 mongo 的 comment collection// 若添加 @Document ,则 save 到 comment collection@Document(collection = "comment")//可以省略,如果省略,则默认使用类名小写映射集合 会映射到与类名一致的集合当中//复合索引// @CompoundIndex( def = "{'userid': 1, 'nickname': -1}")public class Comment implements Serializable {//主键标识,该属性的值会自动对应mongodb的主键字段"_id",如果该属性名就叫“id”,则该注解可以省略,否则必须写@Idprivate String id;//主键//该属性对应mongodb的字段的名字,如果一致,则无需该注解@Field("content")private String content;//吐槽内容private Date publishtime;//发布日期//添加了一个单字段的索引@Indexedprivate String userid;//发布人IDprivate String nickname;//昵称private LocalDateTime createdatetime;//评论的日期时间private Integer likenum;//点赞数private Integer replynum;//回复数private String state;//状态private String parentid;//上级IDprivate String articleid;//getter and setter.....public String getId() {return id;}public void setId(String id) {this.id = id;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public Date getPublishtime() {return publishtime;}public void setPublishtime(Date publishtime) {this.publishtime = publishtime;}public String getUserid() {return userid;}public void setUserid(String userid) {this.userid = userid;}public String getNickname() {return nickname;}public void setNickname(String nickname) {this.nickname = nickname;}public LocalDateTime getCreatedatetime() {return createdatetime;}public void setCreatedatetime(LocalDateTime createdatetime) {this.createdatetime = createdatetime;}public Integer getLikenum() {return likenum;}public void setLikenum(Integer likenum) {this.likenum = likenum;}public Integer getReplynum() {return replynum;}public void setReplynum(Integer replynum) {this.replynum = replynum;}public String getState() {return state;}public void setState(String state) {this.state = state;}public String getParentid() {return parentid;}public void setParentid(String parentid) {this.parentid = parentid;}public String getArticleid() {return articleid;}public void setArticleid(String articleid) {this.articleid = articleid;}@Overridepublic 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 + '\'' +'}';}}
- @Document(collection = “comment”) 声明为mongodb文档 并且映射到comment集合中 如果当前实体类名与集合一致则collection可以忽略(并且只能是小写名称的实体类)
- @CompoundIndex( def = “{‘userid’: 1, ‘nickname’: -1}”) 复合索引 def属性与设置复活索引条件一致 为键和排序方式
- @Id 声明此属性为主键 如果属性名为id则该注解可以省略不加
- @Field(“content”) 当成员属性名称与集合中的键字段不一致时 可以使用该注解映射为集合中指定的键字段 此时映射为content键字段
- @Indexed 添加为单字段索引
12.3. 增删改查方法
- 创建dao层接口 并继承MongoRepository 泛型为 实体类 和 id的类型 ```java package com.itcatst.article.dao;
import com.itcatst.article.po.Comment; import org.springframework.data.mongodb.repository.MongoRepository;
public interface CommentRepository extends MongoRepository
2.创建Service层 注入dao层 并调用动态代理里的方法```javapackage com.itcatst.article.service;import com.itcatst.article.dao.CommentRepository;import com.itcatst.article.po.Comment;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Servicepublic class CommentService {@Autowiredprivate CommentRepository commentRepository;/*** 保存一个评论** @param comment*/public void saveComment(Comment comment) {//如果需要自定义主键,可以在这里指定主键;如果不指定主键,MongoDB会自动生成主键//设置一些默认初始值。。。//调用daocommentRepository.save(comment);}/*** 更新评论** @param comment*/public void updateComment(Comment comment) {//调用daocommentRepository.save(comment);}/*** 根据id删除评论** @param id*/public void deleteCommentById(String id) {//调用daocommentRepository.deleteById(id);}/*** 查询所有评论** @return*/public List<Comment> findCommentList() {//调用daoreturn commentRepository.findAll();}/*** 根据id查询评论** @param id* @return*/public Comment findCommentById(String id) {//调用daoreturn commentRepository.findById(id).get();}}
12.4. 创建测试方法
- 新建junit测试方法 类路径要与主程序一致 ```java package com.itcatst.article.service;
import com.itcatst.article.po.Comment; import com.itcatst.article.service.CommentService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner;
import java.time.LocalDateTime; import java.util.List;
@RunWith(SpringRunner.class) @SpringBootTest public class CommentServiceTest {
@Autowiredprivate CommentService commentService;@Testpublic void testFindCommentList() {List<Comment> commentList = commentService.findCommentList();System.out.println(commentList);}@Testpublic void testFndCommentById() {Comment commentById = commentService.findCommentById("1");System.out.println(commentById);}/*** 保存一个评论*/@Testpublic void testSaveComment() {Comment comment = new Comment();//此处没有指定id 但MongoDB默认自动生成 我们推荐自动生成 不推荐自己书写idcomment.setArticleid("100000");comment.setContent("测试添加的数据");comment.setCreatedatetime(LocalDateTime.now());comment.setUserid("1003");comment.setNickname("凯撒大帝");comment.setState("1");comment.setLikenum(0);comment.setReplynum(0);commentService.saveComment(comment);}
}
<a name="14ae450a"></a>## 12.5. 分页查询测试1.在dao层接口创建新的方法 必须要指定**对应的属性名为方法名**否则会报错<br />格式: Page<实体类> findBy根据哪个属性来查询(传递属性类型 属性名,Pageable pageable);```java////根据父id,查询子评论的分页列表Page<Comment> findByParentid(String parentid, Pageable pageable);
- 在service层调用dao层的方法
/*** 分页查询** @param parentid 根据哪个属性来查询* @param page 页码* @param size 每页个数* @return*/public Page<Comment> findCommentListByParentid(String parentid, int page, int size) {return commentRepository.findByParentid(parentid, PageRequest.of(page-1, size));}
- 测试用例
```java
@Test
public void testFindCommentListByParentid(){
Page
page = commentService.findCommentListByParentid(“3”, 1, 2); System.out.println(page.getTotalElements());//返回总条数 List content = page.getContent(); //返回结果的集合 System.out.println(content);
}
<a name="4bf73c0d"></a>## 12.6. 评论点赞最简单的方法就是根据id查询当前对应的文档 并将点赞数+1 但执行效率不高 因为只需要修改点赞数 没有必要把所有的字段都查询出来我们可以使用 MongoTemplate 类实现对某列进行操作1.在service层注入 MongoTemplate 并编修改方法```java@Autowiredprivate MongoTemplate mongoTemplate;/*** 点赞数+1** @param id*/public void updateCommentLikenum(String id) {//查询条件//Query query = Query.query(Criteria.where("_id").is(id)).addCriteria(Criteria.where("nickname").is("凯撒大帝")); //使用Criteria.when(键字段).is(条件) 判断条件 addCriteria可以无限扩展条件Query query = Query.query(Criteria.where("_id").is(id));//更新条件Update update = new Update();update.inc("likenum",1); //将likenum数+1 如单传键字段则自动加1// update.set()mongoTemplate.updateFirst(query, update, Comment.class);}
13. 副本集
MongoDB中的副本集(Replica Set)是一组维护相同数据集的mongod服务。 副本集可提供冗余和高
可用性,是所有生产部署的基础。
主从复制和副本集区别
主从集群和副本集最大的区别就是副本集没有固定的“主节点”;整个集群会选出一个“主节点”,当其挂
掉后,又在剩下的从节点中选中其他节点为“主节点”,副本集总有一个活跃点(主、primary)和一个或多
个备份节点(从、secondary)。
副本集有两种类型三种角色:
两种类型:
- 主节点( Primary)类型:数据操作的主要连接点,可读写。
- 次要(辅助、从)节点( Secondaries)类型:数据冗余备份节点,可以读或选举。
三种角色:
- 主要成员(Primary):主要接收所有写操作。就是主节点。
- 副本成员(Replicate):从主节点通过复制操作以维护相同的数据集,即备份数据,不可写操作,但可
以读操作(但需要配置)。是默认的一种从节点类型。 - 仲裁者( Arbiter):不保留任何数据的副本,只具有投票选举作用。当然也可以将仲裁服务器维护为副
本集的一部分,即副本成员同时也可以是仲裁者。也是一种从节点类型。
13.1. 副本集创建
13.1.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或mongos应向其发送所有诊断日志记录信息的日志文件的路径path: "/mongodb/replica_sets/myrs_27017/log/mongod.log"#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。logAppend: truestorage:#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。dbPath: "/mongodb/replica_sets/myrs_27017/data/db"journal:#启用或禁用持久性日志以确保数据文件保持有效和可恢复。enabled: trueprocessManagement:#启用在后台运行mongos或mongod进程的守护进程模式。fork: true#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PIDpidFilePath: "/mongodb/replica_sets/myrs_27017/log/mongod.pid"net:#服务实例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip#bindIpAll: true#服务实例绑定的IPbindIp: localhost,192.168.130.212#bindIp#绑定的端口port: 27017replication:#副本集的名称 同一个副本集中集群必须一致replSetName: myrs
启动主节点
/usr/local/mongodb/bin/mongod -f /mongodb/replica_sets/myrs_27017/mongod.conf
13.1.2. 从节点
#副本节点mkdir -p /mongodb/replica_sets/myrs_27018/log \ &mkdir -p /mongodb/replica_sets/myrs_27018/data/dbvim /mongodb/replica_sets/myrs_27018/mongod.conf
systemLog:#MongoDB发送所有日志输出的目标指定为文件destination: file#mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径path: "/mongodb/replica_sets/myrs_27018/log/mongod.log"#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。logAppend: truestorage:#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。dbPath: "/mongodb/replica_sets/myrs_27018/data/db"journal:#启用或禁用持久性日志以确保数据文件保持有效和可恢复。enabled: trueprocessManagement:#启用在后台运行mongos或mongod进程的守护进程模式。fork: true#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PIDpidFilePath: "/mongodb/replica_sets/myrs_27018/log/mongod.pid"net:#服务实例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip#bindIpAll: true#服务实例绑定的IPbindIp: localhost,192.168.130.212#bindIp#绑定的端口port: 27018replication:#副本集的名称replSetName: myrs
启动节点
/usr/local/mongodb/bin/mongod -f /mongodb/replica_sets/myrs_27018/mongod.conf
13.1.3. 仲裁节点
#仲裁节点mkdir -p /mongodb/replica_sets/myrs_27019/log \ &mkdir -p /mongodb/replica_sets/myrs_27019/data/dbvim /mongodb/replica_sets/myrs_27019/mongod.conf
systemLog:#MongoDB发送所有日志输出的目标指定为文件destination: file#mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径path: "/mongodb/replica_sets/myrs_27019/log/mongod.log"#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。logAppend: truestorage:#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。dbPath: "/mongodb/replica_sets/myrs_27019/data/db"journal:#启用或禁用持久性日志以确保数据文件保持有效和可恢复。enabled: trueprocessManagement:#启用在后台运行mongos或mongod进程的守护进程模式。fork: true#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PIDpidFilePath: "/mongodb/replica_sets/myrs_27019/log/mongod.pid"net:#服务实例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip#bindIpAll: true#服务实例绑定的IPbindIp: localhost,192.168.130.212#bindIp#绑定的端口port: 27019replication:#副本集的名称replSetName: myrs
启动节点
/usr/local/mongodb/bin/mongod -f /mongodb/replica_sets/myrs_27019/mongod.conf
13.2. 初始化配置副本集和主节点
#使用mongo客户端任意节点都可以 但尽量连上主节点/usr/local/mongodb/bin/mongo --host=192.168.130.212 --port=27017#初始化命令rs.initiate() #使用默认的配置来初始化副本集 “ok”的值为1,说明创建成功 并且此节点变为主节点rs.conf() #查看默认节点配置rs.status() # 查询副本集状态
13.3. 添加副本从节点
#rs.add(host:post)rs.add(192.168.130.212:27018)rs.status() #查看副本集状态
13.4. 添加仲裁节点
#rs.addArb(host:post)rs.addarB(192.168.130.212:27019)rs.status() #查看副本集状态
13.5. 副本集的数据读写操作
只有主节点写入数据和读取数据
默认情况从节点无法读取数据 也无法写入数据 可以进行设置增加读的权限
#连接从节点的客户端rs.slaveOk() #该命令是 db.getMongo().setSlaveOk() 的简化命令#或者rs.slaveOk(true)#此时实现了读写分离 让主插入数据 让从来读取数据
- 仲裁者节点,不存放任何业务数据的,可以登录查看 只存放副本集配置等数据。
13.6. 主节点的选举原则
MongoDB在副本集中,会自动进行主节点的选举,主节点选举的触发条件:
- 主节点故障
- 主节点网络不可达(默认心跳信息为10秒)
- 人工干预(rs.stepDown(600))
选举规则是根据票数来决定谁获胜:
- 票数最高,且获得了 “大多数”成员的投票支持的节点获胜。
“大多数”的定义为:假设复制集内投票成员数量为N,则大多数为 N/2 + 1。例如:3个投票成员,
则大多数的值是2。当复制集内存活成员数量不足大多数时,整个复制集将无法选举出Primary,
复制集将无法提供写服务,处于只读状态。 - 若票数相同,且都获得了 “大多数”成员的投票支持的,数据新的节点获胜。
数据的新旧是通过操作日志oplog来对比的。
在获得票数的时候,优先级(priority)参数影响重大。
可以通过设置优先级(priority)来设置额外票数。优先级即权重,取值为0-1000,相当于可额外增加
0-1000的票数,优先级的值越大,就越可能获得多数成员的投票(votes)数。指定较高的值可使成员
更有资格成为主要成员,更低的值可使成员更不符合条件。
默认情况下,优先级的值是1
13.7. 提升优先级
#先导出配置到变量中cfg=rs.conf()#修改指定优先级 ID号默认从0开始cfg.members[1].priority=2#重新加载配置rs.reconfig(cfg)
13.8. Compass连接副本集
#将host修改为当前主节点的ipvar config = rs.config();config.members[0].host="192.168.130.212:27017";rs.reconfig(config)

选择Primary 为主节点 Secondary为副本集
13.9. SpringDataMongoDB连接副本集
mongodb://host1,host2,host3/articledb?connect=replicaSet&slaveOk=true&replicaSet=副本集名字
- slaveOk=true :开启副本节点读的功能,可实现读写分离。
- connect=replicaSet :自动到副本集中选择读写的主机。如果slaveOK是打开的,则实现了读写分
离
主机必须是副本集中所有的主机,包括主节点、副本节点、仲裁节点。
spring:data:mongodb:# host: 192.168.130.212 # 主机地址# database: articledb # 数据库# port: 27017 # 默认端口是27017#也可以使用uri连接#uri: mongodb://192.168.40.134:27017/articledb# 副本集的连接字符串uri: mongodb://192.168.42.212:27017,192.168.42.212:27018,192.168.42.212:27019/articledb?connect=replicaSet&slaveOk=true&replicaSet=myrs
14. 分片集群
分片(sharding)是一种跨多台机器分布数据的方法, MongoDB使用分片来支持具有非常大的数据集
和高吞吐量操作的部署。
MongoDB分片群集包含以下组件:
- 分片(存储):每个分片包含分片数据的子集。 每个分片都可以部署为副本集。
- mongos (路由):mongos充当查询路由器,在客户端应用程序和分片集群之间提供接口。
- config servers (“调度”的配置):配置服务器存储群集的元数据和配置设置。 从MongoDB 3.4开
始,必须将配置服务器部署为副本集(CSRS)。

14.1. 搭建分片集群架构

14.1.1. 第一个副本集
#-----------myshardrs01mkdir -p /mongodb/sharded_cluster/myshardrs01_27018/log \ &mkdir -p /mongodb/sharded_cluster/myshardrs01_27018/data/db \ &mkdir -p /mongodb/sharded_cluster/myshardrs01_27118/log \ &mkdir -p /mongodb/sharded_cluster/myshardrs01_27118/data/db \ &mkdir -p /mongodb/sharded_cluster/myshardrs01_27218/log \ &mkdir -p /mongodb/sharded_cluster/myshardrs01_27218/data/db#myshardrs01_27018vim /mongodb/sharded_cluster/myshardrs01_27018/mongod.conf
systemLog:#MongoDB发送所有日志输出的目标指定为文件destination: file#mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径path: "/mongodb/sharded_cluster/myshardrs01_27018/log/mongod.log"#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。logAppend: truestorage:#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。dbPath: "/mongodb/sharded_cluster/myshardrs01_27018/data/db"journal:#启用或禁用持久性日志以确保数据文件保持有效和可恢复。enabled: trueprocessManagement:#启用在后台运行mongos或mongod进程的守护进程模式。fork: true#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PIDpidFilePath: "/mongodb/sharded_cluster/myshardrs01_27018/log/mongod.pid"net:#服务实例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip#bindIpAll: true#服务实例绑定的IPbindIp: localhost,192.168.130.212#bindIp#绑定的端口port: 27018replication:#副本集的名称replSetName: myshardrs01sharding:#分片角色clusterRole: shardsvr
clusterRole 设置分片角色:
- shardsvr 分片角色
- configsvr 配置角色
#myshardrs01_27118vim /mongodb/sharded_cluster/myshardrs01_27118/mongod.conf
systemLog:#MongoDB发送所有日志输出的目标指定为文件destination: file#mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径path: "/mongodb/sharded_cluster/myshardrs01_27118/log/mongod.log"#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。logAppend: truestorage:#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。dbPath: "/mongodb/sharded_cluster/myshardrs01_27118/data/db"journal:#启用或禁用持久性日志以确保数据文件保持有效和可恢复。enabled: trueprocessManagement:#启用在后台运行mongos或mongod进程的守护进程模式。fork: true#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PIDpidFilePath: "/mongodb/sharded_cluster/myshardrs01_27118/log/mongod.pid"net:#服务实例绑定所有IP#bindIpAll: true#服务实例绑定的IPbindIp: localhost,192.168.130.212#绑定的端口port: 27118replication:replSetName: myshardrs01sharding:clusterRole: shardsvr
#myshardrs01_27218vim /mongodb/sharded_cluster/myshardrs01_27218/mongod.conf
systemLog:#MongoDB发送所有日志输出的目标指定为文件destination: file#mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径path: "/mongodb/sharded_cluster/myshardrs01_27218/log/mongod.log"#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。logAppend: truestorage:#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。dbPath: "/mongodb/sharded_cluster/myshardrs01_27218/data/db"journal:#启用或禁用持久性日志以确保数据文件保持有效和可恢复。enabled: trueprocessManagement:#启用在后台运行mongos或mongod进程的守护进程模式。fork: true#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PIDpidFilePath: "/mongodb/sharded_cluster/myshardrs01_27218/log/mongod.pid"net:#服务实例绑定的IPbindIp: localhost,192.168.42.130.212#绑定的端口port: 27218replication:replSetName: myshardrs01sharding:clusterRole: shardsvr
启动第一套副本集:一主一副本一仲裁
/usr/local/mongodb/bin/mongod -f /mongodb/sharded_cluster/myshardrs01_27018/mongod.conf/usr/local/mongodb/bin/mongod -f /mongodb/sharded_cluster/myshardrs01_27118/mongod.conf/usr/local/mongodb/bin/mongod -f /mongodb/sharded_cluster/myshardrs01_27218/mongod.conf
- 初始化副本集和创建主节点
/usr/local/mongodb/bin/mongo --host 192.168.130.212 --port 27018 #尽量连接主节点
rs.initiate() #初始化主节点rs.status() #状态查看rs.add("192.168.130.212:27118") #添加副节点rs.addArb("192.168.130.212:27218") #添加仲裁节点rs.conf()
14.1.2. 第二套副本集
#-----------myshardrs02mkdir -p /mongodb/sharded_cluster/myshardrs02_27318/log \ &mkdir -p /mongodb/sharded_cluster/myshardrs02_27318/data/db \ &mkdir -p /mongodb/sharded_cluster/myshardrs02_27418/log \ &mkdir -p /mongodb/sharded_cluster/myshardrs02_27418/data/db \ &mkdir -p /mongodb/sharded_cluster/myshardrs02_27518/log \ &mkdir -p /mongodb/sharded_cluster/myshardrs02_27518/data/db
#myshardrs02_27318vim /mongodb/sharded_cluster/myshardrs02_27318/mongod.conf
systemLog:#MongoDB发送所有日志输出的目标指定为文件destination: file#mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径path: "/mongodb/sharded_cluster/myshardrs02_27318/log/mongod.log"#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。logAppend: truestorage:#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。dbPath: "/mongodb/sharded_cluster/myshardrs02_27318/data/db"journal:#启用或禁用持久性日志以确保数据文件保持有效和可恢复。enabled: trueprocessManagement:#启用在后台运行mongos或mongod进程的守护进程模式。fork: true#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PIDpidFilePath: "/mongodb/sharded_cluster/myshardrs02_27318/log/mongod.pid"net:#服务实例绑定的IPbindIp: localhost,192.168.130.212#绑定的端口port: 27318replication:replSetName: myshardrs02sharding:clusterRole: shardsvr
#myshardrs02_27418vim /mongodb/sharded_cluster/myshardrs02_27418/mongod.conf
systemLog:#MongoDB发送所有日志输出的目标指定为文件destination: file#mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径path: "/mongodb/sharded_cluster/myshardrs02_27418/log/mongod.log"#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。logAppend: truestorage:#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。dbPath: "/mongodb/sharded_cluster/myshardrs02_27418/data/db"journal:#启用或禁用持久性日志以确保数据文件保持有效和可恢复。enabled: trueprocessManagement:#启用在后台运行mongos或mongod进程的守护进程模式。fork: true#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PIDpidFilePath: "/mongodb/sharded_cluster/myshardrs02_27418/log/mongod.pid"net:#服务实例绑定所有IP#bindIpAll: true#服务实例绑定的IPbindIp: localhost,192.168.130.212#绑定的端口port: 27418replication:replSetName: myshardrs02sharding:clusterRole: shardsvr
#myshardrs02_27518vim /mongodb/sharded_cluster/myshardrs02_27518/mongod.conf
systemLog:#MongoDB发送所有日志输出的目标指定为文件destination: file#mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径path: "/mongodb/sharded_cluster/myshardrs02_27518/log/mongod.log"#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。logAppend: truestorage:#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。dbPath: "/mongodb/sharded_cluster/myshardrs02_27518/data/db"journal:#启用或禁用持久性日志以确保数据文件保持有效和可恢复。enabled: trueprocessManagement:#启用在后台运行mongos或mongod进程的守护进程模式。fork: true#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PIDpidFilePath: "/mongodb/sharded_cluster/myshardrs02_27518/log/mongod.pid"net:#服务实例绑定所有IP#bindIpAll: true#服务实例绑定的IPbindIp: localhost,192.168.130.212#绑定的端口port: 27518replication:replSetName: myshardrs02sharding:clusterRole: shardsvr
启动第二套副本集:一主一副本一仲裁
/usr/local/mongodb/bin/mongod -f /mongodb/sharded_cluster/myshardrs02_27318/mongod.conf/usr/local/mongodb/bin/mongod -f /mongodb/sharded_cluster/myshardrs02_27418/mongod.conf/usr/local/mongodb/bin/mongod -f /mongodb/sharded_cluster/myshardrs02_27518/mongod.confps -ef |grep mongod #一共6个服务
- 初始化副本集和创建主节点
/usr/local/mongodb/bin/mongo --host 192.168.130.212 --port 27318 #尽量连接主节点
rs.initiate() #初始化主节点rs.status() #状态查看rs.add("192.168.130.212:27418") #添加副节点rs.addArb("192.168.130.212:27518") #添加仲裁节点rs.conf()
14.1.3. 配置节点副本集
#-----------configrs#建立数据节点data和日志目录mkdir -p /mongodb/sharded_cluster/myconfigrs_27019/log \ &mkdir -p /mongodb/sharded_cluster/myconfigrs_27019/data/db \ &mkdir -p /mongodb/sharded_cluster/myconfigrs_27119/log \ &mkdir -p /mongodb/sharded_cluster/myconfigrs_27119/data/db \ &mkdir -p /mongodb/sharded_cluster/myconfigrs_27219/log \ &mkdir -p /mongodb/sharded_cluster/myconfigrs_27219/data/db
#myconfigrs_27019vim /mongodb/sharded_cluster/myconfigrs_27019/mongod.conf
#MongoDB发送所有日志输出的目标指定为文件destination: file#mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径path: "/mongodb/sharded_cluster/myconfigrs_27019/log/mongod.log"#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。logAppend: truestorage:#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。dbPath: "/mongodb/sharded_cluster/myconfigrs_27019/data/db"journal:#启用或禁用持久性日志以确保数据文件保持有效和可恢复。enabled: trueprocessManagement:#启用在后台运行mongos或mongod进程的守护进程模式。fork: true#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PIDpidFilePath: "/mongodb/sharded_cluster/myconfigrs_27019/log/mongod.pid"net:#服务实例绑定所有IP#bindIpAll: true#服务实例绑定的IPbindIp: localhost,192.168.130.212#绑定的端口port: 27019replication:replSetName: myconfigrssharding:clusterRole: configsvr
此时分片角色为配置角色
#myconfigrs_27119vim /mongodb/sharded_cluster/myconfigrs_27119/mongod.conf
systemLog:#MongoDB发送所有日志输出的目标指定为文件destination: file#mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径path: "/mongodb/sharded_cluster/myconfigrs_27119/log/mongod.log"#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。logAppend: truestorage:#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。dbPath: "/mongodb/sharded_cluster/myconfigrs_27119/data/db"journal:#启用或禁用持久性日志以确保数据文件保持有效和可恢复。enabled: trueprocessManagement:#启用在后台运行mongos或mongod进程的守护进程模式。fork: true#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PIDpidFilePath: "/mongodb/sharded_cluster/myconfigrs_27119/log/mongod.pid"net:#服务实例绑定所有IP#bindIpAll: true#服务实例绑定的IPbindIp: localhost,192.168.130.212#绑定的端口port: 27119replication:replSetName: myconfigrssharding:clusterRole: configsvr
#myconfigrs_27219vim /mongodb/sharded_cluster/myconfigrs_27219/mongod.conf
systemLog:#MongoDB发送所有日志输出的目标指定为文件destination: file#mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径path: "/mongodb/sharded_cluster/myconfigrs_27219/log/mongod.log"#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。logAppend: truestorage:#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。dbPath: "/mongodb/sharded_cluster/myconfigrs_27219/data/db"journal:#启用或禁用持久性日志以确保数据文件保持有效和可恢复。enabled: trueprocessManagement:#启用在后台运行mongos或mongod进程的守护进程模式。fork: true#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PIDpidFilePath: "/mongodb/sharded_cluster/myconfigrs_27219/log/mongod.pid"net:#服务实例绑定所有IP#bindIpAll: true#服务实例绑定的IPbindIp: localhost,192.168.42.130.212#绑定的端口port: 27219replication:replSetName: myconfigrssharding:clusterRole: configsvr
启动配置副本集:一主两副本
/usr/local/mongodb/bin/mongod -f /mongodb/sharded_cluster/myconfigrs_27019/mongod.conf/usr/local/mongodb/bin/mongod -f /mongodb/sharded_cluster/myconfigrs_27119/mongod.conf/usr/local/mongodb/bin/mongod -f /mongodb/sharded_cluster/myconfigrs_27219/mongod.confps -ef |grep mongod #一共9个服务
- 初始化副本集和创建主节点
/usr/local/mongodb/bin/mongo --host 192.168.130.212 --port 27019 #尽量连接主节点
rs.initiate() #初始化主节点rs.status() #状态查看rs.add("192.168.130.212:27119") #添加副节点rs.add("192.168.130.212:27119") #添加副节点rs.conf()
14.1.4. 路由节点创建和连接
#-----------mongos01mkdir -p /mongodb/sharded_cluster/mymongos_27017/log
#mymongos_27017节点vi /mongodb/sharded_cluster/mymongos_27017/mongos.conf
systemLog:#MongoDB发送所有日志输出的目标指定为文件destination: file#mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径path: "/mongodb/sharded_cluster/mymongos_27017/log/mongod.log"#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。logAppend: trueprocessManagement:#启用在后台运行mongos或mongod进程的守护进程模式。fork: true#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PIDpidFilePath: /mongodb/sharded_cluster/mymongos_27017/log/mongod.pid"net:#服务实例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip#bindIpAll: true#服务实例绑定的IPbindIp: localhost,192.168.130.212#bindIp#绑定的端口port: 27017sharding:#指定配置节点副本集configDB: myconfigrs/192.168.42.130.212:27019,192.168.42.130.212:27119,192.168.42.130.212:27219
configDB 地址为 配置副本集名称/配置节点1,配置节点2,配置节点3
#启动/usr/local/mongodb/bin/mongos -f /mongodb/sharded_cluster/mymongos_27017/mongos.conf
#连接 此时为mongos/usr/local/mongodb/bin/mongo --host 192.168.130.212 --port 27017
通过路由节点操作,现在只是连接了配置节点,还没有连接分片数据节点,因此写不进去数据,如果写数据会报错。
14.1.5. 路由节点添加分片
#sh.addShard(副本集名称/主节点,副本节点,仲裁节点) 三种角色节点必须都添加到路由中sh.addShard("myshardrs01/192.168.42.130.212:27018,192.168.42.130.212:27118,192.168.42.130.212:27218") #第一套sh.addShard("myshardrs02/192.168.42.130.212:27318,192.168.42.130.212:27418,192.168.42.130.212:27518") #第二套sh.status() #查询分片状态#sh.enableSharding("库名")sh.enableSharding("articledb") #开启分片功能
14.1.5.1. 集合分片和分片规则
sh.shardCollection(namespace, key, unique)

- 分片规则一:哈希策略
对指定字段的值 进行哈希计算出存放的位置 每个副本集对应则对应的范围 通过哈希计算出存放的副本集中sh.shardCollection("articledb.comment",{"nickname":"hashed"})
- 分片规则二:范围策略
对于 基于范围的分片 ,MongoDB按键的范围把数据分成不同部分.sh.shardCollection("articledb.author",{"age":1})
注意事项:
- 一个集合只能指定一个片键,否则报错。
- 一旦对一个集合分片,分片键和分片值就不可改变。 如:不能给集合选择不同的分片键、不能更新
分片键的值。 - 根据age索引进行分配数据
如果添加分片失败,需要先手动移除分片,检查添加分片的信息的正确性后,再次添加分片。
use admindb.runCommand( { removeShard: "myshardrs02" } )
db.printShardingStatus() #显示集群的详细信息sh.isBalancerRunning() #查询交换器是否工作sh.getBalancerState() #查看Balancer状态
14.1.5.2. 范围规则配置数据块大小
范围规则默认存放在数据块中 如果数据块(chunk)没有填满,默认的数据块尺寸(chunksize)是64M,填满后才会考虑向其他片的数据块填充数据
use configdb.settings.save({_id:"chunksize", value: 64}) #默认为64m
14.1.6. 追加路由节点
#-----------mongos02mkdir -p /mongodb/sharded_cluster/mymongos_27117/log
vi /mongodb/sharded_cluster/mymongos_27117/mongos.conf
systemLog:#MongoDB发送所有日志输出的目标指定为文件destination: file#mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径path: "/mongodb/sharded_cluster/mymongos_27117/log/mongod.log"#当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。logAppend: trueprocessManagement:#启用在后台运行mongos或mongod进程的守护进程模式。fork: true#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PIDpidFilePath: /mongodb/sharded_cluster/mymongos_27117/log/mongod.pid"net:#服务实例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip#bindIpAll: true#服务实例绑定的IPbindIp: localhost,192.168.0.2#bindIp#绑定的端口port: 27117sharding:configDB:myconfigrs/192.168.42.130.212:27019,192.168.42.130.212:27119,192.168.42.130.212:27219
#启动/usr/local/mongodb/bin/mongos -f /mongodb/sharded_cluster/mymongos_27117/mongos.conf
第二个路由无需配置,因为分片配置都保存到了配置服务器中了。
14.2. Compass连接分片集群
连接路由节点即可

14.3. SpringDataMongDB 连接分片集群
spring:data:mongodb:# host: 192.168.130.212 # 主机地址# database: articledb # 数据库# port: 27017 # 默认端口是27017#也可以使用uri连接#uri: mongodb://192.168.40.134:27017/articledb# 副本集的连接字符串uri: mongodb://192.168.40.134:27017,192.168.40.134:27117/articledb# uri: mongodb://192.168.42.212:27017,192.168.42.212:27018,192.168.42.212:27019/article# db?connect=replicaSet&slaveOk=true&replicaSet=myrs
如果有多个节点可以使用逗号隔开 SpringDataMongDB默认有负载均衡策略
15. 安全认证
默认情况下,MongoDB实例启动运行时是没有启用用户访问权限控制的
15.1. 角色
常用的内置角色:
- 数据库用户角色: read、readWrite;
- 所有数据库用户角色: readAnyDatabase、readWriteAnyDatabase、
- userAdminAnyDatabase、dbAdminAnyDatabase
- 数据库管理角色: dbAdmin、dbOwner、userAdmin;
- 集群管理角色: clusterAdmin、clusterManager、clusterMonitor、hostManager;
- 备份恢复角色: backup、restore;
- 超级用户角色: root
- 内部角色: system

db.runCommand({ rolesInfo: 1 }) #查询所有角色权限(仅用户自定义角色)db.runCommand({ rolesInfo: 1, showBuiltinRoles: true }) #查询所有角色权限(包含内置角色)db.runCommand({ rolesInfo: "<rolename>" }) #查询当前数据库中的某角色的权限db.runCommand({ rolesInfo: { role: "<rolename>", db: "<database>" } } #查询其它数据库中指定的角色权限
15.2. 单实例安全认证
- 添加用户和权限
use admindb.createUser({user:"myroot",pwd:"123456",roles:["root"]}) #创建myroot用户 密码123456 权限为root 如果不指定db名称则默认为当前所在库db.createUser({user:"myadmin",pwd:"123456",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 admindb.auth("myroot","123456")
如果开启了认证后,登录的客户端的用户必须使用admin库的角色,如拥有root角色的myadmin用
户,再通过myadmin用户去创建其他角色的用户
15.2.1. 服务端开启认证和客户端连接登陆
- 参数方式 启动服务端时加锁—auth参数
/usr/local/mongodb/bin/mongod -f /mongodb/single/mongod.conf --auth
- 配置方式 ```sh vim /mongodb/single/mongod.conf
配置文件追加
security: authorization: enabled #开启授权认证
```sh#此时连接可以进入客户端 但无法执行任何操作 只有认证成功后能使用use admin #此用户能查看什么库就切换到什么库中再认证 否则报错db.auth("myroot","123456")
连接客户端时直接认证 同样查看什么库就切换到什么库中再认证 否则报错
mongo --host 192.168.42.130.212 --port 27017 --authenticationDatabase admin -u myroot -p 123456
- -u :用户名
- -p :密码
- — authenticationDatabase :指定连接到哪个库。当登录是指定用户名密码时,必须指定对应的
数据库!
15.2.2. SpringDataMongoDB连接认证
spring:#数据源配置data:mongodb:# 主机地址# host: 192.168.42.130.212# 数据库# database: articledb# 默认端口是27017# port: 27017#帐号# username: bobo#密码# password: 123456#单机有认证的情况下,也使用字符串连接uri: mongodb://bobo:123456@192.168.130.212/articledb
15.3. 副本集环境
只需要在主节点上添加用户,副本集会自动同步
use admindb.createUser({user:"myroot",pwd:"123456",roles:["root"]})
生成副本集认证的key文件 在centos中
openssl rand -base64 90 -out ./mongo.keyfilechmod 400 ./mongo.keyfilell mongo.keyfile
所有副本集节点都必须要用同一份keyfile,一般是在一台机器上生成,然后拷贝到其他机器上,且必须
有读的权限,否则将来会报错
#此时应该通过网络传输此key文件给集群机器cp mongo.keyfile /mongodb/replica_sets/myrs_27017cp mongo.keyfile /mongodb/replica_sets/myrs_27018cp mongo.keyfile /mongodb/replica_sets/myrs_27019
修改各个节点的配置文件
vim /mongodb/replica_sets/myrs_27017/mongod.conf
security:#KeyFile鉴权文件keyFile: /mongodb/replica_sets/myrs_27017/mongo.keyfile#开启认证方式运行authorization: enabled
vim /mongodb/replica_sets/myrs_27018/mongod.conf
security:#KeyFile鉴权文件keyFile: /mongodb/replica_sets/myrs_27018/mongo.keyfile#开启认证方式运行authorization: enabled
vim /mongodb/replica_sets/myrs_27019/mongod.conf
security:#KeyFile鉴权文件keyFile: /mongodb/replica_sets/myrs_27019/mongo.keyfile#开启认证方式运行authorization: enabled
重新启动
/usr/local/mongodb/bin/mongod -f /mongodb/replica_sets/myrs_27017/mongod.conf/usr/local/mongodb/bin/mongod -f /mongodb/replica_sets/myrs_27018/mongod.conf/usr/local/mongodb/bin/mongod -f /mongodb/replica_sets/myrs_27019/mongod.conf
创建新的用户读和写
mongouse admindb.auth("myroot","123456") #先登陆认证管理员账号use articledbdb.createUser({user: "bobo", pwd: "123456", roles: ["readWrite"]})
13.9. SpringDataMongoDB连接副本集
spring:#数据源配置data:mongodb:#副本集有认证的情况下,字符串连接uri:mongodb://bobo:123456@192.168.42.130.212:27017,192.168.42.130.212:27018,192.168.42.130.212:27019/articledb?connect=replicaSet&slaveOk=true&replicaSet=myrs
15.4. 分片集群环境
关闭配置服务器副本集的服务,建议依次关闭副本节点、主节点再关闭路由服务器的服务
rs.stepDown() #告知副本集说本机要下线use admindb.shutdownServer()
- 生成key文件
openssl rand -base64 90 -out ./mongo.keyfilechmod 400 ./mongo.keyfile
拷贝key文件到各个服务上
修改配置文件
#这里是各个服务上的配置文件vim /mongodb/sharded_cluster/myshardrs01_27018/mongod.conf
security:#KeyFile鉴权文件 注意要修改路径keyFile: /mongodb/replica_sets/myrs_27019/mongo.keyfile#开启认证方式运行authorization: enabled
- 重新启动各个节点
先启动配置节点,再启动分片节点,最后启动路由节点。如果先启动分片节点,会卡住/usr/local/mongodb/bin/mongod -f /mongodb/sharded_cluster/myconfigrs_27019/mongod.conf
- 客户端mongo,通过localhost登录任意一个mongos路由
/usr/local/mongodb/bin/mongo --port 27017
- 创建账号
use admindb.createUser({user:"myroot",pwd:"123456",roles:["root"]}) #管理员账号db.createUser({user: "bobo", pwd: "123456", roles: [{ role: "readWrite",db: "articledb" }]}) #指定库读写账号
15.2.2. SpringDataMongoDB连接认证
spring:#数据源配置data:mongodb:# 分片集群有认证的情况下,字符串连接uri:mongodb://bobo:123456@192.168.42.130.212:27017,192.168.42.130.212:27117/articledb
16. 4.0新特性
16.1. 加载外部js文件
加载当前路径下的js文件 启动shell命令时的路径和js所在路径要一致
load("aaa.js") #返回true则成功
16.2. 事务性
package com.itheima.sh.demo_01;import com.mongodb.MongoClient;import com.mongodb.ServerAddress;import com.mongodb.client.*;import com.mongodb.client.model.Filters;import org.bson.Document;import java.util.ArrayList;import java.util.List;import static com.mongodb.client.model.Filters.eq;import static com.mongodb.client.model.Updates.inc;public class Demo02 {static MongoClient mongoClient;static MongoDatabase mongoDatabase;static MongoCollection<Document> collection;public static void main(String[] args) throws Exception{///////////////////////////////////////////////////////////////////////////////////////////////////////////////副本集final List<ServerAddress> servers=new ArrayList<ServerAddress>();servers.add(new ServerAddress("127.0.0.1", 27000));servers.add(new ServerAddress("127.0.0.1", 27001));servers.add(new ServerAddress("127.0.0.1", 27002));mongoClient = new MongoClient(servers);// 连接到数据库 itcast表示数据库名mongoDatabase = mongoClient.getDatabase("itcast");//打印数据库最开始的状态printDataState();//转账 带事务的transerTransacFunds("a", "b", 100);}//带事务的转账private static void transerTransacFunds(String a, String b, int money) {System.out.println("------------使用事务------------");System.out.println("a向b转账100元,有可能会发生异常,回到之前的状态。两个人的操作在同一个事务中");System.out.println("-------------------------------");//获取sessionClientSession session = mongoClient.startSession();try {//开启事务session.startTransaction();//a减100minusTransacFromA(session, a, money);//模拟异常int x = 1 / 0;//b加100addTransacToB(session, b, money);//提交事务session.commitTransaction();} catch (Exception e) {System.out.println("带事务,转账失败,回到开启事务之前的状态");//回滚事务session.abortTransaction();} finally {//关闭sessionsession.close();//输出账户状态printDataState();}}//带事务b加100private static void addTransacToB(ClientSession session, String b, int money) {System.out.println("b账户增加100");//更新文档 将文档中likes=100的文档修改为likes=200collection.updateMany(session, Filters.eq("name", b), inc("money", money));}//带事务a减100private static void minusTransacFromA(ClientSession session, String a, int money) {System.out.println("a账户减少100");//inc 表示累加函数collection.updateMany(session, Filters.eq("name", a), inc("money", -money));}private static void printDataState() {//persons表示itcast数据库中的集合名collection = mongoDatabase.getCollection("account");System.out.println("数据库中的起始状态:");//检索所有文档/*** 1. 获取迭代器FindIterable<Document>* 2. 获取游标MongoCursor<Document>* 3. 通过游标遍历检索出的文档集合* *///1. 获取迭代器FindIterable<Document>FindIterable<Document> findIterable = collection.find();//2. 获取游标MongoCursor<Document>MongoCursor<Document> mongoCursor = findIterable.iterator();//3. 通过游标遍历检索出的文档集合while (mongoCursor.hasNext()) {System.out.println(mongoCursor.next());}}}
16.3. 聚合数据类型转换
https://www.runoob.com/mongodb/mongodb-replication.html
MongoDB4.0增加了一个新的聚合操作符:$convert,用来进行数据类型的转换。这个类型转换操作符简化了数据的抽取、转换和加载的过程。同时将客户端的处理数据的压力转移到了服务器端。从而减轻了客户端处理数据的压力。
#from -- 转账发起人#to -- 转账接收人#time -- 转账时间#money -- 转账金额use itcast;var one = {"from":"a","to":"b","money":100};var two = {"from":"a","to":"b","money":200,"time":ISODate("2018-05-11T13:58:51.122Z")};var thr = {"from":"a","to":"b","money":300,"time":"2018-07-10 14:38:50"};var four = {"from":"a","to":"b","money":100,"time":"2017-04-16 14:38:50"};var five = {"from":"a","to":"b","money":500,"time":1569569092514};var six = {"from":"a","to":"b","money":500,"time":"Last Friday"};db.transfer.insertMany([one,two,thr,four,five,six]);
我们发现上述结果的时间每条转账记录都不一致。非常杂乱。有的转账记录是标准的例如ISODate,有的时间是字符串,有的是使用整数表示,而还有的根本没有转账记录。还有的时间是无效的。
那么这个时候我们就可以使用MongoDB4.0引入的数据类型转换的操作符来将转账时间统一为一致的数据类型。
conversionStage={//聚合通道$project:{//在数据映射通道中保留原来的数据项,设置为1from:1,to:1,money:1,time:{//转账时间需要做一些类型的转换//转换操作符$convert:{//必须书写的 原来的转账时间input:"$time",//必须书写的 希望把这一项数据转换哪种类型,date就是mongo的标准时间类型ISODateto:"date",//onError这一项是可选的。对于存在的属性,但是属性值是完全没有办法转换为标准的日期格式,可以对其显示。onError:{//$concat表示字段拼接操作符$concat:["can not convert ",{$toString:"$time"}," to date type"]},//缺失转账时间这一项,可以对其提示onNull:"missing time"}}}};
使用聚合函数aggregate()处理上述数据。执行转换操作
#db.集合名.aggregate([聚合操作内容])db.transfer.aggregate([conversionStage]);
