¿Por qué rsync no utiliza transferencias delta para un solo archivo en una red?

15

He examinado esta pregunta y esta pregunta , pero no parecen abordar los síntomas que estoy viendo.

Tengo un archivo de registro grande (alrededor de 600 MB) que estoy tratando de transferir a través de una red celular. Debido a que es un archivo de registro al que solo se agrega (aunque en realidad está en una base de datos SQLite con solo INSERTAR, por lo que no es tan simple como eso, pero con la excepción de la última página de 4k (o tal vez un pocos) el archivo es idéntico cada vez. Es importante que solo se envíen los cambios (y las sumas de verificación que deban transmitirse) porque la conexión de datos se mide.

Sin embargo, cuando realizo una prueba a través de una conexión no medida (por ejemplo, punto de acceso wifi gratuito) no veo una transferencia de datos acelerada o reducida observada o informada. A través de una conexión WiFi lenta, veo en el orden de 1 MB / so menos, informando que la transferencia tomará casi 20 minutos. A través de una conexión WiFi rápida, veo una velocidad uniforme más rápida, pero no hay informes de aceleración, y un segundo intento de transferencia (que ahora debería ser más rápido porque los dos archivos son idénticos) ahora no muestra ninguna diferencia.

El comando (desinfectado para eliminar información confidencial) que estoy usando es:

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

El resultado que obtengo al final se ve así:

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

No se menciona ningún tipo de aceleración.

Sospecho que el problema puede ser uno de los siguientes:

  • Me falta alguna opción de línea de comando. Sin embargo, releer la página del manual parece sugerir que las transferencias delta están habilitadas de manera predeterminada: solo veo opciones para deshabilitarlas.
  • Estoy usando rsync sobre ssh (incluso en un puerto no estándar) debido a que el servidor está detrás de un firewall que solo permite ssh. Sin embargo, no he visto nada que diga explícitamente que las transferencias delta no funcionarán si el demonio rsync no se está ejecutando. Intenté usar la notación "::" en lugar de ":" pero la página del manual no tiene muy claro qué es un "módulo", y mi comando es rechazado por especificar un módulo no válido.

He descartado lo siguiente:

  • transferencias delta no realizadas en una red local. Se descartó porque estoy intentando realizar la transferencia a través de Internet
  • gastos generales debido al cálculo de la suma de verificación. He visto este comportamiento tanto en una conexión Wifi rápida como lenta y la velocidad de transferencia no parece estar vinculada al cálculo.
Miguel
fuente
1
but with the exception of the last 4k page (or maybe a few) the file is identical each time. ¿Realmente verificaste eso con cmp? O mejor, con xdeltao algo? Si realmente desea minimizar el tamaño de transferencia, conserve las versiones antiguas y nuevas localmente, de modo que pueda calcular una diferencia binaria mínima localmente (con algo diferente a rsync) y simplemente enviarla sin tener que enviar sumas de verificación a través de la conexión medida. Hacer esto en el nivel de registro de base de datos en lugar del nivel de archivo binario es probablemente aún mejor, como sugiere derobert.
Peter Cordes
1
Además, podría haber usado rsync --stats, y también -v -vpara obtener estadísticas aún más detalladas. Rsync le dirá cuántos datos coincidentes y no coincidentes había.
Peter Cordes

Respuestas:

27

Resumen

Las bases de datos tienden a mantener una gran cantidad de metadatos, datos organizacionales, etc. Es muy improbable que una inserción sea un simple agregado, como lo sería con un archivo de texto. La prueba de SQLite muestra que se comporta de esa manera, tanto en modo WAL como no WAL. Esto lleva a que rsync tenga que sincronizar muchos más datos de los que cabría esperar. Puede reducir esta sobrecarga de alguna manera utilizando un bajo --block-size(a costa de más computación de sobrecarga y transferencia de sumas de verificación).

Un mejor enfoque es probablemente volcar nuevos registros como un volcado de SQL, comprimirlo y transferirlo. Alternativamente, parece haber varias soluciones de replicación para SQLite, podría usar una de ellas.

Roaima sugiere, como mínimo, que probablemente podría hacer un volcado de SQL completo, comprimirlo usando gzip --rsyncabley luego rsync eso. Vale la pena probarlo, supongo, para ver si es un delta lo suficientemente pequeño.

Detalles

Lo que estás intentando debería funcionar. Personalmente agregaría --partiala sus opciones de rsync, en caso de que de alguna manera detecte el archivo creciente como una transferencia parcial. También puede obtener mejores estadísticas de transferencia con --stats.

La segunda cosa a verificar es si SQLite realmente solo toca unas pocas páginas; honestamente, no me sorprendería si está escribiendo páginas en todo el archivo. Una forma rápida de verificar sería usar cmp -ldos versiones: ver si hay cambios en las páginas que no sean las últimas. Recuerde que rsyncla idea de una "página" / bloque es diferente a la de SQLite; puede cambiar a través de rsync --block-size. Reducirlo podría ayudar.

Editar: hice una prueba rápida con SQLite. Incluso con 32k páginas, agregando un montón de entradas de registro garabateadas en cada página. Detalles abajo.

Edición 2 : parece ser mejor en el modo WAL, aunque todavía se necesita una gran cantidad de gastos generales, probablemente desde el punto de control.

Edición 3 : también es mejor cuanto más datos agregue por transferencia, supongo que probablemente garabatea ciertos bloques una y otra vez. Entonces, está transfiriendo ese mismo conjunto de bloques independientemente de si les escribió una o cien veces.

Por cierto: para minimizar la transferencia, probablemente puedas hacerlo mucho mejor que rsync. Por ejemplo, un volcado de SQL de nuevos registros desde la última transferencia xz --best(o incluso gzip) probablemente sería un poco más pequeño.

Prueba rápida de SQLite

Esquema:

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

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

Hubo muchos más mensajes de registro de ejemplo (2076).

Comprobando qué páginas cambiaron:

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
fuente