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\db
systemLog:
destination: file
path: D:\compile\mongodb-win32-x86_64-2012plus-4.2.16\data\logs\mongolog.log
logAppend: 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" --install
net start mongodb
4.2. Linux启动
解压安装
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.2.15.tgz
tar -zxvf mongodb-linux-x86_64-rhel70-4.2.15.tgz
mv mongodb-linux-x86_64-rhel70-4.2.15 /usr/local/mongodb
mkdir -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 information
destination: file
# mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径
path: "/mongodb/single/log/mongod.log"
# 当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。
logAppend: true
storage:
# 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: true
processManagement:
# 启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
net:
# 服务实例绑定的IP,默认是localhost
bindIp: localhost,192.168.130.212 #ip为当前服务器局域网ip地址 不设置外部无法通过ip访问
# bindIp
# 绑定的端口,默认是27017
port: 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/mongodb
export PATH=$PATH:$MONGODB_HOME/bin
4.2.2. 配置为服务加入启动
cd /lib/systemd/system
vi mongodb.service
#配置内容
[Unit]
Description=mongodb
After=network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
ExecStart=/usr/local/mongodb/bin/mongod --config /mongodb/single/mongodb.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/usr/local/mongodb/bin/mongod --shutdown --config /mongodb/single/mongodb.conf
PrivateTmp=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.local
chmod 754 mongodb.service
#启动服务
systemctl start mongodb.service
#关闭服务
systemctl stop mongodb.service
#开机启动
systemctl enable mongodb.service
#关闭开启启动
systemctl disable mongodb.service
4.2.3. 标准的关闭方法
mongo
use admin
db.shutdownServer()
进程id关闭法
ps -ef | grep mongo
kill -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 > 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
#例子
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”,则该注解可以省略,否则必须写
@Id
private String id;//主键
//该属性对应mongodb的字段的名字,如果一致,则无需该注解
@Field("content")
private String content;//吐槽内容
private Date publishtime;//发布日期
//添加了一个单字段的索引
@Indexed
private String userid;//发布人ID
private String nickname;//昵称
private LocalDateTime createdatetime;//评论的日期时间
private Integer likenum;//点赞数
private Integer replynum;//回复数
private String state;//状态
private String parentid;//上级ID
private 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;
}
@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 + '\'' +
'}';
}
}
- @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层 并调用动态代理里的方法
```java
package 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;
@Service
public class CommentService {
@Autowired
private CommentRepository commentRepository;
/**
* 保存一个评论
*
* @param comment
*/
public void saveComment(Comment comment) {
//如果需要自定义主键,可以在这里指定主键;如果不指定主键,MongoDB会自动生成主键
//设置一些默认初始值。。。
//调用dao
commentRepository.save(comment);
}
/**
* 更新评论
*
* @param comment
*/
public void updateComment(Comment comment) {
//调用dao
commentRepository.save(comment);
}
/**
* 根据id删除评论
*
* @param id
*/
public void deleteCommentById(String id) {
//调用dao
commentRepository.deleteById(id);
}
/**
* 查询所有评论
*
* @return
*/
public List<Comment> findCommentList() {
//调用dao
return commentRepository.findAll();
}
/**
* 根据id查询评论
*
* @param id
* @return
*/
public Comment findCommentById(String id) {
//调用dao
return 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 {
@Autowired
private CommentService commentService;
@Test
public void testFindCommentList() {
List<Comment> commentList = commentService.findCommentList();
System.out.println(commentList);
}
@Test
public void testFndCommentById() {
Comment commentById = commentService.findCommentById("1");
System.out.println(commentById);
}
/**
* 保存一个评论
*/
@Test
public void testSaveComment() {
Comment comment = new Comment();
//此处没有指定id 但MongoDB默认自动生成 我们推荐自动生成 不推荐自己书写id
comment.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
@Autowired
private 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: true
storage:
#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。
dbPath: "/mongodb/replica_sets/myrs_27017/data/db"
journal:
#启用或禁用持久性日志以确保数据文件保持有效和可恢复。
enabled: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID
pidFilePath: "/mongodb/replica_sets/myrs_27017/log/mongod.pid"
net:
#服务实例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip
#bindIpAll: true
#服务实例绑定的IP
bindIp: localhost,192.168.130.212
#bindIp
#绑定的端口
port: 27017
replication:
#副本集的名称 同一个副本集中集群必须一致
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/db
vim /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: true
storage:
#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。
dbPath: "/mongodb/replica_sets/myrs_27018/data/db"
journal:
#启用或禁用持久性日志以确保数据文件保持有效和可恢复。
enabled: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID
pidFilePath: "/mongodb/replica_sets/myrs_27018/log/mongod.pid"
net:
#服务实例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip
#bindIpAll: true
#服务实例绑定的IP
bindIp: localhost,192.168.130.212
#bindIp
#绑定的端口
port: 27018
replication:
#副本集的名称
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/db
vim /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: true
storage:
#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。
dbPath: "/mongodb/replica_sets/myrs_27019/data/db"
journal:
#启用或禁用持久性日志以确保数据文件保持有效和可恢复。
enabled: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID
pidFilePath: "/mongodb/replica_sets/myrs_27019/log/mongod.pid"
net:
#服务实例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip
#bindIpAll: true
#服务实例绑定的IP
bindIp: localhost,192.168.130.212
#bindIp
#绑定的端口
port: 27019
replication:
#副本集的名称
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修改为当前主节点的ip
var 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/article
db?connect=replicaSet&slaveOk=true&replicaSet=myrs
14. 分片集群
分片(sharding)是一种跨多台机器分布数据的方法, MongoDB使用分片来支持具有非常大的数据集
和高吞吐量操作的部署。
MongoDB分片群集包含以下组件:
- 分片(存储):每个分片包含分片数据的子集。 每个分片都可以部署为副本集。
- mongos (路由):mongos充当查询路由器,在客户端应用程序和分片集群之间提供接口。
- config servers (“调度”的配置):配置服务器存储群集的元数据和配置设置。 从MongoDB 3.4开
始,必须将配置服务器部署为副本集(CSRS)。
14.1. 搭建分片集群架构
14.1.1. 第一个副本集
#-----------myshardrs01
mkdir -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_27018
vim /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: true
storage:
#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。
dbPath: "/mongodb/sharded_cluster/myshardrs01_27018/data/db"
journal:
#启用或禁用持久性日志以确保数据文件保持有效和可恢复。
enabled: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID
pidFilePath: "/mongodb/sharded_cluster/myshardrs01_27018/log/mongod.pid"
net:
#服务实例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip
#bindIpAll: true
#服务实例绑定的IP
bindIp: localhost,192.168.130.212
#bindIp
#绑定的端口
port: 27018
replication:
#副本集的名称
replSetName: myshardrs01
sharding:
#分片角色
clusterRole: shardsvr
clusterRole 设置分片角色:
- shardsvr 分片角色
- configsvr 配置角色
#myshardrs01_27118
vim /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: true
storage:
#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。
dbPath: "/mongodb/sharded_cluster/myshardrs01_27118/data/db"
journal:
#启用或禁用持久性日志以确保数据文件保持有效和可恢复。
enabled: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID
pidFilePath: "/mongodb/sharded_cluster/myshardrs01_27118/log/mongod.pid"
net:
#服务实例绑定所有IP
#bindIpAll: true
#服务实例绑定的IP
bindIp: localhost,192.168.130.212
#绑定的端口
port: 27118
replication:
replSetName: myshardrs01
sharding:
clusterRole: shardsvr
#myshardrs01_27218
vim /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: true
storage:
#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。
dbPath: "/mongodb/sharded_cluster/myshardrs01_27218/data/db"
journal:
#启用或禁用持久性日志以确保数据文件保持有效和可恢复。
enabled: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID
pidFilePath: "/mongodb/sharded_cluster/myshardrs01_27218/log/mongod.pid"
net:
#服务实例绑定的IP
bindIp: localhost,192.168.42.130.212
#绑定的端口
port: 27218
replication:
replSetName: myshardrs01
sharding:
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. 第二套副本集
#-----------myshardrs02
mkdir -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_27318
vim /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: true
storage:
#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。
dbPath: "/mongodb/sharded_cluster/myshardrs02_27318/data/db"
journal:
#启用或禁用持久性日志以确保数据文件保持有效和可恢复。
enabled: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID
pidFilePath: "/mongodb/sharded_cluster/myshardrs02_27318/log/mongod.pid"
net:
#服务实例绑定的IP
bindIp: localhost,192.168.130.212
#绑定的端口
port: 27318
replication:
replSetName: myshardrs02
sharding:
clusterRole: shardsvr
#myshardrs02_27418
vim /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: true
storage:
#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。
dbPath: "/mongodb/sharded_cluster/myshardrs02_27418/data/db"
journal:
#启用或禁用持久性日志以确保数据文件保持有效和可恢复。
enabled: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID
pidFilePath: "/mongodb/sharded_cluster/myshardrs02_27418/log/mongod.pid"
net:
#服务实例绑定所有IP
#bindIpAll: true
#服务实例绑定的IP
bindIp: localhost,192.168.130.212
#绑定的端口
port: 27418
replication:
replSetName: myshardrs02
sharding:
clusterRole: shardsvr
#myshardrs02_27518
vim /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: true
storage:
#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。
dbPath: "/mongodb/sharded_cluster/myshardrs02_27518/data/db"
journal:
#启用或禁用持久性日志以确保数据文件保持有效和可恢复。
enabled: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID
pidFilePath: "/mongodb/sharded_cluster/myshardrs02_27518/log/mongod.pid"
net:
#服务实例绑定所有IP
#bindIpAll: true
#服务实例绑定的IP
bindIp: localhost,192.168.130.212
#绑定的端口
port: 27518
replication:
replSetName: myshardrs02
sharding:
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.conf
ps -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_27019
vim /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: true
storage:
#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。
dbPath: "/mongodb/sharded_cluster/myconfigrs_27019/data/db"
journal:
#启用或禁用持久性日志以确保数据文件保持有效和可恢复。
enabled: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID
pidFilePath: "/mongodb/sharded_cluster/myconfigrs_27019/log/mongod.pid"
net:
#服务实例绑定所有IP
#bindIpAll: true
#服务实例绑定的IP
bindIp: localhost,192.168.130.212
#绑定的端口
port: 27019
replication:
replSetName: myconfigrs
sharding:
clusterRole: configsvr
此时分片角色为配置角色
#myconfigrs_27119
vim /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: true
storage:
#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。
dbPath: "/mongodb/sharded_cluster/myconfigrs_27119/data/db"
journal:
#启用或禁用持久性日志以确保数据文件保持有效和可恢复。
enabled: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID
pidFilePath: "/mongodb/sharded_cluster/myconfigrs_27119/log/mongod.pid"
net:
#服务实例绑定所有IP
#bindIpAll: true
#服务实例绑定的IP
bindIp: localhost,192.168.130.212
#绑定的端口
port: 27119
replication:
replSetName: myconfigrs
sharding:
clusterRole: configsvr
#myconfigrs_27219
vim /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: true
storage:
#mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。
dbPath: "/mongodb/sharded_cluster/myconfigrs_27219/data/db"
journal:
#启用或禁用持久性日志以确保数据文件保持有效和可恢复。
enabled: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID
pidFilePath: "/mongodb/sharded_cluster/myconfigrs_27219/log/mongod.pid"
net:
#服务实例绑定所有IP
#bindIpAll: true
#服务实例绑定的IP
bindIp: localhost,192.168.42.130.212
#绑定的端口
port: 27219
replication:
replSetName: myconfigrs
sharding:
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.conf
ps -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. 路由节点创建和连接
#-----------mongos01
mkdir -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: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID
pidFilePath: /mongodb/sharded_cluster/mymongos_27017/log/mongod.pid"
net:
#服务实例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip
#bindIpAll: true
#服务实例绑定的IP
bindIp: localhost,192.168.130.212
#bindIp
#绑定的端口
port: 27017
sharding:
#指定配置节点副本集
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 admin
db.runCommand( { removeShard: "myshardrs02" } )
db.printShardingStatus() #显示集群的详细信息
sh.isBalancerRunning() #查询交换器是否工作
sh.getBalancerState() #查看Balancer状态
14.1.5.2. 范围规则配置数据块大小
范围规则默认存放在数据块中 如果数据块(chunk)没有填满,默认的数据块尺寸(chunksize)是64M,填满后才会考虑向其他片的数据块填充数据
use config
db.settings.save({_id:"chunksize", value: 64}) #默认为64m
14.1.6. 追加路由节点
#-----------mongos02
mkdir -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: true
processManagement:
#启用在后台运行mongos或mongod进程的守护进程模式。
fork: true
#指定用于保存mongos或mongod进程的进程ID的文件位置,其中mongos或mongod将写入其PID
pidFilePath: /mongodb/sharded_cluster/mymongos_27117/log/mongod.pid"
net:
#服务实例绑定所有IP,有副作用,副本集初始化的时候,节点名字会自动设置为本地域名,而不是ip
#bindIpAll: true
#服务实例绑定的IP
bindIp: localhost,192.168.0.2
#bindIp
#绑定的端口
port: 27117
sharding:
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 admin
db.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 admin
db.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 admin
db.createUser({user:"myroot",pwd:"123456",roles:["root"]})
生成副本集认证的key文件 在centos中
openssl rand -base64 90 -out ./mongo.keyfile
chmod 400 ./mongo.keyfile
ll mongo.keyfile
所有副本集节点都必须要用同一份keyfile,一般是在一台机器上生成,然后拷贝到其他机器上,且必须
有读的权限,否则将来会报错
#此时应该通过网络传输此key文件给集群机器
cp mongo.keyfile /mongodb/replica_sets/myrs_27017
cp mongo.keyfile /mongodb/replica_sets/myrs_27018
cp 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
创建新的用户读和写
mongo
use admin
db.auth("myroot","123456") #先登陆认证管理员账号
use articledb
db.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 admin
db.shutdownServer()
- 生成key文件
openssl rand -base64 90 -out ./mongo.keyfile
chmod 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 admin
db.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("-------------------------------");
//获取session
ClientSession session = mongoClient.startSession();
try {
//开启事务
session.startTransaction();
//a减100
minusTransacFromA(session, a, money);
//模拟异常
int x = 1 / 0;
//b加100
addTransacToB(session, b, money);
//提交事务
session.commitTransaction();
} catch (Exception e) {
System.out.println("带事务,转账失败,回到开启事务之前的状态");
//回滚事务
session.abortTransaction();
} finally {
//关闭session
session.close();
//输出账户状态
printDataState();
}
}
//带事务b加100
private static void addTransacToB(ClientSession session, String b, int money) {
System.out.println("b账户增加100");
//更新文档 将文档中likes=100的文档修改为likes=200
collection.updateMany(session, Filters.eq("name", b), inc("money", money));
}
//带事务a减100
private 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:{
//在数据映射通道中保留原来的数据项,设置为1
from:1,
to:1,
money:1,
time:{//转账时间需要做一些类型的转换
//转换操作符
$convert:{
//必须书写的 原来的转账时间
input:"$time",
//必须书写的 希望把这一项数据转换哪种类型,date就是mongo的标准时间类型ISODate
to:"date",
//onError这一项是可选的。对于存在的属性,但是属性值是完全没有办法转换为标准的日期格式,可以对其显示。
onError:{
//$concat表示字段拼接操作符
$concat:["can not convert ",{$toString:"$time"}," to date type"]
},
//缺失转账时间这一项,可以对其提示
onNull:"missing time"
}
}
}
};
使用聚合函数aggregate()处理上述数据。执行转换操作
#db.集合名.aggregate([聚合操作内容])
db.transfer.aggregate([conversionStage]);