mongodb_基础到进阶 – MongoDB 高级–MongoDB 集群部署与安全性(二)

一、mongodb 仲裁节点数据操作

1、mongodb 仲裁者节点,不存放任何业务数据的,可以登录查看

另打开一个终端,登录从节点 (端口 27019),读取数据:


# 打开另一个终端

# 切换目录 
cd /usr/local/mongodb/

# 登录从节点 (端口 27019)
root@WIN-20240529BJA:/usr/local/mongodb# ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27019

# 查询数据库
myrs:ARBITER> show dbs
uncaught exception: Error: listDatabases failed:{
        "topologyVersion" : {
                "processId" : ObjectId("66aa1787c5485192848dde80"),
                "counter" : NumberLong(1)
        },
        "ok" : 0,
        "errmsg" : "not master and slaveOk=false",
        "code" : 13435,
        "codeName" : "NotPrimaryNoSecondaryOk"
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs/<@src/mongo/shell/mongo.js:145:19
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:97:12
shellHelper.show@src/mongo/shell/utils.js:960:13
shellHelper@src/mongo/shell/utils.js:842:15
@(shellhelp2):1:1
myrs:ARBITER> 

# 设置 (端口 27018)为奴隶节点,允许在从成员上运行读的操作
myrs:ARBITER> rs.slaveOk()
WARNING: slaveOk() is deprecated and may be removed in the next major release. Please use secondaryOk() instead.

# 或者 
myrs:ARBITER>  rs.slaveOk(true)

# rs.slaveOk() 该命令是 db.getMongo().setSlaveOk() 的简化命令。

# 再次查询数据库,
myrs:ARBITER> show databases
uncaught exception: Error: listDatabases failed:{
        "topologyVersion" : {
                "processId" : ObjectId("66aa1787c5485192848dde80"),
                "counter" : NumberLong(1)
        },
        "ok" : 0,
        "errmsg" : "node is not in primary or recovering state",
        "code" : 13436,
        "codeName" : "NotPrimaryOrSecondary"
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs/<@src/mongo/shell/mongo.js:145:19
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:97:12
shellHelper.show@src/mongo/shell/utils.js:960:13
shellHelper@src/mongo/shell/utils.js:842:15
@(shellhelp2):1:1
myrs:ARBITER> 

2、mongodb 仲裁节点,只存放副本集配置等数据。


myrs:ARBITER> use local
switched to db local
myrs:ARBITER> show collections
replset.election
replset.minvalid
replset.oplogTruncateAfterPoint
startup_log
system.replset
system.rollback.id
system.tenantMigration.oplogView
system.views

二、mongodb 主节点的选举原则

1、MongoDB 在副本集中,会自动进行主节点的选举,主节点选举的触发条件:

1) 主节点故障
2) 主节点网络不可达(默认心跳信息为10秒)
3) 人工干预(rs.stepDown(600))

2、MongoDB 一旦触发选举,就要根据一定规则来选主节点。选举规则是根据票数来决定谁获胜:

  • 票数最高,且获得了 “大多数”成员的投票支持的节点获胜。

    “大多数”的定义为:假设复制集内投票成员数量为N,则大多数为 N/2 + 1。例如:3个投票成员,
    则大多数的值是2。当复制集内存活成员数量不足大多数时,整个复制集将无法选举出Primary,
    复制集将无法提供写服务,处于只读状态。

  • 若票数相同,且都获得了 “大多数”成员的投票支持的,数据新的节点获胜。
    数据的新旧是通过操作日志oplog来对比的。

3、在获得票数的时候,优先级(priority)参数影响重大。

可以通过设置优先级(priority)来设置额外票数。优先级即权重,取值为0-1000,相当于可额外增加
0-1000的票数,优先级的值越大,就越可能获得多数成员的投票(votes)数。指定较高的值可使成员
更有资格成为主要成员,更低的值可使成员更不符合条件。

4、通过 rs.conf() 可以查看优先级参数。


myrs:PRIMARY> rs.conf()
{
        "_id" : "myrs",
        "version" : 4,
        "term" : 1,
        "members" : [
                {
                        "_id" : 0,
                        "host" : "172.18.30.110:27017",
                        "arbiterOnly" : false,
                        "buildIndexes" : true,
                        "hidden" : false,
                        "priority" : 1,
                        "tags" : {

                        },
                        "secondaryDelaySecs" : NumberLong(0),
                        "votes" : 1
                },
                {
                        "_id" : 1,
                        "host" : "172.18.30.110:27018",
                        "arbiterOnly" : false,
                        "buildIndexes" : true,
                        "hidden" : false,
                        "priority" : 1,
                        "tags" : {

                        },
                        "secondaryDelaySecs" : NumberLong(0),
                        "votes" : 1
                },
                {
                        "_id" : 2,
                        "host" : "172.18.30.110:27019",
                        "arbiterOnly" : true,
                        "buildIndexes" : true,
                        "hidden" : false,
                        "priority" : 0,
                        "tags" : {

                        },
                        "secondaryDelaySecs" : NumberLong(0),
                        "votes" : 1
                }
        ],
        "protocolVersion" : NumberLong(1),
        "writeConcernMajorityJournalDefault" : true,
        "settings" : {
                "chainingAllowed" : true,
                "heartbeatIntervalMillis" : 2000,
                "heartbeatTimeoutSecs" : 10,
                "electionTimeoutMillis" : 10000,
                "catchUpTimeoutMillis" : -1,
                "catchUpTakeoverDelayMillis" : 30000,
                "getLastErrorModes" : {

                },
                "getLastErrorDefaults" : {
                        "w" : 1,
                        "wtimeout" : 0
                },
                "replicaSetId" : ObjectId("66a9e8a9ccbb5feee63a136c")
        }
}
myrs:PRIMARY> 

可以看出,主节点和副本节点的优先级各为 1,即,默认可以认为都已经有了一票。但选举节点,优先
级是0,(要注意是,官方说了,选举节点的优先级必须是0,不能是别的值。即不具备选举权,但具有
投票权)。

5、修改优先级

比如,下面提升从节点的优先级:
1)先将配置导入cfg变量

myrs:SECONDARY> cfg=rs.conf()

2 )然后修改值(ID号默认从0开始):

myrs:SECONDARY> cfg.members[1].priority=2
2

3 )重新加载配置

myrs:SECONDARY> rs.reconfig(cfg)
{ "ok" : 1 }

稍等片刻会重新开始选举。

三、mongodb 故障测试_1

1、副本节点故障测试:另打开一个终端,关闭27018副本节点:

1)关闭 27018 副本节点


# 打开另一个终端(172.18.30.110-测试)

# 切换目录 
cd /usr/local/mongodb/

# 查看进程
root@WIN-20240529BJA:/usr/local/mongodb# ps -ef | grep mongod
root      1549     1  0 14:49 ?        00:02:32 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27017/mongod.conf
root      1604     1  0 14:56 ?        00:03:14 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27018/mongod.conf
root      1919     1  0 18:52 ?        00:00:53 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27019/mongod.conf
root      2081  1337  0 19:49 pts/2    00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27017
root      2100  2067  0 20:04 pts/0    00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27018
root      2226  2216  0 21:41 pts/4    00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27019
root      2304  2289  0 22:24 pts/5    00:00:00 grep --color=auto mongod

关闭27018副本节点
root@WIN-20240529BJA:/usr/local/mongodb# kill -2 1604

2)发现,主节点和仲裁节点对27018的心跳失败。因为主节点还在,因此,没有触发投票选举。

3)此时,在主节点写入数据。


# 打开另一个终端(172.18.30.110-primary :myrs:PRIMARY> )

# 切换目录 
cd /usr/local/mongodb/

# 登录主节点 (端口 27017)
root@WIN-20240529BJA:/usr/local/mongodb# ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27017

myrs:PRIMARY> use articledb
switched to db articledb

myrs:PRIMARY> show collections
comment

myrs:PRIMARY>  db.comment.find()
{ "_id" : ObjectId("66aa2581aeb7d05de8a3a30a"), "articleid" : "100000", "content" : "今天天气真好,阳光明媚", "userid" : "1001", "nickname" : "Rose", "createdatetime" : ISODate("2024-07-31T11:52:33.679Z") }
{ "_id" : "1", "articleid" : "100001", "content" : "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。", "userid" : "1002", "nickname" : "相忘于江湖", "createdatetime" : ISODate("2019-08-05T22:08:15.522Z"), "likenum" : 1000, "state" : "1" }

# 在主节点写入数据
myrs:PRIMARY>  db.comment.insert({"_id":"2","articleid":"100002","content":"李连杰动作好帅,李小龙动作好快","userid":"1003","nickname":"张三丰统一江湖","createdatetime":new Date("2024-07-31"),"likenum":NumberInt(800),"state":"1"})

# 再次查看数据
myrs:PRIMARY> db.comment.find()
{ "_id" : ObjectId("66aa2581aeb7d05de8a3a30a"), "articleid" : "100000", "content" : "今天天气真好,阳光明媚", "userid" : "1001", "nickname" : "Rose", "createdatetime" : ISODate("2024-07-31T11:52:33.679Z") }
{ "_id" : "1", "articleid" : "100001", "content" : "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。", "userid" : "1002", "nickname" : "相忘于江湖", "createdatetime" : ISODate("2019-08-05T22:08:15.522Z"), "likenum" : 1000, "state" : "1" }
{ "_id" : "2", "articleid" : "100002", "content" : "李连杰动作好帅,李小龙动作好快", "userid" : "1003", "nickname" : "张三丰统一江湖", "createdatetime" : ISODate("2024-07-31T00:00:00Z"), "likenum" : 800, "state" : "1" }
myrs:PRIMARY> 

4)再启动从节点,会发现,主节点写入的数据,会自动同步给从节点。


# 打开另一个终端(172.18.30.110-secondary :myrs:SECONDARY>  )

# 切换目录 
cd /usr/local/mongodb/

# 启动从节点 (端口 27018)服务 
root@WIN-20240529BJA:/usr/local/mongodb# ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27018/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 2350
child process started successfully, parent exiting

# 登录从节点 (端口 27018)
root@WIN-20240529BJA:/usr/local/mongodb# ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27018

myrs:PRIMARY> use articledb
switched to db articledb

myrs:SECONDARY> rs.slaveOk()
WARNING: slaveOk() is deprecated and may be removed in the next major release. Please use secondaryOk() instead.

myrs:SECONDARY> show collections
comment

myrs:SECONDARY> db.comment.find()
{ "_id" : ObjectId("66aa2581aeb7d05de8a3a30a"), "articleid" : "100000", "content" : "今天天气真好,阳光明媚", "userid" : "1001", "nickname" : "Rose", "createdatetime" : ISODate("2024-07-31T11:52:33.679Z") }
{ "_id" : "1", "articleid" : "100001", "content" : "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。", "userid" : "1002", "nickname" : "相忘于江湖", "createdatetime" : ISODate("2019-08-05T22:08:15.522Z"), "likenum" : 1000, "state" : "1" }
{ "_id" : "2", "articleid" : "100002", "content" : "李连杰动作好帅,李小龙动作好快", "userid" : "1003", "nickname" : "张三丰统一江湖"

24-关闭27018副本节点.png

2、主节点故障测试:关闭 27017 节点

1)关闭 27017 主节点


# 打开另一个终端(172.18.30.110-测试)

# 切换目录 
cd /usr/local/mongodb/

# 查看进程
root@WIN-20240529BJA:/usr/local/mongodb# ps -ef | grep mongod
root      1549     1  0 14:49 ?        00:02:54 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27017/mongod.conf
root      1919     1  0 18:52 ?        00:01:09 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27019/mongod.conf
root      2325  1337  0 22:51 pts/2    00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27017
root      2333  2216  0 22:59 pts/4    00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27019
root      2350     1  0 23:09 ?        00:00:09 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27018/mongod.conf
root      2447  2067  0 23:09 pts/0    00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27018
root      2477  2289  0 23:35 pts/5    00:00:00 grep --color=auto mongod

关闭27018副本节点
root@WIN-20240529BJA:/usr/local/mongodb# kill -2 1549

2)发现,从节点和仲裁节点对27017的心跳失败,当失败超过10秒,此时因为没有主节点了,会自动发起
投票。

3)而副本节点只有27018,因此,候选人只有一个就是27018,开始投票。

27019向27018投了一票,27018本身自带一票,因此共两票,超过了“大多数”
27019是仲裁节点,没有选举权,27018不向其投票,其票数是0.
最终结果,27018成为主节点。具备读写功能。

4)在27018写入数据查看。


# 打开另一个终端(172.18.30.110-secondary :myrs:SECONDARY>  )

# 切换目录 
cd /usr/local/mongodb/

# 在原来从节点(端口27018)现在主节点,可以正常写入数据 
myrs:SECONDARY> db.comment.insert({"_id":"2","articleid":"100001","content":"我夏天空腹喝凉开水,冬天喝温开水","userid":"1005","nickname":"伊人憔悴","createdatetime":new Date("2019-08-05T23:58:51.485Z"),"likenum":NumberInt(888),"state":"1"})

5)再启动 27017节点,发现27017变成了从节点,27018仍保持主节点。


# 打开另一个终端(172.18.30.110-测试)

# 切换目录 
cd /usr/local/mongodb/

# 启动从节点 (端口 27018)服务 
root@WIN-20240529BJA:/usr/local/mongodb# ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27017/mongod.conf

6)登录27017节点,发现是从节点了,数据自动从27018同步。从而实现了高可用。


# 打开另一个终端(172.18.30.110-primary)

# 切换目录 
cd /usr/local/mongodb/

# 连接 27017 节点 
root@WIN-20240529BJA:/usr/local/mongodb# ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27017

myrs:PRIMARY> 
myrs:PRIMARY> 
myrs:SECONDARY> 
myrs:SECONDARY> 
myrs:SECONDARY> db.comment.find()
Error: error: {
        "topologyVersion" : {
                "processId" : ObjectId("66aa5b34fb0cb3ff1ec01d75"),
                "counter" : NumberLong(3)
        },
        "ok" : 0,
        "errmsg" : "not master and slaveOk=false",
        "code" : 13435,
        "codeName" : "NotPrimaryNoSecondaryOk",
        "$clusterTime" : {
                "clusterTime" : Timestamp(1722440883, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        },
        "operationTime" : Timestamp(1722440883, 1)
}
myrs:SECONDARY> rs.slaveOk()
WARNING: slaveOk() is deprecated and may be removed in the next major release. Please use secondaryOk() instead.

# 获取数据 db.comment.find() 
myrs:SECONDARY> db.comment.find()
{ "_id" : ObjectId("66aa2581aeb7d05de8a3a30a"), "articleid" : "100000", "content" : "今天天气真好,阳光明媚", "userid" : "1001", "nickname" : "Rose", "createdatetime" : ISODate("2024-07-31T11:52:33.679Z") }
{ "_id" : "1", "articleid" : "100001", "content" : "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。", "userid" : "1002", "nickname" : "相忘于江湖", "createdatetime" : ISODate("2019-08-05T22:08:15.522Z"), "likenum" : 1000, "state" : "1" }
{ "_id" : "2", "articleid" : "100002", "content" : "李连杰动作好帅,李小龙动作好快", "userid" : "1003", "nickname" : "张三丰统一江湖", "createdatetime" : ISODate("2024-07-31T00:00:00Z"), "likenum" : 800, "state" : "1" }
myrs:SECONDARY> 

25-关闭27017主节点.png

四、mongodb 故障测试2

1、仲裁节点和主节点故障

1)先关掉仲裁节点27019,

2)关掉现在的主节点27018

3)登录27017后,发现,27017仍然是从节点,副本集中没有主节点了,导致此时,副本集是只读状态,
无法写入。

4)为啥不选举了?因为27017的票数,没有获得大多数,即没有大于等于2,它只有默认的一票(优先级
是1)

5)如果要触发选举,随便加入一个成员即可。

  • 如果只加入 27019仲裁节点成员,则主节点一定是27017,因为没得选了,仲裁节点不参与选举,
    但参与投票。
  • 如果只加入 27018节点,会发起选举。因为27017和27018都是两票,则按照谁数据新,谁当主节
    点。

2、仲裁节点和从节点故障

1)先关掉仲裁节点27019,

2)关掉现在的副本节点27018

3)10秒后,27017主节点自动降级为副本节点。(服务降级)

4)副本集不可写数据了,已经故障了。

26-mongodb仲裁节点和从节点故障.png

五、mongodb Compass & SpringData 连接副本集

1、先启动3个服务


# 切换目录 
cd /usr/local/mongodb/

# 启动 mongodb 服务 
root@WIN-20240529BJA:/usr/local/mongodb# ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27019/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 2904
child process started successfully, parent exiting

root@WIN-20240529BJA:/usr/local/mongodb# ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27018/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 2978
child process started successfully, parent exiting

# 查看进程 
root@WIN-20240529BJA:/usr/local/mongodb# ps -ef | grep mongod
root      2325  1337  0 Jul31 pts/2    00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27017
root      2333  2216  0 Jul31 pts/4    00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27019
root      2447  2067  0 Jul31 pts/0    00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongo --host=172.18.30.110 --port=27018
root      2559     1  0 Jul31 ?        00:00:18 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27017/mongod.conf
root      2904     1  3 00:26 ?        00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27019/mongod.conf
root      2978     1 11 00:26 ?        00:00:00 ./mongodb-linux-x86_64-ubuntu1804-5.0.28/bin/mongod -f ./replica_sets/myrs_27018/mongod.conf
root      3066  2289  0 00:26 pts/5    00:00:00 grep --color=auto mongod

2、如果使用云服务器需要修改配置中的主节点ip


var config = rs.config();
 config.members[0].host="180.76.159.126:27017";rs.reconfig(config)

3、compass 连接:

27-compass连接-1.png

27-compass连接-2.png

说明:

虽然用 27018 端口,连接的从节点,但也可以进行插入数据的写操作,
因为用 compass 连接的是整个副本集。它会自动切换到 主节点,进行写操作。

4、SpringDataMongoDB 连接副本集

1)副本集语法:

mongodb://host1,host2,host3/articledb?
connect=replicaSet&slaveOk=true&replicaSet=副本集名字

2)其中:

slaveOk=true :开启副本节点读的功能,可实现读写分离。
connect=replicaSet :自动到副本集中选择读写的主机。如果slaveOK是打开的,则实现了读写分
离

3)示例:

连接 replica set 三台服务器 (端口 27017, 27018, 和27019),直接连接第一个服务器,无论是replica
set一部分或者主服务器或者从服务器,写入操作应用在主服务器 并且分布查询到从服务器。


spring:
 #数据源配置
  data:
    mongodb:
      # 主机地址
      # host: 172.18.30.110
      # 数据库
      # database: articledb
      # 默认端口是27017
      # port: 27017
      #也可以使用uri连接
      #uri: mongodb://192.168.40.134:27017/articledb
      # 副本集的连接字符串
      uri:
mongodb://172.18.30.110:27017,172.18.30.110:27018,172.18.30.110:27019/article
db?connect=replicaSet&slaveOk=true&replicaSet=myrs

4)注意:

  • 主机必须是副本集中所有的主机,包括主节点、副本节点、仲裁节点。
  • SpringDataMongoDB自动实现了读写分离:
  • 写操作时,只打开主节点连接:
  • 读操作是,同时打开主节点和从节点连接,但使用从节点获取数据:

5、打开 idea,创建 article-mongodb 的 maven 工程。


	--> idea --> File 
	--> New --> Project 
	--> Maven 
		Project SDK: ( 1.8(java version "1.8.0_131" ) 
	--> Next 
	--> Groupld : ( djh.it )
		Artifactld : ( article-mongodb )
		Version : 1.0-SNAPSHOT
	--> Name: ( article-mongodb )
		Location: ( ...\article-mongodb\ )	
	--> Finish	

6、在工程 article-mongodb (模块)中的 pom.xml 中导入依赖


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>djh.it</groupId>
    <artifactId>article-mongodb</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath></relativePath>
    </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>

</project>
<!-- article-mongodb\pom.xml -->

7、在工程 article-mongodb (模块)中,创建配置文件 application.yml


# article-mongodb\src\main\resources\application.yml

spring:
  # 数据源配置
  data:
    mongodb:
#      # 主机地址
#      host: 172.18.30.110
#      # 数据库
#      database: articledb
#      # 默认端口27017
#      port: 27017
#      # 也可以使用uri连接
#      # uri mongodb //172.18.30.110:27017/articledb
      uri: mongodb://172.18.30.110:27017,172.18.30.110:27018,172.18.30.110:27019/articledb?connect=replicaSet&slaveOk=true&replicaSet=myrs



8、在工程 article-mongodb (模块)中,创建启动类 ArticleApplication.java


/**
 *  article-mongodb\src\main\java\djh\it\article\ArticleApplication.java
 *
 *  2024-7-28 创建启动类 ArticleApplication.java
 */
package djh.it.article;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ArticleApplication {
    public static void main( String[] args ) {
        SpringApplication.run(ArticleApplication.class, args);
    }
}

9、在工程 article-mongodb (模块)中,创建 文章评论实体类 Comment.java

/**
 *   article-mongodb\src\main\java\djh\it\article\pojo\Comment.java
 *
 *   2024-7-28 创建 文章评论实体类 Comment.java
 */
package djh.it.article.pojo;

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 + '\'' +
                '}';
    }
}

10、在工程 article-mongodb (模块)中,创建 dao 接口类 CommentRepository.java

/**
 *  article-mongodb\src\main\java\djh\it\article\dao\CommentRepository.java
 *
 *  2024-7-28 创建 dao 接口类 CommentRepository.java
 */
package djh.it.article.dao;

import djh.it.article.pojo.Comment;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.MongoRepository;

public interface CommentRepository extends MongoRepository<Comment,String > {

    //根据父id,查询子评论的分页列表, 方法名固定,不能随便更改。否则会报错
    Page<Comment> findByParentid(String parentid,Pageable pageable);

}

11、在工程 article-mongodb (模块)中,创建 service 类 CommentService.java

/**
 *  article-mongodb\src\main\java\djh\it\article\service\CommentService.java
 *
 *  2024-7-28 创建 service 类 CommentService.java
 */
package djh.it.article.service;

import djh.it.article.dao.CommentRepository;
import djh.it.article.pojo.Comment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class CommentService {

    @Autowired
    private CommentRepository commentRepository;

    //注入MongoTemplate
    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 保存一个评论
     * @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();
    }

    /**
    * 根据父id查询分页列表,方法名固定,不能随便更改。否则会报错
    * @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就可以了,没必要查询出所有字段修改后再更新所有字
     段。(蝴蝶效应),效率低
     * @param id
     */
    public void updateCommentThumbupToIncrementingOld(String id) {
        Comment comment = commentRepository.findById(id).get();
        comment.setLikenum(comment.getLikenum() + 1);
        commentRepository.save(comment);
    }

        //MongoTemplate实现评论点赞, 点赞数+1,执行效率较高。
    public void updateCommentLikenum(String id){

        //  查询条件
        Query query = Query.query(Criteria.where("_id").is(id));
        //  更新条件
        Update update = new Update();

//        //局部更新,相当于$set
//        update.set(key,value)
//        //递增$inc
//        update.inc("likenum",1);

        update.inc("likenum");
        //参数1:查询对象
        //参数2:更新对象
        //参数3:集合的名字或实体类的类型Comment.class
        mongoTemplate.updateFirst(query,update,Comment.class);
    }
}

12、在工程 article-mongodb (模块)中,创建 测试类 CommentServiceTest.java 进行测试。

/**
 *  article-mongodb\src\test\java\djh\it\article\service\CommentServiceTest.java
 *
 *  2024-7-28 创建 测试类 CommentServiceTest.java
 */
package djh.it.article.service;

import djh.it.article.pojo.Comment;
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.data.domain.Page;
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 testFindCommentById() {
        Comment commentById = commentService.findCommentById("1");
        System.out.println(commentById);
    }

    @Test
    public void testSaveComment(){
        Comment comment=new Comment();
        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);
    }

    //测试根据父id查询子评论的分页列表
    @Test
    public void testFindCommentListByParentid() {
        Page<Comment> page = commentService.findCommentListByParentid("3", 1, 2);
        System.out.println("======总记录数: "+page.getTotalElements());
        System.out.println("======当前页数据: "+page.getContent());
    }

    //点赞测试1:方法简单,执行效率低
    @Test
    public void testUpdateCommentThumbupToIncrementingOld(){
        commentService.updateCommentThumbupToIncrementingOld("1");
    }

    //点赞测试2:方法稍复杂,执行效率高
    @Test
    public void testUpdateCommentLikenum() {
        commentService.updateCommentLikenum("1");
    }
}

上一节关联文档请点击
# mongodb_基础到进阶 – MongoDB 高级–MongoDB 集群部署与安全性(一)

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐