想要mongo支持事务的首要条件是mongo版本4.x 以上,且为复制集模式。由于很多时候使用mongo都不需要部署多副本,但是想支持事务,所以可以使用‘单副本模式’,既能保证mongo实例只有一个,又是复制集模式。 本文使用mongo5.0.8作为样例。
本文只是日常遇到问题的小记,如有错误,欢迎指出。
首先给出docker-compose.yml
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
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。
sudo chmod 600 keyFile
sudo chown 999:999 keyFile
容器外
容器里
当keyFile文件准备好以后,就可以根据上面的docker-compose.yml启动容器。
初始化
容器启动后这时mongo还不能使用,还需要进入容器内初始化复制集。 进入容器
docker exec -it mongodb /bin/bash
用刚刚设置的用户名密码进入mongo
mongo -u admin --authenticationDatabase admin
执行初始化
rs.initiate()
显示一下就说明成功了
到这mongo就算是启动完成了。
spring boot项目配置
要想实现事务还需要在项目中进行配置
spring boot 版本: 2.1.6.RELEASE
@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
@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
rs.config()
正如上述所说config里的hostname与当前容器的不一样
方法1:更改hostname
1.获取副本集配置
config=rs.conf()
2.可以根据上面配置json的格式可以知道hostname的位置,从而对他进行重新赋值(将host改为当前容器的host)
config.members[0].host="c3e9261f8a04:27017"
3.最后更新config
rs.reconfig(config, {force : true})
改完以后mongo就能正常启动了
方法2:在yaml中指定hostname
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都是同一个了。