Mengapa rsync tidak menggunakan transfer delta untuk satu file di jaringan?

15

Saya telah melihat pertanyaan ini dan pertanyaan ini , tetapi mereka tampaknya tidak mengatasi gejala yang saya lihat.

Saya memiliki file log besar (sekitar 600 MB) yang saya coba transfer melalui jaringan seluler. Karena merupakan file log itu hanya ditambahkan ke (meskipun sebenarnya dalam database SQLite dengan hanya INSERT yang dilakukan, sehingga tidak cukup sesederhana itu, tetapi dengan pengecualian dari 4k halaman terakhir (atau mungkin beberapa) file identik setiap kali. Penting bahwa hanya perubahan (dan checksum apa pun yang perlu dikirim) yang benar-benar dapat dikirim, karena koneksi data diukur.

Namun ketika saya melakukan tes di koneksi yang tidak diukur (misalnya hotspot wifi gratis) saya tidak melihat kecepatan atau pengurangan transfer data yang diamati atau dilaporkan. Melalui koneksi WiFi yang lambat saya melihat pada urutan 1MB / s atau kurang, melaporkan bahwa transfer akan memakan waktu hampir 20 menit. Melalui koneksi WiFi yang cepat saya melihat kecepatan yang lebih cepat seragam, tetapi tidak ada laporan speedup, dan upaya kedua untuk mentransfer (yang sekarang harus lebih cepat karena kedua file identik) sekarang tidak menunjukkan perbedaan.

Perintah (dibersihkan untuk menghapus informasi sensitif) yang saya gunakan adalah:

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

Output yang saya dapatkan pada akhirnya terlihat seperti ini:

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

Tidak ada sebutan speedup apa pun.

Saya menduga masalahnya mungkin salah satu dari yang berikut:

  • Saya kehilangan beberapa opsi baris perintah. Namun, membaca ulang halaman manual tampaknya menyarankan bahwa transfer delta diaktifkan secara default: Saya hanya melihat opsi untuk menonaktifkannya.
  • Saya menggunakan rsync over ssh (bahkan pada port non-standar) karena server berada di belakang firewall yang hanya memungkinkan ssh. Saya belum melihat sesuatu yang secara eksplisit mengatakan transfer delta tidak akan berfungsi jika daemon rsync tidak berjalan. Saya mencoba menggunakan notasi "::" alih-alih ":" tetapi halaman manual tidak begitu jelas tentang apa itu "modul", dan perintah saya ditolak karena menentukan modul yang tidak valid.

Saya telah mengesampingkan hal berikut:

  • transfer delta tidak dilakukan pada jaringan lokal. Dihapuskan karena saya mencoba melakukan transfer di internet
  • overhead karena perhitungan checksum. Saya telah melihat perilaku ini baik pada koneksi Wifi yang cepat dan lambat dan kecepatan transfer tampaknya tidak dihitung terikat.
Michael
sumber
1
but with the exception of the last 4k page (or maybe a few) the file is identical each time. Apakah Anda benar-benar memverifikasi itu dengan cmp? Atau lebih baik, dengan xdeltaatau sesuatu? Jika Anda benar-benar ingin memperkecil ukuran transfer, pertahankan versi lama dan baru secara lokal, sehingga Anda dapat menghitung perbedaan biner minimal secara lokal (dengan sesuatu selain rsync) dan kirimkan saja tanpa harus mengirim checksum melalui koneksi terukur. Melakukan ini di tingkat basis data-catatan alih-alih tingkat file biner mungkin lebih baik, seperti yang disarankan derobert.
Peter Cordes
1
Juga, Anda bisa menggunakan rsync --stats, dan juga -v -vuntuk mendapatkan lebih banyak statistik verbose. Rsync akan memberi tahu Anda seberapa banyak data yang cocok dan tidak cocok ada.
Peter Cordes

Jawaban:

27

Ringkasan

Basis data cenderung menyimpan banyak metadata, data organisasi, dll. Sisipan sangat tidak mungkin ditambahkan sederhana, seperti halnya dengan file teks. Menguji SQLite menunjukkan itu berperilaku seperti itu, baik dalam mode WAL dan non-WAL. Ini menyebabkan rsync harus menyinkronkan lebih banyak data daripada yang Anda harapkan. Anda dapat mengurangi overhead ini agak dengan menggunakan rendah --block-size(dengan biaya lebih banyak komputasi overhead dan mentransfer checksum).

Pendekatan yang lebih baik mungkin untuk membuang catatan baru sebagai SQL dump, kompres, dan transfer itu. Atau, tampaknya ada beberapa solusi replikasi untuk SQLite, Anda bisa menggunakan salah satunya.

roaima menyarankan minimal Anda mungkin bisa melakukan SQL dump penuh, kompres menggunakan gzip --rsyncable, dan kemudian rsync itu. Layak untuk dicoba, saya kira, untuk melihat apakah itu delta yang cukup kecil.

Detail

Apa yang Anda coba harus berhasil. Saya pribadi menambahkan --partialke opsi rsync Anda, kalau-kalau entah bagaimana mendeteksi file yang tumbuh sebagai transfer parsial. Anda juga bisa mendapatkan statistik transfer yang lebih baik --stats.

Hal kedua yang perlu diperiksa adalah apakah SQLite benar-benar hanya menyentuh beberapa halaman — jujur, saya tidak akan terkejut jika itu menulis halaman di seluruh file. Satu cara cepat untuk memeriksa adalah dengan menggunakan cmp -ldua versi — lihat apakah ada perubahan pada halaman selain yang terakhir. Ingat bahwa rsyncgagasan tentang "halaman" / blok berbeda dengan SQLite; Anda dapat mengubah rsync via --block-size. Mengurangi itu mungkin bisa membantu.

Sunting: Saya melakukan tes cepat dengan SQLite. Bahkan dengan halaman 32k, menambahkan banyak entri log yang ditulis pada setiap halaman. Detail di bawah.

Sunting 2 : Tampaknya lebih baik dalam mode WAL, meskipun Anda masih mengambil banyak overhead, mungkin dari pos pemeriksaan.

Sunting 3 : Juga lebih baik semakin banyak data yang Anda tambahkan per transfer — saya kira itu mungkin mencoret-coret blok tertentu berulang kali. Jadi, Anda mentransfer serangkaian blok yang sama terlepas dari apakah itu ditulis kepada mereka sekali atau seratus kali.

BTW: Untuk meminimalkan transfer, Anda mungkin bisa melakukan jauh lebih baik daripada rsync. Sebagai contoh, dump SQL dari catatan baru sejak transfer terakhir dijalankan xz --best(atau bahkan gzip) mungkin akan sedikit lebih kecil.

Uji SQLite Cepat

Skema:

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)

Ada lebih banyak contoh pesan log (2076).

Memeriksa halaman mana yang berubah:

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
sumber