YAOTU INSIGHTS

Linux下MongoDB定时备份方案:mongodump与crontab实战

Linux下MongoDB定时备份方案:mongodump与crontab实战
数据这玩意儿平时躺在库里岁月静好一旦出问题就是事故现场。我见过太多团队在业务跑得正欢的时候压根没想过备份这回事等到某天误删了一个集合、磁盘写满导致数据文件损坏或者一次不小心的批量更新把关键字段全刷成了同一个值才开始满世界找恢复手段。所以这篇内容我想聊的是在Linux环境下怎么给MongoDB搭一套靠谱的定时备份方案做到数据自动落盘、按天归档、出问题能快速回滚。核心关键词就是MongoDB备份、mongodump、crontab定时任务、数据恢复这几个东西串起来就是一套完整的兜底体系。不管你是刚接手一台跑着MongoDB的服务器的新手运维还是自己写小项目需要给数据上保险的开发者这套方案都能直接抄作业配置量不大但覆盖了从备份脚本编写、定时调度、日志记录到恢复验证的完整链路。1. 方案整体设计与选型思路1.1 为什么选mongodump而不是直接拷数据文件给MongoDB做备份摆在面前的路其实有好几条直接停库拷贝数据目录、用文件系统快照、用mongodump逻辑导出、用mongorestore配合副本集做热备。我先把选型逻辑讲清楚因为选错了后面全是坑。直接拷贝数据目录比如把 /var/lib/mongodb 整个打包是最粗暴的方式但它要求数据库处于停止状态或者至少是干净关闭状态否则拷出来的文件可能处于不一致状态恢复时直接报错。生产环境不可能为了备份停库所以这条路基本排除。文件系统快照LVM快照、云盘快照速度快、对业务影响小但它依赖底层存储支持而且快照恢复是整盘级别的粒度太粗你想单独恢复某个集合就做不到。mongodump 是MongoDB官方自带的逻辑备份工具它把数据导出成BSON格式文件配合mongorestore可以精确恢复到集合级别甚至文档级别。它的优势在于不停库、粒度细、跨版本兼容性好、备份文件可读可迁移。缺点是数据量大时导出慢、对数据库有一定读压力。但对于绝大多数中小规模业务数据量在几十GB以内mongodump完全够用而且它是官方工具随MongoDB安装包一起提供不需要额外装东西。提示如果你的单库数据量超过几百GBmongodump的全量导出时间会很长这时候要考虑增量备份方案或者副本集从节点备份把读压力转移到从库上。本文方案针对的是常规规模场景。所以整体设计思路就定了用 mongodump 做逻辑备份用 shell 脚本封装备份逻辑包括目录创建、文件压缩、过期清理、日志记录用 crontab 做定时调度。这套组合的特点是零外部依赖、纯系统自带能力、脚本可读可改出问题容易排查。1.2 备份策略的几个关键决策方案里有两个绕不开的决策点我逐个说。第一个是全量还是增量。mongodump 本身只支持全量导出它没有原生的增量备份能力所以定时任务跑的都是全量。全量备份的好处是恢复简单——拿到最新一份备份直接还原就行不依赖备份链。坏处是占空间、耗时间。我的处理办法是每天全量备份一次保留最近N天超期自动删除。对于数据量不大的场景每天全量完全可接受数据量大的话可以调整为每周全量每天只备份关键集合或者把备份频率降到每天一次但错开业务高峰。第二个是备份文件存哪里。最省事的做法是存在本机另一个目录但这有个致命问题如果整台机器挂了或者磁盘坏了备份和数据一起没。所以稳妥的做法是备份到独立挂载的磁盘或者远程存储比如挂载的NFS、对象存储。本文脚本会预留一个备份根目录变量你可以指向本地独立分区也可以指向挂载的远程路径脚本本身不关心存储介质只负责往指定目录写。第三个是要不要压缩。mongodump 支持--gzip参数直接输出压缩文件能显著减小体积BSON文本压缩率通常不错。我建议开启代价是导出时多一点点CPU开销但省下的磁盘空间和传输时间很值。脚本里我会用 gzip 压缩整个备份目录这样一份备份就是一个 tar.gz 包管理起来清爽。2. 环境准备与核心工具确认2.1 确认MongoDB工具链是否齐全动手写脚本之前先确认服务器上 mongodump 和 mongorestore 这两个命令能不能用。它们通常随MongoDB服务端一起安装但有些精简安装或者容器环境里可能只有 mongod 没有工具集。which mongodump which mongorestore mongodump --version如果which找不到说明工具没装。不同发行版的安装方式不一样以常见的包管理为例MongoDB官方源里的mongodb-org-tools包就包含这些工具。装好之后再验证一次版本注意 mongodump 的版本最好和服务端版本接近跨大版本导出可能有兼容性问题。注意mongodump 的版本不要低于服务端太多。比如服务端是6.x工具是4.x导出时可能遇到认证机制或BSON格式不兼容。版本对齐是最省心的做法。2.2 准备备份目录与权限备份要写文件就得有目录和权限。我习惯把备份统一放在/data/backup/mongodb这种独立路径下方便管理和挂载独立磁盘。创建目录并确认运行备份的用户有写权限mkdir -p /data/backup/mongodb chown -R $(whoami):$(whoami) /data/backup/mongodb这里有个细节crontab 执行任务时的环境和你在终端里手动执行不一样PATH 可能不完整用户身份也可能不同。所以脚本里所有命令尽量用绝对路径或者在脚本开头显式设置 PATH。这一点后面写脚本时会重点处理是新手最容易踩的坑之一。2.3 数据库连接信息的准备mongodump 连接数据库需要知道地址、端口、认证信息。如果数据库开了认证生产环境基本都开需要提供用户名密码和认证库。这里有个安全考量密码不要明文写在脚本里最好放在一个权限受限的配置文件里脚本去读取。我通常建一个/etc/mongodb-backup.cnf这样的配置文件权限设成 600内容大致是host127.0.0.1 port27017 usernamebackup_user password你的密码 authenticationDatabaseadmin然后脚本里用--config参数引用它。这样脚本本身可以随便看密码不会泄露。备份用的账号建议单独建一个只有backup和read角色的用户最小权限原则别拿管理员账号跑备份。3. 备份脚本的完整实现与逐段解析3.1 脚本骨架与变量定义先看整体结构。脚本我命名为mongo_backup.sh放在/usr/local/bin/下方便调用。开头定义一组变量把路径、保留天数、日志位置都集中管理改配置只改这一块。#!/bin/bash # 基础配置 BACKUP_ROOT/data/backup/mongodb DATE$(date %Y%m%d_%H%M%S) BACKUP_DIR${BACKUP_ROOT}/${DATE} LOG_FILE${BACKUP_ROOT}/backup.log KEEP_DAYS7 MONGO_CONFIG/etc/mongodb-backup.cnf # 确保PATH完整crontab环境下很关键 export PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binDATE用精确到秒的时间戳做目录名好处是同一台机器上多次备份不会互相覆盖也方便按时间排序找最新备份。KEEP_DAYS7表示保留最近7天的备份这个值按你的磁盘容量和恢复需求调整。export PATH那行是血泪教训——crontab 默认的 PATH 往往很短不显式设置的话脚本里调用的命令可能找不到。3.2 备份执行与错误处理核心备份动作就一行 mongodump但外面要包一层错误判断备份失败得能感知到。echo [$(date %Y-%m-%d %H:%M:%S)] 开始备份 ${LOG_FILE} mkdir -p ${BACKUP_DIR} mongodump --config${MONGO_CONFIG} \ --out${BACKUP_DIR} \ --gzip \ --quiet ${LOG_FILE} 21 if [ $? -ne 0 ]; then echo [$(date %Y-%m-%d %H:%M:%S)] 备份失败请检查 ${LOG_FILE} exit 1 fi echo [$(date %Y-%m-%d %H:%M:%S)] 备份完成: ${BACKUP_DIR} ${LOG_FILE}这里几个参数值得展开说。--out指定输出目录mongodump 会在里面按数据库名建子目录每个集合导出成.bson.gz文件。--gzip开启压缩前面说过能省空间。--quiet减少冗余输出日志干净些。$?判断上一条命令的退出码非0说明备份出错直接退出并记录避免后续压缩一个残缺的备份。提示如果只想备份特定数据库加--db库名只想备份特定集合加--collection集合名。全库备份不加这些参数即可。3.3 打包压缩与过期清理备份目录里是一堆散文件打包成一个 tar.gz 更利于归档和传输。cd ${BACKUP_ROOT} tar -czf ${DATE}.tar.gz ${DATE} rm -rf ${DATE} # 清理过期备份 find ${BACKUP_ROOT} -name *.tar.gz -type f -mtime ${KEEP_DAYS} -delete echo [$(date %Y-%m-%d %H:%M:%S)] 已清理${KEEP_DAYS}天前的备份 ${LOG_FILE}打包成功后删掉原始目录避免磁盘上同时存在散文件和压缩包占双份空间。find配合-mtime 7删除7天前的文件-delete直接删。这里要注意-mtime的语义是“修改时间在N天以前”7表示超过7天正好符合保留7天的需求。3.4 脚本完整版与执行权限把上面几段拼起来就是完整脚本。给执行权限chmod x /usr/local/bin/mongo_backup.sh先手动跑一次验证/usr/local/bin/mongo_backup.sh跑完去/data/backup/mongodb下看有没有生成日期.tar.gz日志里有没有“备份完成”。手动跑通是配置定时任务的前提别跳过这步直接上 crontab否则出了问题你分不清是脚本问题还是调度问题。4. crontab定时调度配置与验证4.1 编写定时任务用crontab -e编辑当前用户的定时任务加一行0 2 * * * /usr/local/bin/mongo_backup.sh这行的意思是每天凌晨2点执行备份脚本。选凌晨2点是因为业务低峰mongodump 的读压力对线上影响最小。如果你的业务是全天候高峰可以选一个相对空闲的时段比如凌晨4点。注意crontab 里的命令必须用绝对路径因为 cron 环境的 PATH 和你登录 shell 的不一样。脚本内部虽然设置了 PATH但调用脚本本身这个路径也得写全。4.2 验证定时任务是否生效配完别干等用几种方式验证。先看 cron 服务在不在跑systemctl status cron不同发行版服务名可能是cron或crond。再看当前用户的定时任务列表crontab -l确认那行任务在。然后可以临时把时间改成几分钟后观察是否自动执行、日志是否追加。验证完再改回凌晨2点。这一步很多人省了结果任务根本没触发等到真需要恢复时才发现备份是空的。4.3 日志轮转的考虑备份日志会一直追加时间长了文件变大。简单处理是在脚本里加个判断日志超过一定大小就清空或归档。更规范的做法是配 logrotate但对备份日志来说脚本里加几行就够if [ -f ${LOG_FILE} ] [ $(stat -c%s ${LOG_FILE}) -gt 10485760 ]; then mv ${LOG_FILE} ${LOG_FILE}.old fi超过10MB就把旧日志改名新日志重新写。这样日志不会无限膨胀排查问题时也不会翻半天。5. 恢复验证与常见问题排查5.1 备份能不能恢复才是关键备份做完不验证等于没备份。定期挑一份备份做恢复演练是这套方案里最容易被忽略但最重要的一环。恢复用 mongorestore# 先解压 tar -xzf 20240101_020000.tar.gz # 恢复到指定库建议恢复到测试库别直接覆盖生产 mongorestore --config/etc/mongodb-backup.cnf \ --gzip \ --nsInclude你的库名.* \ --drop \ ./20240101_020000/你的库名--gzip对应备份时的压缩--nsInclude指定只恢复某个库--drop表示恢复前先删掉目标库里的同名集合谨慎使用恢复演练时指向测试库就安全。恢复完连上去查几条数据确认集合数量和文档数量对得上这才算验证通过。5.2 常见问题速查表实际操作中遇到的问题五花八门我把高频的整理成表方便对照排查。现象可能原因处理办法脚本手动能跑crontab不执行cron环境PATH不全或脚本无执行权限脚本内设PATHchmod x命令用绝对路径备份文件为空或很小认证失败或连接不上数据库检查配置文件账号密码、认证库、host端口mongodump报版本不兼容工具版本与服务端差距过大升级mongodb工具包到与服务端接近的版本磁盘被备份占满保留天数过多或未清理调小KEEP_DAYS确认find清理逻辑生效恢复时报集合已存在未加--drop或目标库有数据恢复演练指向测试库或按需加--drop日志无输出日志路径无写权限检查目录权限确保运行用户可写5.3 几个实操心得说几个文档里不常写但很实用的点。第一备份账号一定要单独建只给backup和read权限别图省事用 root 账号一旦配置文件泄露损失可控。第二备份目录最好在独立磁盘上用df -h定期看一眼剩余空间磁盘满了备份会静默失败脚本里虽然有错误判断但磁盘满时连日志都可能写不进去。第三恢复演练至少每季度做一次别等真出事才第一次用 mongorestore那时候手忙脚乱容易出错。还有个小技巧可以在备份脚本最后加一行把最新备份的文件名和大小写进一个latest.txt监控系统读这个文件就能判断备份是否正常比翻日志方便。这套方案不复杂但把备份、压缩、清理、日志、验证串成了闭环跑起来之后基本不用操心数据安全这件事靠的就是这种平时不起眼、关键时刻能救命的自动化兜底。