Perché rsync non utilizza i trasferimenti delta per un singolo file in una rete?


15

Ho esaminato questa domanda e questa domanda , ma non sembrano affrontare i sintomi che sto vedendo.

Ho un file di registro di grandi dimensioni (circa 600 MB) che sto cercando di trasferire attraverso una rete cellulare. Poiché si tratta di un file di log è appena aggiunto al (anche se in realtà in un database SQLite con solo INSERT corso di esecuzione, quindi non è del tutto così semplice, ma con l'eccezione dell'ultima 4k pagina (o forse un pochi) il file è identico ogni volta. È importante che vengano effettivamente inviati solo i cambiamenti (e qualunque somma di controllo), perché la connessione dati è misurata.

Tuttavia, quando eseguo un test attraverso una connessione illimitata (ad es. Hotspot wifi gratuito) non vedo un rapporto accelerato o ridotto di dati osservato o segnalato. Tramite una connessione WiFi lenta vedo nell'ordine di 1 MB / s o meno, segnalando che il trasferimento richiederà circa 20 minuti. Su una connessione WiFi veloce vedo una velocità uniforme più veloce, ma nessun rapporto di speedup e un secondo tentativo di trasferimento (che ora dovrebbe essere più veloce perché i due file sono identici) ora mostra alcuna differenza.

Il comando (sanificato per rimuovere le informazioni sensibili) che sto usando è:

rsync 'ssh -p 9999' --progress LogFile michael@my.host.zzz:/home/michael/logs/LogFile

L'output che ottengo alla fine è simile al seguente:

LogFile
    640,856,064 100%   21.25MB/s   0:00:28 (xfr$1, to-chk=0/1)

Non si parla di alcun tipo di accelerazione.

Sospetto che il problema potrebbe essere uno dei seguenti:

  • Mi manca qualche opzione da riga di comando. Tuttavia, rileggere la pagina man sembra suggerire che i trasferimenti delta sono abilitati per impostazione predefinita: vedo solo le opzioni per disabilitarli.
  • Sto usando rsync su ssh (anche su una porta non standard) a causa del fatto che il server si trova dietro un firewall che consente solo ssh. Non ho visto nulla che dica esplicitamente che i trasferimenti delta non funzioneranno se il demone rsync non è in esecuzione. Ho provato a usare la notazione "::" anziché ":" ma la pagina man non è molto chiara su cosa sia un "modulo" e il mio comando è stato respinto per aver specificato un modulo non valido.

Ho escluso quanto segue:

  • trasferimenti delta non eseguiti su una rete locale. Eliminato perché sto cercando di eseguire il trasferimento su Internet
  • spese generali dovute al calcolo del checksum. Ho visto questo comportamento sia su una connessione Wifi veloce che lenta e la velocità di trasferimento non sembra essere legata al calcolo.

1
but with the exception of the last 4k page (or maybe a few) the file is identical each time. Lo hai effettivamente verificato con cmp? O meglio, con xdeltao qualcosa del genere? Se vuoi davvero ridurre al minimo le dimensioni del trasferimento, mantieni localmente la vecchia e la nuova versione, in modo da poter calcolare localmente una differenza binaria minima (con qualcosa di diverso da rsync) e inviarla senza dover inviare checksum sulla connessione misurata. Fare questo a livello di record di database anziché a livello di file binario è probabilmente ancora migliore, come suggerisce Derobert.
Peter Cordes,

1
Inoltre, avresti potuto usare rsync --statse anche -v -vottenere statistiche ancora più dettagliate. Rsync ti dirà quanti dati abbinati rispetto a quelli non corrispondenti c'erano.
Peter Cordes,

Risposte:


27

Sommario

I database tendono a conservare molti metadati, dati organizzativi, ecc. È molto improbabile che un inserto sia un'appendice semplice, come sarebbe con un file di testo. Il test di SQLite mostra che si comporta in questo modo, sia in modalità WAL che non WAL. Questo porta a rsync dover sincronizzare molti più dati di quanto ti aspetti. È possibile ridurre un po 'questo sovraccarico utilizzando un valore basso --block-size(a scapito di una maggiore computazione e trasferimento di checksum).

Un approccio migliore è probabilmente quello di scaricare nuovi record come dump SQL, comprimerlo e trasferirlo. In alternativa, sembrano esserci diverse soluzioni di replica per SQLite, è possibile utilizzare una di queste.

roaima suggerisce come minimo che potresti probabilmente fare un dump SQL completo, comprimerlo usando gzip --rsyncablee quindi risincronizzarlo. Merita una prova, suppongo, per vedere se è un delta abbastanza piccolo.

Dettagli

Quello che stai cercando dovrebbe funzionare. Aggiungerei personalmente --partialalle tue opzioni rsync, nel caso in cui in qualche modo rilevi il file in crescita come trasferimento parziale. Puoi anche ottenere statistiche di trasferimento migliori con --stats.

La seconda cosa da verificare è se SQLite sta davvero toccando solo alcune pagine - onestamente, non sarei sorpreso se sta scrivendo pagine in tutto il file. Un modo rapido per verificare sarebbe usarecmp -l su due versioni: vedere se ci sono modifiche alle pagine diverse da quelle finali. Ricorda che rsyncl'idea di una "pagina" / blocco è diversa da quella di SQLite; puoi cambiare rsync tramite --block-size. Ridurlo potrebbe aiutare.

Modificare: ho fatto un test rapido con SQLite. Anche con 32k pagine, aggiungendo un sacco di voci di registro scarabocchiate su ogni pagina. Dettagli sotto.

Modifica 2 : sembra essere migliore in modalità WAL, anche se si prende ancora una grande quantità di spese generali, probabilmente dal punto di controllo.

Modifica 3 : è anche meglio quanti più dati stai aggiungendo per trasferimento, suppongo che probabilmente scarabocchi certi blocchi ancora e ancora. Quindi trasferisci lo stesso set di blocchi indipendentemente dal fatto che li abbia scritti una o cento volte.

A proposito: per ridurre al minimo il trasferimento, probabilmente puoi fare molto meglio di rsync. Ad esempio, un dump SQL di nuovi record dall'ultimo trasferimento eseguito xz --best(o anchegzip ) sarebbe probabilmente un po 'più piccolo.

Test rapido di SQLite

Schema:

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);

Programma 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)

C'erano molti più messaggi di log di esempio (2076).

Verifica per quali pagine sono cambiate:

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) }'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.