SQLite把整个数据库存储在单一文件中,这让它的备份看起来非常简单——直接复制文件不就行了?但实际情况没这么轻松。如果数据库正被写入,直接复制的文件可能处于不一致状态,恢复时轻则数据错乱,重则整个文件损坏无法打开。因此,SQLite的备份需要区分冷备和热备两种策略,分别应对停机窗口和在线运行两种场景。本文将深入分析这两种方案的原理、操作方法和适用边界。

冷备份:停机状态下的文件复制
冷备份(Cold Backup)是指在数据库完全关闭、没有任何进程持有连接的状态下,直接复制数据库文件。这是最简单也最可靠的备份方式,因为此时磁盘上的文件就是数据库的完整一致快照。
执行冷备份前,必须确保所有客户端连接都已关闭。可以先用lsof或fuser检查文件是否被占用,然后执行复制操作:
# 检查数据库文件是否被进程占用 lsof /var/data/app.db # 确认无进程占用后,执行冷备份 sqlite3 /var/data/app.db "PRAGMA wal_checkpoint(TRUNCATE);" cp /var/data/app.db /backup/app_$(date +%Y%m%d).db
这里有一个非常关键的坑:如果数据库运行在WAL(Write-Ahead Logging)模式下,目录下除了app.db还会有app.db-wal和app.db-shm两个文件。WAL文件中保存着尚未合并回主数据库文件的改动,如果只复制主文件而遗漏WAL文件,备份就会缺少最新的事务数据。上面的脚本在复制前先执行PRAGMA wal_checkpoint(TRUNCATE),把WAL日志强制合并进主文件并清空,这样只需备份一个文件即可。如果无法执行checkpoint,就必须把三个文件一起复制,并且必须保证它们是同一时刻的一致状态。
冷备份的优点是绝对一致、恢复极快(把文件拷回去即可)、不依赖任何工具。缺点也很明显:需要停机或至少暂停写入,对7×24小时运行的服务不友好。因此它适合维护窗口明确的场景,比如每天凌晨业务低峰期执行一次定时备份。
热备份:在线完成一致性拷贝
当业务不允许中断时,就需要热备份(Hot Backup)。SQLite官方提供了两种热备方式:命令行的.backup命令和编程接口层面的在线备份API。
使用sqlite3命令行的.backup
最常用的方式是通过sqlite3 shell执行备份命令:
# 基本备份 sqlite3 /var/data/app.db ".backup /backup/app_backup.db" # 备份到指定压缩文件(借助管道) sqlite3 /var/data/app.db ".backup '/backup/app_backup.db'" # 备份远程或指定数据库中的特定表结构时,可结合dump使用 sqlite3 /var/data/app.db ".dump" | gzip > /backup/app_dump.sql.gz
.backup命令的底层是在源数据库上开启一个读事务,获取一致性快照后逐页拷贝到目标文件。整个过程源数据库可以继续正常读写,写操作不会阻塞备份,备份也不会长时间阻塞写操作(仅在页拷贝的短暂瞬间需要协调)。相比直接复制文件,它保证拷出来的目标文件一定是一致且完整的,不需要担心WAL文件的问题。
使用在线备份API
如果需要在应用程序内部实现备份逻辑,可以使用SQLite提供的sqlite3_backup_*系列C API。以Python为例,其内置的sqlite3模块对此做了封装:
import sqlite3
src = sqlite3.connect('/var/data/app.db')
dst = sqlite3.connect('/backup/app_backup.db')
# backup方法内部使用sqlite3_backup_step逐页拷贝
# pages=-1表示一次性拷贝全部页,sleep参数控制重试间隔
src.backup(dst, pages=-1, progress=None, sleep=0.25)
dst.close()
src.close()
print('备份完成')在线备份API的一个重要特性是支持增量推进:通过设置pages参数,每次只拷贝固定数量的页,然后把控制权交还给主循环。这对超大数据库特别有价值——可以在应用空闲时逐步完成备份,避免长时间占用资源。需要注意,备份过程中如果源数据库持续被写入,API内部会自动重试;但如果写入压力极大导致每次重试前又有新写入,备份可能反复回退,此时应考虑加大sleep间隔或改在低峰期执行。
冷备与热备如何选择:方案对比与实践建议
两种方案各有明确的适用边界,可以通过下表直观对比:
| 对比维度 | 冷备份 | 热备份 |
|---|---|---|
| 业务影响 | 需停机或暂停写入 | 不中断读写 |
| 一致性保证 | 绝对一致 | 快照级一致 |
| 实现复杂度 | 极低,复制文件即可 | 需调用backup命令或API |
| 大库友好度 | 拷贝速度快 | 逐页拷贝,支持增量推进 |
| WAL模式处理 | 需checkpoint或同时备份wal文件 | 自动处理,无额外负担 |
实践中建议按照业务特点组合使用:对于允许短暂停机的内部系统,每天低峰期执行冷备份即可;对于在线服务,推荐使用.backup命令做定时热备,并配合.dump导出SQL文本作为异构备份。SQL文本格式有一个额外好处——它不依赖特定页大小和SQLite版本,极端情况下即使二进制备份损坏,文本导出仍然可以恢复数据。
备份策略还应包含验证环节。备份完成后务必检查文件完整性:
# 校验备份数据库的完整性 sqlite3 /backup/app_backup.db "PRAGMA integrity_check;" # 校验通过后记录校验和,便于后续比对 sha256sum /backup/app_backup.db >> /backup/checksums.txt
此外,务必保留多个时间点的备份副本并遵循异地存储原则。备份文件与主库放在同一块磁盘上等于没有备份,建议通过脚本将备份同步到其他机器或对象存储。对于高写入量的数据库,还可以启用PRAGMA journal_mode=WAL配合定期checkpoint,减少热备期间的页拷贝量,提升备份效率。只要建立定期执行、定期验证、异地存放的完整流程,SQLite的数据安全完全可以得到可靠保障。