Ubuntu 22.04下用LVM快照实现数据库秒级备份与恢复

发布时间:2026/9/28 12:08:29
Ubuntu 22.04下用LVM快照实现数据库秒级备份与恢复 如果你手里管着一台跑数据库的 Ubuntu 22.04 服务器十有八九遇到过这种场景某天凌晨业务数据表被误清空DBA 的第一反应是翻备份结果发现上一次完整备份还是几天前恢复意味着丢失大量数据。更常见的是逻辑备份文件几个 TB备份窗口长到根本没有机会每天跑。我自己在多个生产环境里落地过的方案是用 LVM 快照作为备份体系的核心再围绕它做自动归档和恢复演练。把这套机制理顺之后数据库恢复点数分钟级、备份窗口按秒算日常运维压力会小很多。下面我会把整套思路、选型原因、实操命令、踩坑记录和恢复流程完整写出来。内容偏运维和 DBA 方向但只要你熟悉 Linux 基本操作跟着做就能搭出一套可用的大规模数据库快照备份机制。1. 方案选型为什么我推荐LVM快照而非直接冷备1.1 LVM 的底层机制和数据库场景下的真实优缺点LVM 全称 Logical Volume Manager核心逻辑是把物理磁盘抽象成 PV物理卷、VG卷组、LV逻辑卷三层。数据库的数据文件、日志文件、配置文件都可以放进独立的 LV然后基于 LV 创建快照。快照的原理是写时复制Copy-on-WriteCoW。创建快照的瞬间LVM 并不复制整个 LV 的数据只记录一份元数据把快照点之前的原始数据块保留下来之后原 LV 上发生新写入时LVM 会先把即将被覆盖的旧数据块复制到快照预留的空间里。也就是说快照创建动作本身几乎是秒级的成本主要发生在快照存活期间的原卷持续写入。这个机制决定了 LVM 快照在数据库场景里非常讨巧你可以在业务几乎不受影响的情况下拿到一个时间点一致的卷副本然后用这个副本去做备份、做恢复演练、甚至临时拉一个测试环境。相比数据库自身逻辑备份动辄几小时的导出快照是真正意义上的分钟级恢复点。但快照不是银弹它的优缺点非常鲜明。我整理了一张常用的对比表。维度优点缺点与限制创建速度秒级完成元数据级操作业务几乎无感快照数量过多时 LVM 元数据处理有压力空间占用初始几乎不占空间按写入增量增长增量过大时会写满快照空间导致快照失效数据一致性配合数据库一致性锁定可得到崩溃一致性副本不配合一致性操作数据文件可能损坏故障域依赖同一存储底层恢复速度快无法防硬件故障、存储阵列故障、机房级灾难恢复方式可挂载只读副本也可直接 merge 回原卷merge 操作有风险必须谨慎规划在我实际接触的案例里LVM 快照最适合的场景是单机或主备架构中的数据库服务器本地 SSD 容量足够需要频繁恢复点来应对误操作或数据损坏。它不适合单独作为容灾方案更不适合跨机房异地恢复。所以下面整套自动备份机制里LVM 快照承担的是“快速恢复点”职责真正的异地灾备还是要靠周期性归档。1.2 快照与常规备份的搭配思路数据库备份工具五花八门MySQL 系常用 XtraBackupPostgreSQL 常用 pg_basebackup还有通用的 mysqldump、pg_dump。我自己在选型时有一条比较明确的分工逻辑LVM 快照解决“快速恢复”问题提供分钟级恢复点用于误删、数据损坏、逻辑错误回滚。数据库原生的物理备份工具解决“异地容灾”问题把归档文件复制到远端存储。逻辑备份解决“单表恢复”和“跨版本迁移”问题一般频率低、量级小作为最后兜底。快照要配合备份但不能替代备份。原因是快照和原卷在同一个存储域里如果磁盘阵列坏了、机器整机宕了快照也会跟着遭殃。所以我的习惯是每天用 LVM 快照作为快速恢复点保留 3 到 7 天同时每周做一次数据库级物理备份传到异地对象存储双保险。还有一个很多人忽略的点快照不能跨文件系统类型随意恢复。比如源 LV 是 ext4快照挂载出来也是 ext4文件系统层必须匹配。另外 Ubuntu 22.04 默认安装已经带 lvm2 包但如果你用的是精简系统镜像记得先补装。2. 环境准备Ubuntu 22.04下LVM规划与快照预留空间计算2.1 先搞清楚当前LVM布局拿到一台 Ubuntu 22.04 服务器第一步不是急着建快照而是确认存储布局。我常用的命令组合是下面这段。lsblk pvs vgs lvs输出里重点看几块内容PV 是否覆盖了所有数据盘有没有磁盘还没归入 LVM。VG 名称、剩余空间能否支撑新建快照。LV 的路径比如 /dev/vg_data/lv_data后面所有快照操作都要基于这个完整路径。如果pvs提示没有物理卷说明系统盘安装时没用 LVM。这种情况下要么重装系统并在分区阶段选择 LVM要么在额外数据盘上重新初始化。生产环境我强烈不建议用工具把非 LVM 系统盘原地转换成 LVM风险远大于收益。如果数据盘是独立磁盘可以用下面命令快速初始化。sudo pvcreate /dev/sdb sudo vgcreate vg_data /dev/sdb sudo lvcreate -L 500G -n lv_data vg_data sudo mkfs.ext4 /dev/vg_data/lv_data这里有一个非常关键的设计决策数据库的数据卷和备份卷应该分成两个不同的 LV甚至可以考虑放到不同的 VG。原因很简单快照需要空间如果备份归档也写进同一个 LV快照空间和原数据会互相挤占最终引发空间不足连锁故障。我的习惯是vg_data只放数据库vg_backup专门放备份归档和快照临时挂载目录。2.2 创建数据库逻辑卷并预留快照空间假设数据库目录在/var/lib/mysql对应 LV 是/dev/vg_data/lv_data。我们需要在同一个 VG 里预留一部分空闲空间。查看 VG 剩余空间的方法vgs vg_data关注VFree列只有这里有空间才能创建快照。如果剩余为 0就得先从别处腾空间或者使用 thin pool。Thin pool 是另一套玩法快照空间是池化的不会出现单个快照撑爆的问题但 thin pool 的元数据损坏会带来更大的恢复复杂度对于数据库这种核心负载我倾向于用传统厚卷加合理预留稳字当头。创建数据库 LV 时建议直接留出 10% 到 20% 的额外空间作为快照池。比如数据卷计划用 500G那 VG 整体至少留出 600G其中 100G 不要分配给任何 LV。这 100G 不是浪费是快照的“变化缓冲”。用一条命令创建数据卷和快照卷的典型方式如下。sudo lvcreate -L 500G -n lv_data vg_data sudo lvcreate -L 100G -n lv_snap_pool vg_data快照空间具体预留多少不能拍脑袋。给你一个可以实际套用的估算公式快照空间 ≈ 数据变更速率 / 小时 × 备份窗口小时数 × 1.5 冗余系数举个例子你的数据库高峰期每小时写入 20GB备份脚本从创建快照到归档完成需要 2 小时那么快照最少需要 20 × 2 × 1.5 60GB。如果你每天做快照后保留 7 天再删除还需要把快照数量和单快照增量都算进去。需要说明的是多个快照共享 COW 存储但每个快照的增量都会占用独立空间7 个快照就是 7 份增量空间。所以不要盲目保留太多快照数量控制在 3 到 5 个以内比较现实。创建完 LV 后记得把数据库数据初始化到该卷上再把/etc/fstab配置好。这一步如果做错重启后数据库起不来后面所有快照操作都会很被动。3. 实操核心从一致性锁定到归档的完整备份链路3.1 让数据库处于一致性状态LVM 快照是存储层面的操作它不关心你跑的是 MySQL 还是 PostgreSQL。问题是数据库的数据文件和日志文件写入顺序有依赖如果快照创建时 binlog、redo log、undo log、数据页没有对齐快照里的数据可能处于“崩溃一致性”而不是“可恢复一致性”。直接用崩溃一致性的卷启动数据库InnoDB 会自动回滚PostgreSQL 也会用 WAL 重放大多数情况下能自动恢复但复杂场景下可能会出现一部分事务丢失甚至启动失败。所以我在生产环境里从来不会裸拍数据库卷一定先让数据库进入一致性状态。MySQL 和 MariaDB 的经典方法是全局读锁FLUSH TABLES WITH READ LOCK; SHOW MASTER STATUS;执行后记录 binlog 文件名和 position这个位置就是快照恢复点对应的日志位。然后等锁期间创建快照创建完成后立刻解除锁定UNLOCK TABLES;需要注意FLUSH TABLES WITH READ LOCK在读写压力大的库上可能造成堆积执行时间必须控制在 10 秒以内。如果业务不允许长时间锁表就换一个思路在备库上做快照。备库不承担写入锁定和快照的配合会从容很多。PostgreSQL 15 之前使用pg_start_backup()和pg_stop_backup()15 及以后改成了pg_backup_start()和pg_backup_stop()语义一样。示例SELECT pg_backup_start(lvm_snapshot_backup);创建快照后再执行SELECT pg_backup_stop();pg_backup_stop()会生成 backup_label 文件这个文件是 PostgreSQL 恢复时必须的。很多人会漏掉这一步认为只要卷快照一致就能恢复实际启动时会报“invalid checkpoint record”之类的错。3.2 创建快照、挂载验证与归档数据库状态就绪后创建快照的命令如下。sudo lvcreate -L 60G -s -n snap_db_20250101 /dev/vg_data/lv_data参数说明-L 60G表示给快照预留 60G 空间-s表示快照-n指定快照名最后一个是源 LV 完整路径。创建完成后立刻用lvs检查快照状态。快照创建后不要急着归档先挂载验证副本是否完整。习惯上我挂到/mnt/snap_verify并强制只读。sudo mkdir -p /mnt/snap_verify sudo mount -o ro /dev/vg_data/snap_db_20250101 /mnt/snap_verify验证时重点检查三样东西数据库数据目录是否存在关键文件比如 MySQL 的ibdata1、ib_logfile0PostgreSQL 的PG_VERSION、pg_wal。是否有一致性标记文件比如 PostgreSQL 的backup_label。文件系统日志和目录挂载是否完整。验证通过后从快照归档数据到独立备份目录。这一步推荐用 rsync因为 rsync 支持断点续传、增量同步并且能保留文件属性和 ACL。sudo rsync -aHAX --numeric-ids /mnt/snap_verify/ /data/backup/full/20250101/归档完成后卸载快照但先不急着删除建议保留到下一轮快照创建成功之后再清理。归档时注意数据库目录的权限MySQL 通常要求mysql:mysqlPostgreSQL 是postgres:postgresrsync 加--numeric-ids能避免 uid 映射错乱。清理旧快照的命令sudo umount /mnt/snap_verify sudo lvremove -f /dev/vg_data/snap_db_202501013.3 备份链路的脚本化封装手敲命令只适合临时用大规模数据库必须把上面的链路写成一个完整脚本包含前置检查、一致性锁定、创建快照、归档、解锁、清理。脚本的关键点在于任何一步失败都要立刻中止并且把错误信息记录到日志文件。下面是一个 MySQL 场景的简化脚本框架。#!/bin/bash set -e SNAP_NAMEsnap_db_$(date %Y%m%d_%H%M%S) VG_NAMEvg_data LV_NAMElv_data BACKUP_DIR/data/backup/full/$(date %Y%m%d) LOG_FILE/var/log/lvm_backup.log echo $(date) [INFO] check disk space $LOG_FILE VG_FREE$(vgs --noheadings --units g -o vg_free $VG_NAME | awk {print int($1)}) if [ $VG_FREE -lt 30 ]; then echo $(date) [ERROR] vg free space low: ${VG_FREE}G $LOG_FILE exit 1 fi echo $(date) [INFO] flush tables with read lock $LOG_FILE mysql -uroot -e FLUSH TABLES WITH READ LOCK; SHOW MASTER STATUS; /tmp/master_info.txt echo $(date) [INFO] create snapshot $LOG_FILE lvcreate -L 50G -s -n $SNAP_NAME /dev/$VG_NAME/$LV_NAME echo $(date) [INFO] unlock tables $LOG_FILE mysql -uroot -e UNLOCK TABLES; mkdir -p /mnt/snap_verify $BACKUP_DIR mount -o ro /dev/$VG_NAME/$SNAP_NAME /mnt/snap_verify rsync -aHAX --numeric-ids /mnt/snap_verify/ $BACKUP_DIR/ umount /mnt/snap_verify lvremove -f /dev/$VG_NAME/$SNAP_NAME echo $(date) [INFO] backup finished $LOG_FILE这个脚本里的VG_FREE检查、日志输出、每一步的失败退出都是我踩过坑之后加进去的。裸写版本只跑一周就会遇到一次快照空间不足而日志里毫无提示非常被动。4. 自动化与告警让备份机制自己跑起来的工程化细节4.1 用systemd timer接管定时任务备份脚本写好后定时执行一般有两个选择crontab 和 systemd timer。crontab 简单直接但缺少依赖管理和失败追踪。systemd timer 的Persistenttrue可以保证服务器长时间关机后开机恢复时自动补跑错过的任务这一点 crontab 做不到。我推荐 systemd timer。先写 service 单元文件路径/etc/systemd/system/lvm-backup.service。[Unit] DescriptionLVM snapshot backup for database Afternetwork.target mysql.service [Service] Typeoneshot ExecStart/usr/local/bin/lvm_backup.sh再写 timer 单元文件/etc/systemd/system/lvm-backup.timer。[Unit] DescriptionRun LVM backup daily at 02:00 [Timer] OnCalendar*-*-* 02:00:00 Persistenttrue [Install] WantedBytimers.target启用并启动定时器sudo systemctl daemon-reload sudo systemctl enable --now lvm-backup.timer sudo systemctl list-timers lvm-backup.timer用 systemd timer 还有一个额外好处脚本异常退出后service 会记录非零退出码你可以通过systemctl status lvm-backup.service快速定位故障。如果脚本挂死或退出码异常再配合监控系统发告警。4.2 保留策略、空间告警与恢复演练备份不是无限堆积就行空间管理稍不注意就会把整台服务器磁盘占满。我自己执行的经验法则是快照保留 3 个归档全量备份保留 7 天异地备份保留 14 天。这个节奏兼顾了恢复效率和存储成本。脚本里可以做简单的旧备份清理find /data/backup/full -maxdepth 1 -type d -mtime 7 -exec rm -rf {} \;清理时别直接在归档目录上乱删先确认最近一次快照和归档都成功再执行清理。稳妥的建议是在归档目录下写一个.last_success标记文件脚本清理前先检查该文件的时间戳。另一个必须做的是空间告警。监控vgs的VFree和快照空间的百分比一旦接近 80% 就要提前处理。快照空间满了会直接失效数据库本身不受影响但恢复点瞬间消失这是最容易被人忽略的隐形故障。告警可以走企业微信、钉钉、Slack 的 webhook。实际使用中并不需要写复杂代码一行 curl 就能把备份脚本的结果发出去。curl -X POST -H Content-Type: application/json \ -d {msgtype: text, text: {content: LVM backup failed: 磁盘空间不足}} \ https://hooks.example.com/webhook不要只在失败时发告警成功时也要发一条精简消息。我的习惯是成功消息只包含备份文件大小、耗时、快照状态失败消息则带上具体日志片段。这样值班的人不用登录服务器就能判断备份是否正常。备份机制跑通后恢复演练比备份本身更重要。每个季度至少做一次破坏性测试故意删掉数据库中的一个表然后按恢复流程从快照把数据找回来记录实际 RTO。如果演练做过一次心里就有底真出事时才不会手忙脚乱。5. 数据恢复实战与常见问题排查5.1 从快照恢复的两种主流方式快照的真正价值体现在恢复那一刻。实际恢复有两种主流办法各有适用场景。第一种是直接合并快照这也是 LVM 提供的最快恢复方式。sudo lvconvert --merge /dev/vg_data/snap_db_20250101执行合并后源 LV 的数据会回滚到快照创建时的状态。如果你的服务器重启过合并可能延迟到重启后自动完成。关键前提是合并会丢弃快照之后所有的数据变更所以在合并前一定要把当前生产数据再备份一遍万一恢复错了还有退路。第二种方式是从快照挂载点把数据文件拷贝回原 LV。这种办法更安全适合只想恢复某个表空间或个别数据文件的场景。sudo mount -o ro /dev/vg_data/snap_db_20250101 /mnt/snap_verify cp /mnt/snap_verify/xxx/ibdata1 /var/lib/mysql/拷贝方式的问题在于如果你只覆盖部分文件数据库整体可能处于不一致状态。所以用拷贝恢复时最好是整个数据库目录整体覆盖并且启动数据库前删掉旧的 redo 日志或 WAL让数据库做一次崩溃恢复。恢复完成后不要急着对外开放连接。先启动数据库检查日志里有没有错误执行SELECT COUNT(*)验证关键表数据量再对比一致性标记文件确认恢复点。MySQL 可以SHOW BINLOG EVENTS确认日志位置PostgreSQL 可以查pg_control或者恢复日志。5.2 故障排查速查与避坑心得实际操作中总会遇到各种奇奇怪怪的问题。我把高频故障整理成了一张速查表希望能帮你看完直接对症下药。故障现象常见原因排查命令处理办法快照挂载后数据目录不完整创建快照前未同步刷盘dmesg | tail用sync或数据库一致性锁定后重新建快照快照空间 100%写入增量超过预留空间lvs -a看 Data%删除不需要的快照或lvresize扩容合并快照后数据库起不来日志文件与数据文件不一致查看数据库 err log删除 redo/WAL 后尝试崩溃恢复归档速度极慢备份目录与生产卷在同一磁盘iostat -x 1备份目录换到独立磁盘或远端挂载定时备份未执行systemd timer 状态异常systemctl list-timers检查Persistenttrue和脚本可执行权限快照存在但挂载只读失败文件系统被标记为脏fsck -n /dev/vg_data/snap_xxx先确认数据完整性再做文件系统修复避坑心得方面有几条是拿真金白银换来的教训。快照创建前一定要确保 VG 有足够空闲空间。我遇到过两次快照空间不足导致快照失效的情况原因都是业务写入突增之前预留的 60G 两天就写满了。后来我把快照空间监控和告警纳入脚本一旦 Data% 超过 80% 就自动发告警情况才缓解。快照不是数据安全的全部不要把鸡蛋放在同一个篮子里。LVM 快照只解决逻辑错误和误操作机器的电源、磁盘控制器故障、存储整列故障任何一项都能让快照蒸发。所以我的原则是快照做快速恢复点异地备份做最终保底两条线都不能断。另外恢复演练千万别只在测试环境玩测试环境和生产环境的数据规模差异会让恢复时间完全失真。有条件的话在生产机器的空闲时间做一次低峰期演练记录真实的挂载、拷贝、启动时间才能订出可靠的数据恢复 SLA。最后分享一个比较实用的小习惯我会把快照名称加上时间戳和用途后缀比如snap_db_20250101_reset、snap_db_20250101_pre_upgrade这样管理大量快照时不会搞混。命名清晰在紧急恢复时能省下不少判断时间毕竟那时候最不需要的就是对着十几条日期不明确的快照纠结该选哪一个。