Чому rsync не використовує передачу дельти для одного файлу в мережі?


15

Я розглянув це питання і це питання , але, схоже, вони не вирішують симптомів, які я бачу.

У мене великий файл журналу (близько 600 Мб), який я намагаюся перенести через стільникову мережу. Тому що це файл журналу він просто додається до (хоча це насправді в SQLite базі даних тільки з INSERT виконується, так що це не зовсім так просто, але за винятком останньої сторінки 4K (або , може бути, небагато) файл щоразу ідентичний. Важливо, що насправді надсилаються лише ті зміни (і те, що потрібно перевіряти), тому що з'єднання даних вимірюється.

Однак, коли я виконую тест через немереговане з'єднання (наприклад, безкоштовна точка доступу до Wi-Fi), я не бачу спостереження за швидкістю або зменшенням передачі даних. Через повільне підключення до WiFi я бачу порядку 1 Мб / с або менше, повідомляючи, що передача триватиме майже 20 хвилин. Через швидке підключення Wi-Fi я бачу рівномірну швидкість, але жодного звіту про швидкість, і друга спроба передачі (яка тепер повинна бути швидшою, оскільки два файли однакові) тепер не показує різниці.

Команда (дезінфікована для видалення конфіденційної інформації), яку я використовую:

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

Вихід, який я отримую в кінці, виглядає приблизно так:

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

Немає жодної згадки про швидкість.

Я підозрюю, що проблема може бути однією з таких:

  • Я відсутній варіант командного рядка. Однак, повторне читання довідкової сторінки, схоже, говорить про те, що передача дельти вмикається за замовчуванням: я бачу лише варіанти їх відключення.
  • Я використовую rsync через ssh (навіть на нестандартному порту) через те, що сервер знаходиться за брандмауером, який дозволяє лише ssh. Я не бачив нічого прямо сказаного, що передача дельти не буде працювати, якщо демон rsync не працює. Я спробував використати нотацію "::" замість ":", але на сторінці man не дуже зрозуміло, що таке "модуль", і моя команда відхилена для введення недійсного модуля.

Я виключив таке:

  • дельта передачі, які не здійснюються в локальній мережі. Виключено, тому що я намагаюся виконати передачу через Інтернет
  • накладні витрати через розрахунок контрольної суми. Я бачив таку поведінку як на швидкому, так і повільному Wi-Fi-зв’язку, і швидкість передачі даних, здається, не обмежена.

1
but with the exception of the last 4k page (or maybe a few) the file is identical each time. Ви насправді це підтвердили cmp? Або краще, з xdeltaчи чимсь? Якщо ви дійсно хочете мінімізувати розмір передачі, зберігайте стару та нову версії локально, щоб ви могли обчислити мінімальний бінарний розріз локально (з чимось іншим, ніж rsync), і просто надіслати це, не надсилаючи контрольні суми через дозований з’єднання. Робити це на рівні запису бази даних замість рівня бінарних файлів, мабуть, ще краще, як пропонує дероберт.
Пітер Кордес

1
Також ви могли використати rsync --stats, а також -v -vотримати ще більш детальну статистику. Rsync підкаже вам, скільки було зіставлених та неперевершених даних.
Пітер Кордес

Відповіді:


27

Підсумок

Бази даних, як правило, зберігають багато метаданих, організаційних даних тощо. Вкладиш навряд чи буде простим додатком, як це було б із текстовим файлом. Тестування 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-сторінковими сторінками, додаючи купу записів журналу на кожній сторінці. Деталі нижче.

Редагування 2 : Схоже, це краще в режимі WAL, хоча ви все ще приймаєте величезну кількість накладних витрат, ймовірно, з пункту пропуску.

Редагувати 3 : Краще також більше даних, які ви додаєте за передачу - я думаю, що певно переписує певні блоки знову і знову. Таким чином, ви передаєте той самий набір блоків незалежно від того, писав він їх один раз чи сто разів.

BTW: Для мінімізації передачі ви, ймовірно, можете зробити набагато краще, ніж rsync. Наприклад, дамп SQL нових записів з моменту останньої передачі через xz --best(або навіть gzip), ймовірно, буде трохи меншим.

Швидкий тест 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) }'
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.