更快地在计算机之间复制 SQLite 数据库的方法
alexwlchan
一种更快速的计算机间复制 SQLite 数据库的方法
- 标签:sqlite
- 发布于 2025年4月29日
我将大量数据存储在远程服务器上的 SQLite 数据库中,并且经常需要将它们复制到本地机器上进行分析或备份。
当开始一个新项目且数据库几乎为空时,这只是一个简单的 rsync
操作:
$rsync --progress username@server:my_remote_database.db my_local_database.db
随着项目成熟和数据库增长,这种方式变得越来越慢且不太可靠。从我的 Web 服务器下载一个 250MB 的数据库,通过我的家庭网络连接大约需要一分钟,这已经算很小的了——我的大多数数据库都有好几个 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
文本文件 (sqlite3 my_database.db .dump
) | 1.3 GB
gzip 压缩的文本 (sqlite3 my_database.db .dump | gzip -c
) | 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"
# 将 gzip 压缩的文本文件复制到我的本地计算机
rsync --progress username@server:my_remote_database.db.txt.gz my_local_database.db.txt.gz
# 从我的服务器中删除 gzip 压缩的文本文件
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
数据库转储是稳定的复制源
这种方法解决了我在复制 SQLite 数据库时遇到的另一个问题。
如果复制数据库需要很长时间,并且在复制过程中数据库被更新,rsync
可能会给我一个无效的数据库文件。 文件的前半部分是更新前的,后半部分是更新后的,它们不匹配。 当我尝试在本地打开数据库时,会收到错误:
database disk image is malformed
通过在开始复制操作之前创建文本转储,我为 rsync
提供了一个稳定的复制源。 该文本转储在复制过程中不会更改,因此我将始终获得完整且一致的文本文件。
这种方法为我在处理大型数据库时节省了数小时的时间,并使我的下载速度更快,更可靠。 如果你必须复制大型 SQLite 数据库,请尝试一下。