设计带加锁机制的串行备份队列,核心是按目标设备(如 /backup/nas)分配唯一 flock 锁文件实现写互斥,配合队列文件与守护进程调度任务顺序,并辅以 ionice、bwlimit、timeout 及空间检查等 I/O 降级与保护措施。

设计带加锁机制的串行备份队列,核心是避免多个备份任务并发写磁盘,尤其在慢速存储(如NAS、USB硬盘)上极易引发I/O阻塞。关键不在于“完全禁止并发”,而在于“同一时间只允许一个备份进程写目标设备”,其他任务排队等待。
用文件锁实现轻量级互斥
推荐使用 flock —— 它基于内核文件锁,可靠、无残留、支持阻塞/非阻塞模式,比自建 lockfile 更安全。
- 为每个备份目标(如 /backup/nas)分配唯一锁文件,例如
/var/lock/backup_nas.lock - 所有备份脚本统一用
flock -x /var/lock/backup_nas.lock -c '执行实际备份命令'包裹核心操作 - 锁自动释放:进程退出或管道中断时,内核自动清理,无需手动 rm lockfile
- 若想让等待更友好(比如显示排队信息),可用
flock -w 300设置最长等待5分钟,超时退出并报错
队列化调度:用临时队列文件 + flock 控制执行顺序
单纯加锁只能保证“不并发”,但无法管理“谁先谁后”。需引入简单队列机制:
- 所有备份请求(如 cron 触发或手动调用)先向共享队列文件(如
/var/run/backup.queue)追加一行:任务标识+时间戳+参数 - 启动一个常驻守护进程(或每分钟轮询的 cron),用
flock -x /var/lock/backup_queue.lock锁住队列文件,读取首行、移除该行、再解锁 - 取出的任务交由独立子 shell 执行(仍套用目标锁),确保“取任务”和“执行任务”解耦,避免单点卡死影响队列推进
- 可选:在队列中记录 PID 或日志路径,便于排查挂起任务
备份任务自身做 I/O 降级与超时保护
加锁防并发只是第一步,单个备份也要避免霸占磁盘:
- 用
ionice -c 3降低 I/O 调度优先级(空闲类),让交互任务优先响应 - 用
rsync --bwlimit=2000或tar --tape-length=... --tape-device=...(如适用)控制写入速率 - 对 tar/dd/rsync 等主命令设置超时:
timeout 6h rsync ...,防止网络挂起或坏盘导致长期阻塞 - 检查目标磁盘剩余空间与 inode 数量,不足时直接退出,不排队不加锁
异常恢复与可观测性
锁和队列都可能因 kill -9 或断电残留,需容错设计:
- 启动时检测锁文件是否被占用:用
flock -n lockfile -c true测试,失败则尝试lsof | grep lockfile查残留进程 - 队列文件定期归档(如每天 mv backup.queue backup.queue.$(date +%F)),避免无限增长
- 每个任务执行前后写结构化日志(含锁获取时间、开始/结束时间、退出码),方便分析排队延迟
- 用
lslocks或lsof /var/lock/快速查看当前哪些锁被谁持有
这套机制不依赖外部服务(如 Redis、数据库),纯 Shell 实现,部署简单,已在中小型 NAS 备份场景稳定运行多年。重点在于锁粒度要细——按目标设备隔离,而不是全局一把锁;队列要轻量,避免引入新单点故障。

















