![概括](https://linux22.com/image/91102/%E6%A6%82%E6%8B%AC.png)
我看过这个问题和这个问题,但它们似乎没有解决我所看到的症状。
我有一个很大的日志文件(大约 600 MB),我正在尝试通过蜂窝网络传输该文件。因为它是一个日志文件,所以它只是附加到其中(尽管它实际上位于仅执行 INSERT 的 SQLite 数据库中,所以它不是相当就这么简单,但除了最后 4k 页(或者可能是少数页)之外,文件每次都是相同的。重要的是,只有更改(以及需要传输的任何校验和)才会实际发送,因为数据连接是按流量计费的。
然而,当我通过不限流量的连接(例如免费 WiFi 热点)执行测试时,我没有看到观察或报告的数据传输加速或减少。通过慢速 WiFi 连接,我发现传输速度约为 1MB/s 或更低,报告称传输将花费近 20 分钟。通过快速 WiFi 连接,我看到统一的更快速度,但没有加速报告,并且第二次尝试传输(现在应该更快,因为两个文件相同)现在确实显示出任何差异。
我正在使用的(已清理以删除敏感信息)命令是:
rsync 'ssh -p 9999' --progress LogFile [email protected]:/home/michael/logs/LogFile
我最后得到的输出如下所示:
LogFile
640,856,064 100% 21.25MB/s 0:00:28 (xfr$1, to-chk=0/1)
没有提到任何类型的加速。
我怀疑问题可能是以下之一:
- 我缺少一些命令行选项。然而,重新阅读手册页似乎表明默认情况下启用增量传输:我只看到禁用它们的选项。
- 我通过 ssh 使用 rsync(甚至在非标准端口上),因为服务器位于仅允许 ssh 的防火墙后面。不过,我还没有看到任何明确说明如果 rsync 守护进程未运行则增量传输将无法工作的内容。我尝试使用“::”符号而不是“:”,但手册页不太清楚“模块”是什么,并且我的命令因指定无效模块而被拒绝。
我已经排除了以下情况:
- 增量传输不在本地网络上执行。已排除,因为我正在尝试通过互联网进行传输
- 由于校验和计算而产生的开销。我在快速和慢速 Wifi 连接上都看到过这种行为,并且传输速率似乎不受计算限制。
答案1
概括
数据库往往会保留大量元数据、组织数据等。插入不太可能是简单的追加,就像文本文件一样。测试 SQLite 表明它在 WAL 和非 WAL 模式下都有这种行为。这导致 rsync 必须同步比您预期更多的数据。您可以通过使用低值来稍微减少此开销--block-size
(以更多开销计算和传输校验和为代价)。
更好的方法可能是将新记录转储为 SQL 转储、压缩并传输。或者,似乎有多种 SQLite 复制解决方案,您可以使用其中之一。
罗埃马建议您至少可以执行完整的 SQL 转储,使用 压缩它gzip --rsyncable
,然后进行 rsync。我认为值得测试一下,看看这个增量是否足够小。
细节
你正在尝试什么应该工作。我个人会添加--partial
到您的 rsync 选项中,以防万一它以某种方式将不断增长的文件检测为部分传输。您还可以通过 获得更好的转会统计数据--stats
。
要检查的第二件事是 SQLite 是否真的只涉及几个页面 - 老实说,如果它在整个文件中写入页面,我不会感到惊讶。一种快速检查方法是在两个版本上使用cmp -l
- 查看除最后几页之外的页面是否有更改。请记住,rsync
“页面”/块的概念与 SQLite 的不同;您可以通过更改rsync --block-size
。减少它可能会有所帮助。
编辑:我用 SQLite 做了一个快速测试。即使有 32k 页,添加一堆潦草的日志条目每一个页。详细信息如下。
编辑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 ([email protected]) (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) }'