SQLite和其他数据库很不一样,它不需要独立的服务进程,整个数据库就是磁盘上的一个文件。正因为这个特性,很多开发者想当然地认为迁移就是把文件复制过去。但实际操作中,因为复制时机不对、附属文件遗漏、权限没配置好等问题导致的故障屡见不鲜。这篇文章就来系统地讲一讲,如何把一个SQLite数据库完整、安全地迁移到新服务器。

迁移前必须搞清楚的三类附属文件
一个正在运行的SQLite数据库,往往不止一个主文件。除了.db或者.sqlite这个主数据库文件之外,还可能存在两个关键的附属文件。第一个是WAL文件,也就是Write-Ahead Logging日志文件,扩展名通常是-wal。如果你的数据库开启了WAL模式,最近的事务数据很可能还停留在WAL文件里,没有合并到主文件中。此时只复制主文件,就等于丢掉了这部分还没落盘的数据。
第二个是SHM文件,扩展名-shm,它是共享内存索引文件,用于多进程并发访问WAL时的协调。这个文件丢了倒不至于损坏数据,因为它可以自动重建,但如果你在复制文件的同时有进程还在写库,它和WAL文件的状态就可能出现不一致。
第三个需要关注的是journal文件,扩展名为-journal,这是默认回滚模式下产生的日志。如果数据库在事务执行中途被强行停止,这个文件就承担着恢复未完成事务的职责。总结一下:迁移前最好先确认数据库处于关闭状态,或者通过命令主动做一次checkpoint,把WAL日志合并进主文件,这样复制时只需要处理一个主文件,风险最低。
三种主流迁移方式对比与操作步骤
第一种方式是冷拷贝,也就是停掉所有访问数据库的进程后直接复制文件。这是最简单粗暴的方式,适合停机窗口允许的场景。操作时要注意把主文件、wal文件、shm文件一起复制,或者先执行checkpoint。在sqlite3命令行里可以这样操作:
# 进入sqlite3命令行 sqlite3 /path/to/app.db # 将WAL日志合并到主库文件 PRAGMA wal_checkpoint(TRUNCATE); # 退出后确认wal文件已经清空 .quit
checkpoint执行完之后,-wal文件会被清空成零字节,这时候主文件就是完整的,可以放心复制。如果不确定数据库当前的日志模式,可以先用PRAGMA journal_mode;查询一下。
第二种方式是使用.backup命令,这是官方推荐的热备份方式。它最大的好处是不需要停库,即使在有其他进程读写数据库的情况下,也能得到一个逻辑上一致的副本。命令的用法非常简单:
# 在源服务器上执行在线备份 sqlite3 /path/to/app.db ".backup /tmp/app_backup.db" # 备份完成后校验完整性 sqlite3 /tmp/app_backup.db "PRAGMA integrity_check;"
如果integrity_check返回的是ok,说明备份文件结构完整。之后把app_backup.db通过scp或者rsync传到新服务器即可。注意.backup生成的是一个新的主文件,不携带wal日志,这一点比手工拷贝三个文件省心得多。
第三种方式是使用VACUUM INTO,这是SQLite 3.27之后引入的功能。它和.backup类似,但会顺便对输出文件做碎片整理,生成的副本体积更紧凑,适合顺便优化数据库文件大小的场景:
-- 在源库上执行,生成一份整理过的完整副本 VACUUM INTO '/tmp/app_compact.db';
三种方式的取舍可以这样判断:能停库就冷拷贝,最省事也最可靠;不能停库就优先用.backup;顺便想清理碎片就用VACUUM INTO。无论哪种方式,传输到新服务器时都建议用scp配合校验,例如先在源端计算md5值,传输后在目标端再算一次,两边一致才说明文件没有在传输中损坏。
新服务器上的配置、校验与常见报错排查
文件传过去只是完成了一半,新环境上的配置同样关键。首先是文件权限问题。SQLite在执行写操作时,不仅需要数据库文件本身可写,还需要文件所在目录可写,因为创建journal或wal临时文件时要在目录下生成新文件。这是新手最容易踩的坑:明明chmod 666了数据库文件还是报unable to open database file,原因往往就是目录权限不足。给运行程序的系统用户授予目录写权限即可解决:
# 把数据库目录的所有权交给运行程序的用户 chown -R appuser:appuser /var/data/appdb chmod 755 /var/data/appdb chmod 644 /var/data/appdb/app.db
其次是版本兼容性问题。SQLite 3.25之后修改了窗口函数相关的文件格式细节,一般新的sqlite3读旧文件没问题,但旧版本程序打开新格式文件可能报错。迁移前务必确认新服务器的SQLite版本不低于源服务器,可以用SELECT sqlite_version();对比确认。
最后做一次完整的落地校验。除了前面提到的integrity_check,还建议对比数据量,例如统计关键表的行数:
-- 在源库和新库分别执行,对比结果 SELECT COUNT(*) FROM orders; SELECT COUNT(*) FROM users;
如果迁移后程序报database is locked,多半是文件系统不支持POSIX锁导致的,典型的场景是把数据库放在了NFS或者某些网络挂载盘上。解决办法要么把数据库文件放回本地磁盘,要么换用其他数据库方案。网络文件系统上的锁不可靠,这一点官方文档有明确警告,千万别抱侥幸心理。
总结一下,SQLite迁移的核心思路就三句话:迁移前做checkpoint或在线备份保证一致性,传输时校验哈希值保证完整性,落地后配好权限并跑integrity_check。把这几个环节都做到位,整个迁移过程基本不会出问题。
SQLite数据库迁移数据库备份服务器迁移修改时间:2026-09-10 05:24:31