SQLite 数据库快速迁移小技巧

331 阅读4分钟

原文链接:alexwlchan.net/2025/copyin…

原文作者:Alex Chan

译者:菜小鸟魔王

我保存在远程服务器上的 SQLite 数据库里存储了大量数据,经常需要将它们复制到本地机器进行分析或备份。

当新项目处于启动阶段时,数据库近乎空置,简单的 rsync 操作即可完成: rsync --progress username@server:my_remote_database.db my_local_database.db

随着项目的推进和数据库空间的增长,传输会越来越慢且不稳定。通过家庭网络从网络服务器下载 250MB 的数据库需要约 1 分钟,而这还只是很小的一部分 —— 我的数据库大多都有数 GB。

我一直在努力加快传输速度,最近发现了一个小窍门。

真正的瓶颈其实来自索引。SQLite 数据库中的大量索引虽然大大提升了查询速度,却导致数据库文件臃肿且传输速度缓慢(在某个数据库中,仅一个索引就独自霸占了整个数据库文件一半的存储空间!)

这些索引并不存储新内容 —— 只是复制其他数据表的数据来加快查询速度。重复传输索引数据会大大降低传输效率,因为相同的数据被多次传输了。在思考如何跳过传输索引时,我意识到 SQLite 其实内置了现成的解决方案。

将数据库转储为文本文件

SQLite 支持将数据库转储为文本文件。使用.dump命令时,数据库会被转写为一系列SQL语句。这类文本文件的体积通常远小于原数据库。

操作命令如下:

sqlite3 my_database.db .dump > my_database.db.txt  

下面是该文件的开头:

PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS "tags" (
   [name] TEXT PRIMARY KEY,
   [count_uses] INTEGER NOT NULL
);
INSERT INTO tags VALUES('carving',260);
INSERT INTO tags VALUES('grass',743);
…

最重要的是,原本占据大量磁盘空间的索引被简化为一行文本 —— 即创建索引的指令,而非索引实体本身。

CREATE INDEX [idx_photo_locations]
    ON [photos] ([longitude], [latitude]);

这意味着每个数据值在文本文件中仅存储一次,而非像原数据库那样,数据可能同时在原始表和多个索引中被重复保存多次。这就是文本文件比原始数据库文件更小的原因。

如果要重建数据库,可以将该文本文件导回 SQLite:

cat my_database.db.txt | sqlite3 my_reconstructed_database.db

由于 SQL 语句高度重复,文本文件特别适合压缩处理:

sqlite3 explorer.db .dump | gzip -c > explorer.db.txt.gz

以下是某数据库的磁盘空间占用对比

文件类型磁盘大小
原始 SQLite 数据库3.4 GB
文本文件1.3 GB
使用 gzip 算法压缩后的文本文件240 MB

经 gzip 压缩的文本文件比原始 SQLite 数据库小 14 倍,这使得下载数据库的速度大大加快。

升级版的 SSH+Rsync 传输命令

现在我不再直接拷贝数据库文件,而是先在服务器生成使用 gzip 压缩的转储文本文件,再将其传输到本地机器后重建数据库。具体流程如下:

# 在服务器端生成使用 gzip 算法压缩的文本文件
ssh username@server "sqlite3 my_remote_database.db .dump | gzip -c > my_remote_database.db.txt.gz"

# 将压缩文件同步到本地机器(显示传输进度)
rsync --progress username@server:my_remote_database.db.txt.gz my_local_database.db.txt.gz

# 删除服务器端的压缩文件
ssh username@server "rm my_remote_database.db.txt.gz"

# 解压本地机器上的压缩文件
gunzip my_local_database.db.txt.gz

# 通过转储文件重建数据库
cat my_local_database.db.txt | sqlite3 my_local_database.db

# 删除本地转储文本文件
rm my_local_database.db.txt

数据库转储文件是可靠的静态数据源

这个方法还顺带解决了另一个问题。

当数据库体积较大导致传输耗时过长时,若中途数据库发生更新,rsync 可能生成部分旧数据+部分新数据的无效数据库文件。此时在本地机器尝试打开数据库会报错:

database disk image is malformed

而通过预先生成转储文本文件,相当于为 rsync 提供了一个稳定的传输源。该文本文件在传输期间不会被修改,确保最终获得的始终是完整且一致的数据副本。

这套方法能够使大型数据库的传输耗时锐减,同时大大提升传输可靠性。若您需要频繁操作 SQLite 数据库迁移,不妨一试。