Mongo副本集和PBM备份
Mongo副本集和PBM备份
数据库同步的备份场景主要有:
- 实时同步,主从同步,以实现读写分离和实时切换主从,提供性能和高可用
- 历史版本留存,将数据库的写操作延迟后写入延迟数据库,以达到恢复到可用时间点的效果
- 分片集群,用于快速扩容,适用于大规模数据。
数据库备份层面:
- 逻辑备份
- 物理备份
- 增量备份
- 快照备份
Mongo副本集实现主从同步
简单介绍:
实现效果:只需一个主节点、一个从节点、一个从节点PSS(或者一个仲裁节点PSA),在进行配置成功后,主节点若在宕机的情况下,一个从节点将会被选举成为新的主节点,从而继续工作。
仲裁节点的用处是添加一个票数,因为Mongo选举需要有2/3的票数,且需保证存活的结点是2/3,否则将复制集将不可用。
SpringBoot配置文件配置:spring.data.mongodb.uri=mongodb://admin:123456@172.16.60.1:27019,172.16.60.2:27019,172.16.60.3:27020/aaim-backend?authSource=admin&connect=replicaSet&readPreference=secondaryPreferred&replicaSet=rs0
第一步:安装docker和上传相关镜像(镜像包和docker安装包可从129服务器/home/uvi/docker目录获取)
#(@@)安装docker略
# 将和原服务器版本相同的mongo镜像包上传至/home/uvi/software下
mkdir -p /home/uvi/docker-image
cd /home/uvi/docker-image
#(@@)使用ssh工具或者跳板机上传文件
#将包转换为docker镜像
docker load -i xxx.tar
#查看镜像
docker images
第二步:生成相关前提文件和开启防火墙
#创建mongo挂在目录
mkdir -p /data/mongo-secondary
mkdir -p /data/mongo-arbiter
#(@@)从原服务器上复制一份密码的文件过来
#生成密钥文件
openssl rand -base64 741 > mongodb-keyfile
chmod 600 mongodb-keyfile
#(@@)创建的密钥文件拷贝到其他各节点的相同路径下,mongo5替换成对应mongo仓库的名字
mv mongodb-keyfile /data/mongo-secondary/keyfile/
mv mongodb-keyfile /data/mongo-arbiter/keyfile/
#开启防火墙 (Ubuntu使用ufw,Centos使用firewalld)
ufw allow 27017/tcp
ufw reload
ufw status verbose | grep 27017 查看端口开放情况
或
firewall-cmd --zone=public --add-port=27017/tcp --permanent
firewall-cmd --reload
firewall-cmd --list-ports | grep 27017
第三步:备份服务器docker-compose配置从节点和仲裁节点(主节点添加command和network_mode和 - /data/mongo-secondary/keyfile这一行)
#从原mongo的docker配置出粘贴一份docker-compose.yaml文件到新服务器处
#例如
version: '3'
services:
mongo-secondary:
image: mongo:5.0.21
restart: always
container_name: mongo-secondary
ports:
- "27019:27017" #端口自由配置
environment:
- MONGO_INITDB_ROOT_USERNAME=admin
- MONGO_INITDB_ROOT_PASSWORD_FILE=/run/secrets/mongo_password
- TZ=Asia/Shanghai
command: mongod --keyFile /opt/keyfile/mongodb-keyfile --bind_ip_all --replSet rs0
# Mount database file
volumes:
- /data/mongo-secondary:/data/db
- /data/mongo-secondary/keyfile:/opt/keyfile
logging:
driver: json-file
options:
max-size: 100m
max-file: '5'
network_mode: bridge
secrets:
- mongo_password
mongo-arbiter:
image: mongo:5.0.21
restart: always
container_name: mongo-arbiter
ports:
- "27020:27017" #端口自由配置
environment:
- MONGO_INITDB_ROOT_USERNAME=admin
- MONGO_INITDB_ROOT_PASSWORD_FILE=/run/secrets/mongo_password
- TZ=Asia/Shanghai
command: mongod --keyFile /opt/keyfile/mongodb-keyfile --bind_ip_all --replSet rs0
# Mount database file
volumes:
- /data/mongo-arbiter:/data/db
- /data/mongo-arbiter/keyfile:/opt/keyfile
network_mode: bridge
logging:
driver: json-file
options:
max-size: 100m
max-file: '5'
secrets:
- mongo_password
# MONGODB PASSWORD CONFIG
secrets:
mongo_password:
file: ./mongo_password.txt
第四步:重启Mongo并配置副本集
配置前重要数据的备份:cp -r /{各医院mongo容器对应的数据挂载目录} /{各医院mongo容器对应的数据挂载目录的父目录}/bak1/
数据恢复:
mv /{各医院mongo容器对应的数据挂载目录} /{各医院mongo容器对应的数据挂载目录的父目录}/bak2/
mv /{各医院mongo容器对应的数据挂载目录} /{各医院mongo容器对应的数据挂载目录的父目录}/bak1/
mv /{各医院mongo容器对应的数据挂载目录的父目录}/bak1/ /{各医院mongo容器对应的数据挂载目录}
副本集初始化没问题则删除多余备份文件:
rm -rf /{各医院mongo容器对应的数据挂载目录的父目录}/bak1/
恢复没问题则删除多余备份文件:
rm -rf /{各医院mongo容器对应的数据挂载目录的父目录}/bak2/
rm -rf /{各医院mongo容器对应的数据挂载目录的父目录}/bak1/
#启动从节点和仲裁节点
docker compose up -d mongo-secondary
docker compose up -d mongo-arbiter
#在从节点和仲裁节点以及主节点上互相使用(包括自己ping自己,因为可能会出现容器内无法访问宿主机的问题)下面的命令进行测试连接
#(!!!)在重启主节点前务必先检验在各个结点上能否连接通其余结点,否则重启后,复制集不生效,甚至原库也不能访问
docker exec mongo-secondary mongosh -u admin -p 123456 --eval 'db.runCommand({ ping: 1 })' "mongodb://{ip}:{port}"
#重启容器(旧版本的docker compose使用docker-compose,新的使用docker compose)
docker rm -f mongo5
docker compose up -d mongo5
#进入主节点(原数据库)容器内数据库
docker exec -it mongo5 mongosh admin
>db.auth("admin", "123456")
#执行副本集初始化,一定要等待其初始化完毕
>rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "172.16.60.1:27019", priority: 2 },
{ _id: 1, host: "172.16.60.2:27019", priority: 1 },
{ _id: 2, host: "172.16.60.3:27020", priority: 0, arbiterOnly: true }
]
})
#初始化完毕后查看副本集状态
>rs.status()
#当看到主库变为PRIMARY状态时即初始化副本集成功,从节点状态为STARTUP或STARTUP2状态时,表示该从节点正在同步主库,可能需要半小时到1小时的时间,才会变为SECONDARY状态
第五步:重启后端服务
docker restart xxx-backend
附:意外情况处理
#当备份服务器宕机后,两个节点都会挂掉,此时复制集和主节点也不可用
#恢复办法是去掉command这一行,重新启动mongo,让其以单节点方式启动。
command: mongod --keyFile /opt/keyfile/mongodb-keyfile --bind_ip_all --replSet rs0
PBM实现历史版本留存
第一步:在备份服务器上传相关镜像并生成相关文件
#(@@)将镜像包上传至/home/uvi/software
#将包转换为docker镜像
docker load -i xxx.tar
#创建备份文件目录和日志目录
mkdir -p /data/backups/data
mkdir /data/backups/logs
chmod -R 777 /data/backups/data
chmod -R 777 /data/backups/logs
#这里有点迷(建议先跳过,后面权限不够再来),0是root用户,不授权也成功了,999是docker组Id,下面这个授权也不对,权限不足的时候再试试其他授权,或者把目录删了,重新建
chown -R 0:0 /data/backups/data
chown -R 1000:999 /data/backups/logs
第二步:创建用于备份和恢复的用户
>db.getSiblingDB("admin").createRole({ "role": "pbmadminRole",
"privileges": [
{ "resource": { "anyResource": true },
"actions": [ "anyAction" ]
}
],
"roles": []
})
>db.getSiblingDB("admin").createUser({user: "pbmadmin",
"pwd": "uvi_admin",
"roles" : [
{ "db" : "admin", "role" : "readWrite", "collection": "" },
{ "db" : "admin", "role" : "backup" },
{ "db" : "admin", "role" : "clusterMonitor" },
{ "db" : "admin", "role" : "restore" },
{ "db" : "admin", "role" : "pbmadminRole" }
]
});
第三步:docker-compose添加pbm容器配置(一个结点需要配备一个pbm,最好分别在两台服务器上部署一个pbm,分开的话secondary就去掉,都叫pbm了)
-secondary pbm:
image: percona/percona-backup-mongodb:latest
container_name: pbm
restart: always
command: pbm-agent -f /etc/pbm-config.yaml
environment:
- PBM_MONGODB_URI=mongodb://pbmadmin:123456@172.16.60.1:27019/?authSource=admin&replicaSet=rs0&readConcernLevel=local&w=1
volumes:
- /data/backups/data:/backups # 持久化备份文件
- /data/logs:/logs
- /home/uvi/db-deploy/pbm-config.yaml:/etc/pbm-config.yaml # 配置文件
network_mode: bridge
pbm-secondary:
image: percona/percona-backup-mongodb:latest
container_name: pbm-secondary
restart: always
command: pbm-agent -f /etc/pbm-secondary-config.yaml
environment:
- PBM_MONGODB_URI=mongodb://pbmadmin:123456@172.16.60.1:27019/?authSource=admin&replicaSet=rs0&readConcernLevel=local&w=1
volumes:
- /data/backups/data:/backups # 持久化备份文件
- /data/backups/logs:/logs
- /home/uvi/db-deploy/pbm-secondary-config.yaml:/etc/pbm-secondary-config.yaml # 配置文件
network_mode: bridge
第四步:配置pbm-agent
mongodb-uri: mongodb://pbmadmin:123456@172.16.60.1:27019/?authSource=admin&replicaSet=rs0&readConcernLevel=local&w=1
storage:
type: filesystem
filesystem:
path: /backups
backup:
priority: #选择备份结点的优先级
- "172.16.60.1:27019": "2"
- "172.16.60.2:27020": "1"
- "172.16.60.3:27020": "0"
# compression: s2 # 备份压缩类型,s2 是默认的压缩方式
timeouts:
startingStatus: 30 #半分钟的等待备份时间,默认33秒
oplogSpanMin: 10 #备份的oplog切片大小,默认10,若高负载情况下,可适当减小该值,以减轻DB的负载,但这会总在备份
# numParallelCollections: #并行的集合数,默认是CPU核心的一半
pitr: #时间点恢复
enabled: true
oplogSpanMin: 1 #增量时间,默认10分钟
# compression: s2 #默认s2,速度快压缩少,gzip速度慢,压缩大,snappy,lz4,pgzip,zstd
# compressionLevel: 1 压缩等级
oplogOnly: true #是否启动只保存opLog,而不完全base backup(完全备份)
priority: #拉取opLog结点的优先级
- "172.16.60.1:27019": "2"
- "172.16.60.2:27020": "1"
- "172.16.60.3:27020": "0"
#restore:
# batchSize: 500 #缓存的文档大小,默认500,默入工作者的数量
# numParallelCollections: #并行的集合数,默认是CPU核心的一半
# numDownloadWorkers: # 存储块请求数据的工作者数量,默认CPU的核心数
# maxDownloadBufferMb: #请求备份数据的缓存大小,默认 numDownloadWorkers * downloadChunkMb * 16
# downloadChunkMb: 32 # 请求备份数据块的大小
# numInsertionWorkers: 10 #每个集合并发的
# mongodLocation: #物理恢复时使用的 mongod 二进制文件的路径
# mongodLocationMap: mongo在每个结点上的mongod路径
log:
path: "/logs/pbm.json"
level: "I"
json: true
mongodb-uri: mongodb://pbmadmin:123456@172.16.60.1:27019/?authSource=admin&replicaSet=rs0&readConcernLevel=local&w=1
storage:
type: filesystem
filesystem:
path: /backups
backup:
priority: #选择备份结点的优先级
- "172.16.60.1:27019": "2"
- "172.16.60.2:27020": "1"
- "172.16.60.3:27020": "0"
# compression: s2 # 备份压缩类型,s2 是默认的压缩方式
timeouts:
startingStatus: 30 #半分钟的等待备份时间,默认33秒
oplogSpanMin: 10 #备份的oplog切片大小,默认10,若高负载情况下,可适当减小该值,以减轻DB的负载,但这会总在备份
# numParallelCollections: #并行的集合数,默认是CPU核心的一半
pitr: #时间点恢复
enabled: true
oplogSpanMin: 1 #增量时间,默认10分钟
# compression: s2 #默认s2,速度快压缩少,gzip速度慢,压缩大,snappy,lz4,pgzip,zstd
# compressionLevel: 1 压缩等级
oplogOnly: true #是否启动只保存opLog,而不完全base backup(完全备份)
priority: #拉取opLog结点的优先级
- "172.16.60.1:27019": "2"
- "172.16.60.2:27020": "1"
- "172.16.60.3:27020": "0"
#restore:
# batchSize: 500 #缓存的文档大小,默认500,默入工作者的数量
# numParallelCollections: #并行的集合数,默认是CPU核心的一半
# numDownloadWorkers: # 存储块请求数据的工作者数量,默认CPU的核心数
# maxDownloadBufferMb: #请求备份数据的缓存大小,默认 numDownloadWorkers * downloadChunkMb * 16
# downloadChunkMb: 32 # 请求备份数据块的大小
# numInsertionWorkers: 10 #每个集合并发的
# mongodLocation: #物理恢复时使用的 mongod 二进制文件的路径
# mongodLocationMap: mongo在每个结点上的mongod路径
log:
path: "/logs/pbm.json"
level: "I"
json: true
第五步:启动pbm
#启动pbm
docker compose up -d pbm pbm-secondary
#查看pbm是否正常启动,如果没出现restarting的状态说明启动成功
docker ps | grep pbm
#若未正常启动,可查看日志
docker logs pbm --tail 10
#启动成功后,可查看pbm状态
docker exec pbm pbm status
#进行一次全量备份(逻辑备份,物理备份只支持Percona Server for Mongodb,Mongo社区版和企业版均不支持)
docker exec pbm pbm backup
#查看pbm日志
tail -f /data/backup/logs/pbm.json
第六步:PBM单体不提供自动备份功能,可通过crond脚本之类的或者Percona Operator for MongoDB.
由于Percon Operator for MongoDB需要Kubernates环境,所以建议使用crond调度(只需在一台服务器上就可以了)
# 创建调度文件
touch /home/uvi/db-deploy/backup.sh
# 编辑脚本内容
# 授权
chmod +x /home/uvi/db-deploy/backup.sh
# 创建调度任务
crontab -e
#输入调度时间和调度脚本,每两天执行一次
0 3 */2 * * /home/uvi/db-deploy/backup.sh
#所有工作完成
#!/bin/bash
# 设置日志文件路径
LOG_FILE="/data/backups/auto_backup.log"
# 获取当前日期,格式为 yyyy-mm-dd
CURRENT_DATE=$(date +%F)
# 获取当前时间,格式为 yyyy-mm-dd HH:MM:SS
CURRENT_TIME=$(date +"%Y-%m-%d %H:%M:%S")
# 将执行时间写入日志
echo "[$CURRENT_TIME] 开始执行pbm全量备份..." >> $LOG_FILE
# 进行pbm全量备份
echo "[$CURRENT_TIME] 执行pbm全量备份" >> $LOG_FILE
docker exec pbm pbm backup --wait >> $LOG_FILE 2>&1
# 记录备份完成的时间
CURRENT_TIME=$(date +"%Y-%m-%d %H:%M:%S")
echo "[$CURRENT_TIME] pbm全量备份完成" >> $LOG_FILE
# 清理超过5天的备份
echo "[$CURRENT_TIME] 开始执行pbm清理,删除超过5天的备份..." >> $LOG_FILE
docker exec pbm pbm cleanup --older-than=5d -y --wait >> $LOG_FILE 2>&1
# 记录清理完成的时间
CURRENT_TIME=$(date +"%Y-%m-%d %H:%M:%S")
echo "[$CURRENT_TIME] 过时备份清理完成" >> $LOG_FILE
# 完成日志
echo "[$CURRENT_TIME] 备份和清理过程完成" >> $LOG_FILE
读写分离
介绍:SpringData的框架自动实现了读写分离。
注意:主库到从库的同步会有延迟,在手麻系统中,应该尽量避免读到过时数据,mongo提供了写操作的相关设置:
通过writeConcern指定REPLICA_ACKNOWLEDGED(2,2000),2台节点全部写确认成功后才返回,设置了2秒的等待超时,可以根据复制集群数来指定节点数和等待超时时间。
相关命令
- 4.4.22版本的登录mongo使用docker exec -it mongo mongo admin,5.0.21版本使用docker exec -it mongo mongosh admin
- pbm list 查看备份历史列表
- pbm backup 手动备份(无选项默认逻辑备份)
-t physical 物理全备
-t logical 逻辑备份
-t incremental --base 增量备份
- pbm cleanup 删除过时备份
--older-than=TIMESTAMP 删除早于这个时间的
-w 阻塞命令行直到完成
-y 不用询问
- pbm delete-backup 删除指定备份(用法跟pbm cleanup 差不多,多一个--type指定类型)
- pbm delete-pitr 删除时间点恢复
-a 删除所有
--older-than=TIMESTAMP 删除早于这个时间的
- pbm restore 恢复数据
--external 恢复其他的pbm的备份数据
--time=TIME 恢复数据到指定时间点
-w 阻塞等待完成
--wait-time 等待恢复的时间
--base-snapshot 指定基准备份,新版本pbm如果需要从物理方式恢复备份,则需要指定这个选项,否则会默认从最近的一个备份恢复
--replset-remapping 当恢复时,如果想要改变副本集的名称,或者从不同的副本集恢复数据时,可以使用此选项进行映射。
--ns=<database.collection> 恢复备份时,你可以选择只恢复某些特定的数据库或集合。
--with-users-and-roles 恢复用户和角色
-c 指定mongod.fonf路径
--ns-from="database.collection" 恢复集合原始名称
--ns-to="database.collection" 恢复集合的新名称
意外情况
- 启动pbm时日志产生如下报错
解决方法:降版本至2.8版本,将pbm-config.yaml删减为以下内容,docker-compose.yml修改为以下内容
storage:
type: filesystem
filesystem:
path: /backups
pitr: #时间点恢复
enabled: true
oplogSpanMin: 1 #增量时间,默认10分钟
oplogOnly: true #是否启动只保存opLog,而不完全base backup(完全备份)
version: '3'
services:
pbm:
image: percona/percona-backup-mongodb:2.8.0
container_name: pbm
restart: always
command:
- /bin/bash
- -c
- |
pbm config --file=/etc/pbm-config.yaml
pbm-agent
environment:
- PBM_MONGODB_URI=mongodb://pbmadmin:uvi_admin@172.17.249.124:27017/?authSource=admin&replicaSet=rs0&readConcernLevel=local&w=1
- LOG_LEVEL=I
- LOG_JSON=1
- LOG_PATH=/logs/pbm.json
volumes:
- /data/backups/data:/backups # 持久化备份文件
- /data/backups/logs:/logs
- /home/uvi/db-deploy/pbm-config.yaml:/etc/pbm-config.yaml # 配置文件
network_mode: bridge
验证主从复制
第一步:对比数据库统计信息。分别在主节点和从节点上连接到要查看的数据库后,执行db.stats()命令,它会返回当前数据库的详细统计信息,包括数据库的大小(以字节为单位),对比主节点和从节点的数据是否一致。
#选择数据库
use uc-dayuan
#返回当前数据库的详细统计信息
db.stats()
这个 db.stats() 命令的输出结果提供了关于 uc-dayuan 数据库的很多有用信息,以下是对各字段的详细解释:
db:表示当前操作的数据库名称,这里是 "uc-dayuan"。
collections:数据库中集合的数量,这里有 28 个集合。
views:数据库中视图的数量,此数据库中视图数量为 0。
objects:数据库中所有集合中文档的总数,这里是 242258 个文档。
avgObjSize:平均每个文档的大小(以字节为单位),这里平均每个文档大小约为 14683.216144 字节。
dataSize:数据库中所有文档占用的数据总大小(以字节为单位),这里是 3555658255 字节。
storageSize:数据库在磁盘上占用的存储总大小(以字节为单位),为 902742016 字节。
indexes:数据库中所有集合上索引的总数,这里有 28 个索引。
indexSize:所有索引占用的总大小(以字节为单位),是 6193152 字节。
totalSize:数据库的总大小,包括数据和索引,这里是 908935168 字节。
scaleFactor:存储引擎使用的比例因子,这里是 1。
fsUsedSize:数据库所在文件系统已使用的大小(以字节为单位),这里是 79447347200 字节。
第二步:抽样对比数据。如使用MongoDB Compass连接工具进行界面操作抽样对比数据,
1、抽样检查主节点与从节点集合中数据一致性(主要针对从节点第一次全量拷贝主节点数据时是否保证数据一致),设置抽样条件时,要确保抽取的数据能够代表整个数据集的特征,以提高发现潜在数据不一致问题的概率。
2、数据差异情况:
- 新增数据:在对比结果中,若某行数据仅在主节点存在,而从节点不存在,该行会标记为 “Inserted in source”。
- 删除数据:若某行数据仅在从节点存在,而主节点不存在,该行会标记为 “Deleted in source”。
- 数据修改:若某行数据在主从节点都存在,但字段值不同,该行会标记为 “Updated in source”,并详细显示字段的新旧值对比。

浙公网安备 33010602011771号