Dlaczego rsync nie używa transferu delta dla pojedynczego pliku w sieci?

15

Patrzyłem na to pytanie i to pytanie , ale wydaje się, że nie odnoszą się one do objawów, które widzę.

Mam duży plik dziennika (około 600 MB), który próbuję przesłać przez sieć komórkową. Ponieważ jest to plik dziennika jest dołączany tylko do (chociaż to jest rzeczywiście w bazie danych SQLite tylko z INSERT wykonywana, więc nie jest całkiem takie proste, ale z wyjątkiem ostatniego 4k strony (a może kilka) plik jest za każdym razem identyczny Ważne jest, aby tylko zmiany (i wszelkie sumy kontrolne, które należy przesłać) były faktycznie wysyłane, ponieważ połączenie danych jest mierzone.

Jednak gdy przeprowadzam test na nieskomunikowanym połączeniu (np. Bezpłatny hotspot Wi-Fi), nie widzę, aby zaobserwowano lub zgłoszono przyspieszenie lub zmniejszenie transferu danych. Przez wolne połączenie Wi-Fi widzę, że rzędu 1 MB / s lub mniej, zgłaszając, że transfer zajmie prawie 20 minut. Przez szybkie połączenie Wi-Fi widzę jednolitą, większą prędkość, ale nie ma raportu o przyspieszeniu, a druga próba transferu (która powinna być teraz szybsza, ponieważ oba pliki są identyczne) nie wykazuje żadnej różnicy.

Polecenie (odkażone w celu usunięcia poufnych informacji) to:

rsync 'ssh -p 9999' --progress LogFile [email protected]:/home/michael/logs/LogFile

Wynik, który otrzymuję na końcu, wygląda następująco:

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

Nie ma wzmianki o żadnym przyspieszeniu.

Podejrzewam, że problem może być jednym z następujących:

  • Brakuje mi opcji wiersza polecenia. Jednak ponowne czytanie strony podręcznika wydaje się sugerować, że transfery delta są domyślnie włączone: widzę tylko opcje ich wyłączenia.
  • Używam rsync przez ssh (nawet na niestandardowym porcie), ponieważ serwer znajduje się za zaporą ogniową, która zezwala tylko na ssh. Jednak nic nie widziałem wyraźnie mówiącego, że transfery delta nie będą działać, jeśli demon rsync nie działa. Próbowałem użyć notacji „::” zamiast „:”, ale strona podręcznika nie jest bardzo jasna na temat tego, czym jest „moduł”, a moje polecenie zostało odrzucone za podanie nieprawidłowego modułu.

Wykluczyłem, co następuje:

  • transfery delta nie są wykonywane w sieci lokalnej. Wykluczony, ponieważ próbuję wykonać transfer przez Internet
  • koszty ogólne z powodu obliczenia sumy kontrolnej. Widziałem to zachowanie zarówno na szybkim, jak i wolnym połączeniu Wi-Fi, a szybkość transferu nie wydaje się być związana z obliczeniami.
Michael
źródło
1
but with the exception of the last 4k page (or maybe a few) the file is identical each time. Czy faktycznie to zweryfikowałeś cmp? A może lepiej, xdeltaczy coś? Jeśli naprawdę chcesz zminimalizować rozmiar transferu, zachowaj starą i nową wersję lokalnie, abyś mógł obliczyć lokalnie minimalny binarny plik różnicowy (z czymś innym niż rsync) i po prostu wysłać go bez konieczności wysyłania sum kontrolnych przez mierzone połączenie. Robienie tego na poziomie rekordu bazy danych zamiast poziomu pliku binarnego jest prawdopodobnie jeszcze lepsze, jak sugeruje derobert.
Peter Cordes,
1
Można również użyć rsync --stats, a także -v -vuzyskać jeszcze więcej szczegółowych statystyk. Rsync powie ci, ile było dopasowanych i niepasujących danych.
Peter Cordes

Odpowiedzi:

27

streszczenie

Bazy danych przechowują zwykle wiele metadanych, danych organizacyjnych itp. Bardzo mało prawdopodobne jest, aby wstawka była prostym dodatkiem, tak jak w przypadku pliku tekstowego. Testowanie SQLite pokazuje, że zachowuje się w ten sposób, zarówno w trybie WAL, jak i innym niż WAL. To powoduje, że rsync musi zsynchronizować znacznie więcej danych, niż można by się spodziewać. Możesz nieco zmniejszyć ten narzut, używając niskiej --block-size(kosztem większej liczby obliczeń ogólnych i przesyłania sum kontrolnych).

Lepszym rozwiązaniem jest prawdopodobnie zrzucenie nowych rekordów jako zrzut SQL, skompresowanie go i przesłanie. Alternatywnie, wydaje się, że istnieje kilka rozwiązań replikacji dla SQLite, możesz użyć jednego z nich.

roaima sugeruje, że przy minimalnym minimum można wykonać pełny zrzut SQL, skompresować go gzip --rsyncable, a następnie zsynchronizować. Warto sprawdzić, jak sądzę, czy jest to wystarczająco mała delta.

Detale

To, czego próbujesz, powinno działać. Osobiście dodałbym --partialdo twoich opcji rsync, na wypadek, gdyby w jakiś sposób wykrywa rosnący plik jako częściowy transfer. Możesz także uzyskać lepsze statystyki transferu --stats.

Drugą rzeczą do sprawdzenia jest to, czy SQLite rzeczywiście dotyka tylko kilku stron - szczerze mówiąc, nie zdziwiłbym się, gdyby zapisywał strony w całym pliku. Jednym szybkim sposobem sprawdzenia byłoby użycie cmp -lw dwóch wersjach - sprawdź, czy nastąpiły zmiany na stronach innych niż kilka ostatnich. Pamiętaj, że rsyncidea „strony” / bloku jest inna niż SQLite; możesz zmienić rsync przez --block-size. Zmniejszenie tego może pomóc.

Edycja: Zrobiłem szybki test z SQLite. Nawet przy 32 tysiącach stron, dodając kilka wpisów dziennika zapisanych na każdej stronie. Szczegóły poniżej.

Edycja 2 : Wydaje się, że jest lepiej w trybie WAL, choć nadal bierzesz ogromną ilość kosztów ogólnych, prawdopodobnie z punktu kontrolnego.

Edycja 3 : Im lepiej, tym więcej danych dodajesz na transfer - myślę, że pewnie ciągle zapisuje pewne bloki. Więc przenosisz ten sam zestaw bloków, niezależnie od tego, czy napisał do nich raz czy sto razy.

BTW: Aby zminimalizować transfer, prawdopodobnie możesz zrobić znacznie lepiej niż rsync. Na przykład zrzut nowych rekordów SQL od ostatniego uruchomienia xz --best(lub nawet gzip) transferu byłby prawdopodobnie nieco mniejszy.

Szybki test SQLite

Schemat:

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

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

Było znacznie więcej przykładowych komunikatów w dzienniku (2076).

Sprawdzanie, które strony uległy zmianie:

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) }'
derobert
źródło