【mongo事务】使用docker-compose启动mongo,‘单副本模式‘实现支持事务

想要 mongo 支持事务的首要条件是 mongo 版本 4.x 以上，且为复制集模式。由于很多时候使用 mongo 都不需要部署多副本，但是想支持事务，所以可以使用‘单副本模式’，既能保证 mongo 实例只有一个，又是复制集模式。
本文使用 mongo5.0.8 作为样例。

本文只是日常遇到问题的小记，如有错误，欢迎指出。

首先给出 docker-compose.yml

Copy
version: '3.0'
services:
mongo:
image: mongo:5.0.8
restart: unless-stopped
container_name: mongodb
command: --replSet rs0 --bind_ip_all --keyFile /data/mongodb/keyFile
environment:
TZ: 'Asia/Shanghai'
#用户名密码
MONGO_INITDB_ROOT_USERNAME: 'admin'
MONGO_INITDB_ROOT_PASSWORD: 'password'
ports:
- 27017:27017
volumes:
- ./mongodb/data:/data/db
- ./mongodb/keyFile:/data/mongodb/keyFile

准备 keyFile

本人粗略测试在 4.x 版本不需要使用 keyFile, 但是在 5.x 版本是必须要 KeyFile 的，不然会报 “BadValue: security.keyFile is required when authorization is enabled with replica sets”

生成 keyFile

Copy
openssl rand -base64 128 > ./mongodb/keyFile

其中 ./mongodb/keyFile 是指定生成文件的名字以及在哪一个文件夹下

设置权限以及所属用户

keyFile 文件的权限必须为 600, 如果权限太大，启动时会报 “error opening file: /data/mongodb/keyFile: bad file”

当权限改为 600 以后还需要把 keyFile 文件的所属用户和用户组改为 mongodb 不然在启动时会报 "permissions on /data/mongodb/keyFile are too open"

由于使用容器启动所以需要把 keyFile 文件的所属用户和用户组 改为 999，这样容器会自动把 keyFile 文件的所属用户和用户组改为 mongodb。

Copy
sudo chmod 600 keyFile
sudo chown 999:999 keyFile

容器外

容器里

当 keyFile 文件准备好以后，就可以根据上面的 docker-compose.yml 启动容器。

初始化

容器启动后这时 mongo 还不能使用，还需要进入容器内初始化复制集。
进入容器

Copy
docker exec -it mongodb /bin/bash

用刚刚设置的用户名密码进入 mongo

Copy
mongo -u admin --authenticationDatabase admin

执行初始化

Copy
rs.initiate()

显示一下就说明成功了

到这 mongo 就算是启动完成了。

spring boot 项目配置

要想实现事务还需要在项目中进行配置

spring boot 版本： 2.1.6.RELEASE

Copy
@Configuration
@Slf4j
public class MongoTransactionConfig {
@Bean
MongoTransactionManager transactionManager(MongoDbFactory factory){
log.warn("开启mongo事务");
return new MongoTransactionManager(factory);
}
}

在 spring boot 高版本中 MongoDbFactory 被弃用需要换成 MongoDatabaseFactory

spring boot 版本：2.6.1

Copy
@Configuration
@Slf4j
public class MongoTransactionConfig {
@Bean
MongoTransactionManager transactionManager(MongoDatabaseFactory factory){
log.warn("开启mongo事务");
return new MongoTransactionManager(factory);
}
}

添加配置类以后只需要在方法上加入 @Transactional 注解就可以实现 mongo 事务了。

！！！新增！！！
很多时候我们会保留挂载出来的数据，重新构建容器，由于 docker 在每次构建新容器时都会为容器随机分配一个 hostname。但因为我们还是使用上一个容器挂载出来的数据，这就导致 mongo 副本集配置的 hostname 还是上一个容器，这就导致新启动的 mongo 不能用。因此我们只需要改变 mongo 副本集配置的 hostname，就能解决这个问题

当我们启动 mongo 后直接连接会报这个错。

我们首先进入容器，查看当前容器的 hostname

使用上面提到的方式，使用 admin 账号登陆 mongo，查看 config

Copy
rs.config()

正如上述所说 config 里的 hostname 与当前容器的不一样

方法 1：更改 hostname

1. 获取副本集配置

Copy
config=rs.conf()

2. 可以根据上面配置 json 的格式可以知道 hostname 的位置，从而对他进行重新赋值（将 host 改为当前容器的 host）

Copy
config.members[0].host="c3e9261f8a04:27017"

3. 最后更新 config

Copy
rs.reconfig(config, {force : true})

改完以后 mongo 就能正常启动了

方法 2：在 yaml 中指定 hostname

Copy
version: '3.0'
services:
mongo:
hostname: mongo501
image: mongo:5.0.8
restart: unless-stopped
container_name: mongodb
command: --replSet rs0 --bind_ip_all --keyFile /data/mongodb/keyFile
environment:
TZ: 'Asia/Shanghai'
#用户名密码
MONGO_INITDB_ROOT_USERNAME: 'admin'
MONGO_INITDB_ROOT_PASSWORD: 'password'
ports:
- 27017:27017
volumes:
- ./mongodb/data:/data/db
- ./mongodb/keyFile:/data/mongodb/keyFile

这样的话每次新建的容器的 hostname 都是同一个了。