摘要
数据库倾向于保留大量的元数据,组织数据等。插入不太可能是简单的追加,就像文本文件一样。测试SQLite可以显示它在WAL和非WAL模式下的行为。这导致rsync必须同步比您期望的更多的数据。您可以通过使用较低的开销来减少此开销--block-size
(以更多开销计算和传输校验和为代价)。
更好的方法可能是将新记录转储为SQL转储,对其进行压缩并进行传输。另外,似乎有几种SQLite复制解决方案,您可以使用其中一种。
roaima建议您至少可以进行完整的SQL转储,使用进行压缩gzip --rsyncable
,然后rsync。我想值得进行一次测试,看看这是否足够小。
细节
您正在尝试的应该起作用。我个人会添加--partial
到您的rsync选项中,以防万一某种方式将正在增长的文件检测为部分传输。您还可以使用获得更好的转移统计信息--stats
。
要检查的第二件事是SQLite是否真的只触及了几页-老实说,如果它在整个文件中都写页面,我不会感到惊讶。一种快速的检查方法是cmp -l
在两个版本上使用-查看除最后几个页面以外是否对页面进行了更改。记住,rsync
“页面” /块的思想不同于SQLite的思想。您可以通过更改rsync的--block-size
。减少它可能会有所帮助。
编辑:我用SQLite进行了快速测试。即使有32,000页,在每页上都添加了一些日志条目。详细信息如下。
编辑2:在WAL模式下,它看起来似乎更好,尽管您仍然要承担大量的开销(可能来自检查点)。
编辑3:每次传输添加的数据越多越好-我想它可能会一遍又一遍地乱涂某些块。因此,无论是写入一次还是一百次,您都在传输同一组块。
顺便说一句:为了最大程度地减少传输,您可能比rsync更好。例如,自上次传输以来xz --best
(甚至gzip
)以来,新记录的SQL转储可能会小很多。
快速SQLite测试
架构:
CREATE TABLE log (id integer primary key not null, ts integer not null, app text not null, message text not null);
CREATE INDEX log_ts_idx on log(ts);
CREATE INDEX log_app_idx on log(app);
Perl程序:
use 5.022;
use DBI;
my $DBH = DBI->connect('dbi:SQLite:test.db', '', '', {RaiseError => 1, AutoCommit => 0})
or die "connect...";
my @apps = (
'[kthreadd]', '[ksoftirqd/0]',
⋮ # there were 191 of these
'[kworker/5:0H]',
);
my @messages = <DATA>;
(my $curr_time) = $DBH->selectrow_array(<<QUERY);
SELECT COALESCE(MAX(ts),978307200) FROM log
QUERY
my $n_apps = @apps;
my $n_msgs = @messages;
say "Apps: $n_apps";
say "Messages: $n_msgs";
say 'Start time: ', scalar gmtime($curr_time), ' UTC';
my $sth = $DBH->prepare(<<QUERY);
INSERT INTO log(ts, app, message) VALUES (?, ?, ?)
QUERY
for (my $i = 0; $i < 10_000; ++$i) {
$sth->execute(int($curr_time), $apps[int rand $n_apps], $messages[int rand $n_msgs]);
$curr_time += rand 0.1;
}
$DBH->commit;
__DATA__
microcode: CPU0 microcode updated early to revision 0x19, date = 2013-06-21
Linux version 4.5.0-2-amd64 (debian-kernel@lists.debian.org) (gcc version 5.3.1 20160528 (Debian 5.3.1-21) ) #1 SMP Debian 4.5.5-1 (2016-05-29)
⋮
有更多示例日志消息(2076)。
检查哪些页面已更改:
cp test.db test.db.old
perl test.pl
cmp -l test.db.old test.db | perl -n -E '/^\s*(\d+) / or die "wtf"; $bucket{int $1/32768} = 1; END { say join "\n", sort( { $a <=> $b } keys %bucket) }'
but with the exception of the last 4k page (or maybe a few) the file is identical each time.
您确实通过验证了cmp
吗?还是更好,xdelta
或者什么?如果您真的想最小化传输大小,请在本地保留新旧版本,这样就可以在本地计算最小的二进制差异(使用rsync以外的其他东西),而不必通过计量连接发送校验和就可以发送。如derobert建议的那样,在数据库记录级别而不是二进制文件级别执行此操作可能会更好。