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

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

# 单机部署
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 是修正版本号,数字越大越好。
# 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 --logappend1
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
# 使用配置文件启动
在 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: true1
2
3
4
5
6在 bin 目录下命令行启动
mongod -f ../config/mongod.conf #或者 mongod --config ../config/mongod.conf1
2
3
# 连接数据库
在 bin 目录下命令行启动
mongo #或者 mongo --host=127.0.0.1 --port=270171
2
3show dbs; #查询数据库 exit; #退出1
2
# 安装为服务项
在 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
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
2
3
4
5
6
# 配置内容
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
启动
/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
2
3
4
5
6
7
8
如果一旦数据损坏 导致表死锁
rm -f /mongodb/single/data/db/*.lock #删除锁文件
/usr/local/mongdb/bin/mongod --repair
2
# 配置环境变量
vi /etc/profile
#配置内容
export MONGODB_HOME=/usr/local/mongodb
export PATH=$PATH:$MONGODB_HOME/bin
2
3
4
# 配置为服务加入启动
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#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
2
3
#启动服务
systemctl start mongodb.service
#关闭服务
systemctl stop mongodb.service
#开机启动
systemctl enable mongodb.service
#关闭开启启动
systemctl disable mongodb.service
2
3
4
5
6
7
8
# 标准的关闭方法
mongo
use admin
db.shutdownServer()
2
3
进程 id 关闭法
ps -ef | grep mongo
kill -2 pid
2
# 数据库
# 选择和创建数据库
#use 数据库名称
use articledb #如果没有此数据库则自动创建
#如果新的数据库没有插入数据 showdbs是无法查看到新创建的数据库 因为此时数据库存储在内存当中 并未持久化
2
3
# 查询当前有权限查看的数据
show dbs
#或者
show databases
2
3
# 查看当前正在使用的数据库名称
db
# 数据库命名规范
数据库名可以是满足以下条件的任意 UTF-8 字符串。
- 不能是空字符串( "")。
- 不得含有 ' '(空格)、.、$、/、\ 和 \0 (空字符)。
- 应全部小写。
- 最多 64 字节。
有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。
# 默认三个数据库作用
- admin : 从权限的角度来看,这是 "root" 数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
- local: 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
- config : 当 Mongo 用于分片设置时,config 数据库在内部使用,用于保存分片的相关信息。
# 数据库删除
db.dropDatabase() #删除当前使用的库
# 集合
集合,类似于关系型数据中的表
# 显式创建
#db.createCollection(集合名)
db.createCollection("my") #成功返回1
2
# 隐式创建
当向一个集合中插入文档时 如果集合不存在 则自动创建集合
通常我们使用隐式创建文档即可
# 查询集合
show collections #查询当前库的所有集合
# 集合删除
#db.集合名.drop()
db.my.drop() #成功则返回true
2
# 文档 CRUD
文档的数据结构和 JSON 基本一样
索引存储在集合中数据都是 BSON 格式
# 插入
通过 insert () 或者 save () 方法向集合中插入文档
# 单问插入
#单文档插入
db.collection.insert(
<document or array of documents>,
{
writeConcern: <document>,
ordered: <boolean>
}
)
2
3
4
5
6
7
8

#db.集合名.inset({""})
#单文档插入
db.comment.insert({"articleid":"100000","content":"今天天气真好,阳光明媚","userid":"1001","nickname":"Rose","createdatetime":new Date(),"likenum":NumberInt(10),"state":null}) #返回1则插入成功
2
3
# 批量插入
#批量插入
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"}]);
2
3
4
5
6
7
8
9
10
11
# 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)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 查询
查询当前数据库中的文档
# 查询所有文档
#db.collection.find(<query>, [projection])
db.comment.find()
2

# 条件查询
查询文档中包含此文档内容的所有文档
#db.collection.find({键:"内容"})
db.comment.find({articleid:"100001"})
2
# 只返回第一个结果
#db.collection.findOne({键:"内容"})
db.comment.findOne({articleid:"100001"})
2
# 投影查询
查询出来的文档 只显示需要的键值对 相对应 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过滤
2
3
4
# 更新
//语法
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
}
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15

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

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

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

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

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

# 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/
# 评论案例
# 模块搭建
- 继承 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>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
创建 application.yml
spring: data: mongodb: host: 192.168.130.212 # 主机地址 database: articledb # 数据库 port: 27017 # 默认端口是27017 #也可以使用uri连接 #uri: mongodb://192.168.40.134:27017/articledb1
2
3
4
5
6
7
8
# 实体类
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 + '\'' +
'}';
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
- @Document (collection = "comment") 声明为 mongodb 文档 并且映射到 comment 集合中 如果当前实体类名与集合一致则 collection 可以忽略 (并且只能是小写名称的实体类)
- @CompoundIndex (def = "{'userid': 1, 'nickname': -1}") 复合索引 def 属性与设置复活索引条件一致 为键和排序方式
- @Id 声明此属性为主键 如果属性名为 id 则该注解可以省略不加
- @Field ("content") 当成员属性名称与集合中的键字段不一致时 可以使用该注解映射为集合中指定的键字段 此时映射为 content 键字段
- @Indexed 添加为单字段索引
# 增删改查方法
创建 dao 层接口 并继承 MongoRepository 泛型为 实体类 和 id 的类型
package com.itcatst.article.dao; import com.itcatst.article.po.Comment; import org.springframework.data.mongodb.repository.MongoRepository; public interface CommentRepository extends MongoRepository<Comment,String> { }1
2
3
4
5
6
7
8创建 Service 层 注入 dao 层 并调用动态代理里的方法
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(); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# 创建测试方法
新建 junit 测试方法 类路径要与主程序一致
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); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 分页查询测试
在 dao 层接口创建新的方法 必须要指定对应的属性名为方法名否则会报错
格式: Page <实体类> findBy 根据哪个属性来查询 (传递属性类型 属性名,Pageable pageable);
////根据父id,查询子评论的分页列表 Page<Comment> findByParentid(String parentid, Pageable pageable);1
2在 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)); }1
2
3
4
5
6
7
8
9
10
11测试用例
@Test public void testFindCommentListByParentid(){ Page<Comment> page = commentService.findCommentListByParentid("3", 1, 2); System.out.println(page.getTotalElements());//返回总条数 List<Comment> content = page.getContent(); //返回结果的集合 System.out.println(content); }1
2
3
4
5
6
7
8
# 评论点赞
最简单的方法就是根据 id 查询当前对应的文档 并将点赞数 + 1 但执行效率不高 因为只需要修改点赞数 没有必要把所有的字段都查询出来
我们可以使用 MongoTemplate 类实现对某列进行操作
在 service 层注入 MongoTemplate 并编修改方法
@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); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 副本集
MongoDB 中的副本集(Replica Set)是一组维护相同数据集的 mongod 服务。 副本集可提供冗余和高 可用性,是所有生产部署的基础。
主从复制和副本集区别 主从集群和副本集最大的区别就是副本集没有固定的 “主节点”;整个集群会选出一个 “主节点”,当其挂 掉后,又在剩下的从节点中选中其他节点为 “主节点”,副本集总有一个活跃点 (主、primary) 和一个或多 个备份节点 (从、secondary)。
副本集有两种类型三种角色:
两种类型:
- 主节点( Primary)类型:数据操作的主要连接点,可读写。
- 次要(辅助、从)节点( Secondaries)类型:数据冗余备份节点,可以读或选举。
三种角色:
- 主要成员(Primary):主要接收所有写操作。就是主节点。
- 副本成员(Replicate):从主节点通过复制操作以维护相同的数据集,即备份数据,不可写操作,但可 以读操作(但需要配置)。是默认的一种从节点类型。
- 仲裁者( Arbiter):不保留任何数据的副本,只具有投票选举作用。当然也可以将仲裁服务器维护为副 本集的一部分,即副本成员同时也可以是仲裁者。也是一种从节点类型。
# 副本集创建
# 主节点
#主节点
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
2
3
4
5
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
启动主节点
/usr/local/mongodb/bin/mongod -f /mongodb/replica_sets/myrs_27017/mongod.conf
# 从节点
#副本节点
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
2
3
4
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
启动节点
/usr/local/mongodb/bin/mongod -f /mongodb/replica_sets/myrs_27018/mongod.conf
# 仲裁节点
#仲裁节点
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
2
3
4
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
启动节点
/usr/local/mongodb/bin/mongod -f /mongodb/replica_sets/myrs_27019/mongod.conf
# 初始化配置副本集和主节点
#使用mongo客户端任意节点都可以 但尽量连上主节点
/usr/local/mongodb/bin/mongo --host=192.168.130.212 --port=27017
#初始化命令
rs.initiate() #使用默认的配置来初始化副本集 “ok”的值为1,说明创建成功 并且此节点变为主节点
rs.conf() #查看默认节点配置
rs.status() # 查询副本集状态
2
3
4
5
6
7
8
9
# 添加副本从节点
#rs.add(host:post)
rs.add(192.168.130.212:27018)
rs.status() #查看副本集状态
2
3
4
# 添加仲裁节点
#rs.addArb(host:post)
rs.addarB(192.168.130.212:27019)
rs.status() #查看副本集状态
2
3
4
# 副本集的数据读写操作
只有主节点写入数据和读取数据
默认情况从节点无法读取数据 也无法写入数据 可以进行设置增加读的权限
#连接从节点的客户端 rs.slaveOk() #该命令是 db.getMongo().setSlaveOk() 的简化命令 #或者 rs.slaveOk(true) #此时实现了读写分离 让主插入数据 让从来读取数据1
2
3
4
5仲裁者节点,不存放任何业务数据的,可以登录查看 只存放副本集配置等数据。
# 主节点的选举原则
MongoDB 在副本集中,会自动进行主节点的选举,主节点选举的触发条件:
- 主节点故障
- 主节点网络不可达(默认心跳信息为 10 秒)
- 人工干预(rs.stepDown (600))
选举规则是根据票数来决定谁获胜:
- 票数最高,且获得了 “大多数” 成员的投票支持的节点获胜。 “大多数” 的定义为:假设复制集内投票成员数量为 N,则大多数为 N/2 + 1。例如:3 个投票成员, 则大多数的值是 2。当复制集内存活成员数量不足大多数时,整个复制集将无法选举出 Primary, 复制集将无法提供写服务,处于只读状态。
- 若票数相同,且都获得了 “大多数” 成员的投票支持的,数据新的节点获胜。 数据的新旧是通过操作日志 oplog 来对比的。
在获得票数的时候,优先级(priority)参数影响重大。 可以通过设置优先级(priority)来设置额外票数。优先级即权重,取值为 0-1000,相当于可额外增加 0-1000 的票数,优先级的值越大,就越可能获得多数成员的投票(votes)数。指定较高的值可使成员 更有资格成为主要成员,更低的值可使成员更不符合条件。 默认情况下,优先级的值是 1
# 提升优先级
#先导出配置到变量中
cfg=rs.conf()
#修改指定优先级 ID号默认从0开始
cfg.members[1].priority=2
#重新加载配置
rs.reconfig(cfg)
2
3
4
5
6
# Compass 连接副本集
#将host修改为当前主节点的ip
var config = rs.config();
config.members[0].host="192.168.130.212:27017";rs.reconfig(config)
2
3

选择 Primary 为主节点 Secondary 为副本集
# 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
2
3
4
5
6
7
8
9
10
11
# 分片集群
分片(sharding)是一种跨多台机器分布数据的方法, MongoDB 使用分片来支持具有非常大的数据集 和高吞吐量操作的部署。
MongoDB 分片群集包含以下组件:
- 分片(存储):每个分片包含分片数据的子集。 每个分片都可以部署为副本集。
- mongos (路由):mongos 充当查询路由器,在客户端应用程序和分片集群之间提供接口。
- config servers (“调度” 的配置):配置服务器存储群集的元数据和配置设置。 从 MongoDB 3.4 开 始,必须将配置服务器部署为副本集(CSRS)。

# 搭建分片集群架构

# 第一个副本集
#-----------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
2
3
4
5
6
7
8
9
10
11
12
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
clusterRole 设置分片角色:
- shardsvr 分片角色
- configsvr 配置角色
#myshardrs01_27118
vim /mongodb/sharded_cluster/myshardrs01_27118/mongod.conf
2
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#myshardrs01_27218
vim /mongodb/sharded_cluster/myshardrs01_27218/mongod.conf
2
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
启动第一套副本集:一主一副本一仲裁
/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
2
3
- 初始化副本集和创建主节点
/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()
2
3
4
5
# 第二套副本集
#-----------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
2
3
4
5
6
7
8
9
#myshardrs02_27318
vim /mongodb/sharded_cluster/myshardrs02_27318/mongod.conf
2
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#myshardrs02_27418
vim /mongodb/sharded_cluster/myshardrs02_27418/mongod.conf
2
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#myshardrs02_27518
vim /mongodb/sharded_cluster/myshardrs02_27518/mongod.conf
2
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
启动第二套副本集:一主一副本一仲裁
/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个服务
2
3
4
- 初始化副本集和创建主节点
/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()
2
3
4
5
# 配置节点副本集
#-----------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
2
3
4
5
6
7
8
9
10
#myconfigrs_27019
vim /mongodb/sharded_cluster/myconfigrs_27019/mongod.conf
2
#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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
此时分片角色为配置角色
#myconfigrs_27119
vim /mongodb/sharded_cluster/myconfigrs_27119/mongod.conf
2
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#myconfigrs_27219
vim /mongodb/sharded_cluster/myconfigrs_27219/mongod.conf
2
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
启动配置副本集:一主两副本
/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个服务
2
3
4
- 初始化副本集和创建主节点
/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()
2
3
4
5
# 路由节点创建和连接
#-----------mongos01
mkdir -p /mongodb/sharded_cluster/mymongos_27017/log
2
#mymongos_27017节点
vi /mongodb/sharded_cluster/mymongos_27017/mongos.conf
2
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
configDB 地址为 配置副本集名称 / 配置节点 1, 配置节点 2, 配置节点 3
#启动
/usr/local/mongodb/bin/mongos -f /mongodb/sharded_cluster/mymongos_27017/mongos.conf
2
#连接 此时为mongos
/usr/local/mongodb/bin/mongo --host 192.168.130.212 --port 27017
2
通过路由节点操作,现在只是连接了配置节点,还没有连接分片数据节点,因此写不进去数据,如果写数据会报错。
# 路由节点添加分片
#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") #开启分片功能
2
3
4
5
6
# 集合分片和分片规则
sh.shardCollection(namespace, key, unique)

分片规则一:哈希策略 对指定字段的值 进行哈希计算出存放的位置 每个副本集对应则对应的范围 通过哈希计算出存放的副本集中
sh.shardCollection("articledb.comment",{"nickname":"hashed"})1分片规则二:范围策略 对于 基于范围的分片,MongoDB 按键的范围把数据分成不同部分.
sh.shardCollection("articledb.author",{"age":1})1
注意事项:
- 一个集合只能指定一个片键,否则报错。
- 一旦对一个集合分片,分片键和分片值就不可改变。 如:不能给集合选择不同的分片键、不能更新 分片键的值。
- 根据 age 索引进行分配数据
如果添加分片失败,需要先手动移除分片,检查添加分片的信息的正确性后,再次添加分片。
use admin
db.runCommand( { removeShard: "myshardrs02" } )
2
db.printShardingStatus() #显示集群的详细信息
sh.isBalancerRunning() #查询交换器是否工作
sh.getBalancerState() #查看Balancer状态
2
3
# 范围规则配置数据块大小
范围规则默认存放在数据块中 如果数据块(chunk)没有填满,默认的数据块尺寸(chunksize)是 64M,填满后才会考虑向其他片的数据块填充数据
use config
db.settings.save({_id:"chunksize", value: 64}) #默认为64m
2
# 追加路由节点
#-----------mongos02
mkdir -p /mongodb/sharded_cluster/mymongos_27117/log
2
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#启动
/usr/local/mongodb/bin/mongos -f /mongodb/sharded_cluster/mymongos_27117/mongos.conf
2
第二个路由无需配置,因为分片配置都保存到了配置服务器中了。
# Compass 连接分片集群
连接路由节点即可

# 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
2
3
4
5
6
7
8
9
10
11
12
13
如果有多个节点可以使用逗号隔开 SpringDataMongDB 默认有负载均衡策略
# 安全认证
默认情况下,MongoDB 实例启动运行时是没有启用用户访问权限控制的
# 角色
常用的内置角色:
- 数据库用户角色: 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>" } } #查询其它数据库中指定的角色权限
2
3
4
# 单实例安全认证
- 添加用户和权限
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") #修改指定用户密码
2
3
4
5
6
7
- 本案例创建了两个用户,分别对应超管和专门用来管理用户的角色,事实上,你只需要一个用户即 可。如果你对安全要求很高,防止超管泄漏,则不要创建超管用户。
- 和其它数据库(MySQL)一样,权限的管理都差不多一样,也是将用户和权限信息保存到数据库对 应的表中。Mongodb 存储所有的用户信息在 admin 数据库的集合 system.users 中,保存用户名、密码 和数据库信息。
- 如果不指定数据库,则创建的指定的权限的用户在所有的数据库上有效,如
{role:"userAdminAnyDatabase", db:""}
- 认证测试
use admin
db.auth("myroot","123456")
2
如果开启了认证后,登录的客户端的用户必须使用 admin 库的角色,如拥有 root 角色的 myadmin 用 户,再通过 myadmin 用户去创建其他角色的用户
# 服务端开启认证和客户端连接登陆
参数方式 启动服务端时加锁 --auth 参数
/usr/local/mongodb/bin/mongod -f /mongodb/single/mongod.conf --auth1配置方式
vim /mongodb/single/mongod.conf #配置文件追加 security: authorization: enabled #开启授权认证1
2
3
4
5
#此时连接可以进入客户端 但无法执行任何操作 只有认证成功后能使用
use admin #此用户能查看什么库就切换到什么库中再认证 否则报错
db.auth("myroot","123456")
2
3
连接客户端时直接认证 同样查看什么库就切换到什么库中再认证 否则报错
mongo --host 192.168.42.130.212 --port 27017 --authenticationDatabase admin -u myroot -p 123456
- -u :用户名
- -p :密码
- -- authenticationDatabase :指定连接到哪个库。当登录是指定用户名密码时,必须指定对应的 数据库!
# SpringDataMongoDB 连接认证
spring:
#数据源配置
data:
mongodb:
# 主机地址
# host: 192.168.42.130.212
# 数据库
# database: articledb
# 默认端口是27017
# port: 27017
#帐号
# username: bobo
#密码
# password: 123456
#单机有认证的情况下,也使用字符串连接
uri: mongodb://bobo:[email protected]/articledb
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 副本集环境
只需要在主节点上添加用户,副本集会自动同步
use admin
db.createUser({user:"myroot",pwd:"123456",roles:["root"]})
2
生成副本集认证的 key 文件 在 centos 中
openssl rand -base64 90 -out ./mongo.keyfile
chmod 400 ./mongo.keyfile
ll mongo.keyfile
2
3
所有副本集节点都必须要用同一份 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
2
3
4
修改各个节点的配置文件
vim /mongodb/replica_sets/myrs_27017/mongod.conf
security:
#KeyFile鉴权文件
keyFile: /mongodb/replica_sets/myrs_27017/mongo.keyfile
#开启认证方式运行
authorization: enabled
2
3
4
5
vim /mongodb/replica_sets/myrs_27018/mongod.conf
security:
#KeyFile鉴权文件
keyFile: /mongodb/replica_sets/myrs_27018/mongo.keyfile
#开启认证方式运行
authorization: enabled
2
3
4
5
vim /mongodb/replica_sets/myrs_27019/mongod.conf
security:
#KeyFile鉴权文件
keyFile: /mongodb/replica_sets/myrs_27019/mongo.keyfile
#开启认证方式运行
authorization: enabled
2
3
4
5
重新启动
/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
2
3
创建新的用户读和写
mongo
use admin
db.auth("myroot","123456") #先登陆认证管理员账号
use articledb
db.createUser({user: "bobo", pwd: "123456", roles: ["readWrite"]})
2
3
4
5
# SpringDataMongoDB 连接副本集
spring:
#数据源配置
data:
mongodb:
#副本集有认证的情况下,字符串连接
uri:
mongodb://bobo:[email protected]:27017,192.168.42.130.212:27018,192.168.42.130.212:27019/articledb?connect=replicaSet&slaveOk=true&replicaSet=myrs
2
3
4
5
6
7
# 分片集群环境
关闭配置服务器副本集的服务,建议依次关闭副本节点、主节点再关闭路由服务器的服务
rs.stepDown() #告知副本集说本机要下线
use admin
db.shutdownServer()
2
3
- 生成 key 文件
openssl rand -base64 90 -out ./mongo.keyfile
chmod 400 ./mongo.keyfile
2
拷贝 key 文件到各个服务上
修改配置文件
#这里是各个服务上的配置文件 vim /mongodb/sharded_cluster/myshardrs01_27018/mongod.conf1
2security: #KeyFile鉴权文件 注意要修改路径 keyFile: /mongodb/replica_sets/myrs_27019/mongo.keyfile #开启认证方式运行 authorization: enabled1
2
3
4
5重新启动各个节点
先启动配置节点,再启动分片节点,最后启动路由节点。如果先启动分片节点,会卡住
/usr/local/mongodb/bin/mongod -f /mongodb/sharded_cluster/myconfigrs_27019/mongod.conf1客户端 mongo,通过 localhost 登录任意一个 mongos 路由
/usr/local/mongodb/bin/mongo --port 270171创建账号
use admin db.createUser({user:"myroot",pwd:"123456",roles:["root"]}) #管理员账号 db.createUser({user: "bobo", pwd: "123456", roles: [{ role: "readWrite",db: "articledb" }]}) #指定库读写账号1
2
3
# SpringDataMongoDB 连接认证
spring:
#数据源配置
data:
mongodb:
# 分片集群有认证的情况下,字符串连接
uri:
mongodb://bobo:[email protected]:27017,192.168.42.130.212:27117/articledb
2
3
4
5
6
7
# 4.0 新特性
# 加载外部 js 文件
加载当前路径下的 js 文件 启动 shell 命令时的路径和 js 所在路径要一致
load("aaa.js") #返回true则成功
# 事务性
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());
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# 聚合数据类型转换
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]);
2
3
4
5
6
7
8
9
10
11
12
我们发现上述结果的时间每条转账记录都不一致。非常杂乱。有的转账记录是标准的例如 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"
}
}
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
使用聚合函数 aggregate () 处理上述数据。执行转换操作
#db.集合名.aggregate([聚合操作内容])
db.transfer.aggregate([conversionStage]);
2