误删生产环境 SQLite 数据库后的极限救援
在生产环境中,rm -rf 往往是运维人员的噩梦。今天,我误删了正在高频读写的 SQLite 数据库文件,但在重启服务前,通过 Linux 的 /proc 文件系统,成功将数据“起死回生”。
事故现场
背景:Docker 容器中的 API 网关服务使用 SQLite 作为元数据存储。
误操作:清理磁盘时误删了核心数据库文件 gateway.db。
现状:文件在文件系统中已不可见,但 Docker 容器仍在运行。一旦重启服务,数据将永久丢失。
原理分析:为什么数据还能救?
Linux 下删除文件只是解除了目录项的链接。只要仍有进程持有该文件的句柄(File Descriptor),内核就不会释放磁盘空间,数据依然存在于磁盘上。
只要进程不死,数据就在。
救援三部曲
第一步:锁定进程 PID
找到容器在宿主机上的真实 PID:
docker inspect --format '{{.State.Pid}}' <container_id>
# 假设输出 PID 为 1496463
第二步:寻找“消失”的句柄
进入 /proc/<PID>/fd 目录,查找被删除的文件描述符:
ls -l /proc/1496463/fd | grep deleted
20 -> '.../gateway.db (deleted)'
21 -> '.../gateway.db-wal (deleted)'
22 -> '.../gateway.db-shm (deleted)'
关键点:SQLite 在 WAL 模式下,数据不仅存在于主文件,最新的事务数据在 -wal 文件里。忽略 -wal 会导致数据丢失。
第三步:通过句柄“复活”文件
直接使用 cp 命令将句柄复制出来:
mkdir -p /tmp/db_rescue
# 复制主文件、WAL 文件和 SHM 文件
cp /proc/1496463/fd/20 /tmp/db_rescue/gateway.db
cp /proc/1496463/fd/21 /tmp/db_rescue/gateway.db-wal
cp /proc/1496463/fd/22 /tmp/db_rescue/gateway.db-shm
验证与收尾
将三个文件放在同一目录下,SQLite 会自动合并 WAL 文件。
sqlite3 /tmp/db_rescue/gateway.db "SELECT count(*) FROM users;"
查询结果正常,数据完整无损!
复盘总结
- Linux 机制:
rm只是 unlink,引用计数不为 0 时数据不灭。 - SQLite 特性:必须同时恢复
.db、.db-wal和.db-shm文件。 - 备份:虽然救回数据,但生产环境仍需依赖定期备份。
结论:误删不可怕,只要进程在,/proc 就是最后一道防线。