Por que o rsync não usa transferências delta para um único arquivo na rede?

15

Eu olhei para essa pergunta e essa pergunta , mas elas não parecem abordar os sintomas que estou vendo.

Eu tenho um arquivo de log grande (cerca de 600 MB) que estou tentando transferir através de uma rede celular. Por ser um arquivo de log, ele é apenas anexado (embora na verdade esteja em um banco de dados SQLite com apenas INSERT sendo executado, portanto, não é tão simples assim, mas com exceção da última página 4k (ou talvez uma poucos) o arquivo é idêntico a cada vez.É importante que apenas as alterações (e quaisquer somas de verificação que precisam ser transmitidas) sejam realmente enviadas, porque a conexão de dados é medida.

No entanto, quando executo um teste em uma conexão não-medida (por exemplo, ponto de acesso Wi-Fi gratuito), não vejo uma aceleração ou transferência de dados reduzida observada ou relatada. Em uma conexão WiFi lenta, vejo na ordem de 1 MB / s ou menos, relatando que a transferência levará quase 20 minutos. Em uma conexão WiFi rápida, vejo uma velocidade mais rápida uniforme, mas nenhum relatório de aceleração e uma segunda tentativa de transferência (que agora deve ser mais rápida porque os dois arquivos são idênticos) agora mostra qualquer diferença.

O comando (higienizado para remover informações confidenciais) que estou usando é:

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

A saída que recebo no final é assim:

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

Não há menção de qualquer tipo de aceleração.

Eu suspeito que o problema pode ser um dos seguintes:

  • Estou faltando alguma opção de linha de comando. No entanto, reler a página do manual parece sugerir que as transferências delta estão ativadas por padrão: só vejo opções para desativá-las.
  • Estou usando o rsync over ssh (mesmo em uma porta não padrão) devido ao servidor estar atrás de um firewall que permite apenas o ssh. Não vi nada explicitamente dizendo que transferências delta não funcionariam se o daemon rsync não estiver sendo executado. Tentei usar a notação "::" em vez de ":", mas a página de manual não é muito clara sobre o que é um "módulo" e meu comando é rejeitado por especificar um módulo inválido.

Eu excluí o seguinte:

  • transferências delta não executadas em uma rede local. Descartado porque estou tentando realizar a transferência pela Internet
  • sobrecarga devido ao cálculo da soma de verificação. Vi esse comportamento em uma conexão Wifi rápida e lenta e a taxa de transferência não parece estar vinculada à computação.
Michael
fonte
1
but with the exception of the last 4k page (or maybe a few) the file is identical each time. Você realmente confirmou isso com cmp? Ou melhor, com xdeltaou algo assim? Se você realmente deseja minimizar o tamanho da transferência, mantenha as versões antiga e nova localmente, para poder calcular um diff binário mínimo localmente (com algo diferente de rsync) e enviá-lo sem precisar enviar somas de verificação pela conexão medida. Fazer isso no nível do registro do banco de dados em vez do nível do arquivo binário provavelmente é ainda melhor, como sugere derobert.
Peter Cordes
1
Além disso, você poderia ter usado rsync --statse também -v -vobter estatísticas ainda mais detalhadas. O Rsync informará a quantidade de dados correspondentes a inigualáveis.
Peter Cordes

Respostas:

27

Sumário

Os bancos de dados tendem a manter muitos metadados, dados organizacionais etc. É muito improvável que uma inserção seja um anexo simples, como seria com um arquivo de texto. O teste do SQLite mostra que ele se comporta dessa maneira, nos modos WAL e não WAL. Isso faz com que o rsync precise sincronizar muito mais dados do que você esperaria. É possível reduzir um pouco essa sobrecarga usando uma baixa --block-size(ao custo de mais sobrecarga de computação e transferência de somas de verificação).

Uma abordagem melhor é provavelmente despejar novos registros como um despejo SQL, compactá-lo e transferi-lo. Como alternativa, parece haver várias soluções de replicação para SQLite, você pode usar uma delas.

O roaima sugere no mínimo que você provavelmente poderia fazer um dump SQL completo, compactá-lo usando gzip --rsyncablee depois sincronizá-lo novamente. Vale a pena testar, suponho, para ver se é um delta pequeno o suficiente.

Detalhes

O que você está tentando deve funcionar. Eu adicionaria pessoalmente --partialàs suas opções de rsync, para o caso de detectar de alguma forma o arquivo crescente como uma transferência parcial. Você também pode obter melhores estatísticas de transferência com --stats.

A segunda coisa a verificar é se o SQLite está realmente tocando apenas algumas páginas - honestamente, não ficaria surpreso se estivesse escrevendo páginas em todo o arquivo. Uma maneira rápida de verificar seria usar cmp -lem duas versões - veja se há alterações nas páginas além das poucas finais. Lembre-se de que rsynca idéia de uma "página" / bloco é diferente da do SQLite; você pode mudar via rsync --block-size. Reduzi-lo pode ajudar.

Edit: Eu fiz um teste rápido com SQLite. Mesmo com 32 mil páginas, adicionando várias entradas de log rabiscadas em todas as páginas. Detalhes abaixo.

Editar 2 : parece ser melhor no modo WAL, embora você ainda tenha uma quantidade enorme de sobrecarga, provavelmente do ponto de verificação.

Editar 3 : também é melhor quanto mais dados você adicionar por transferência - acho que provavelmente rabisca certos blocos repetidamente. Portanto, você está transferindo o mesmo conjunto de blocos, independentemente de ter gravado neles uma ou cem vezes.

BTW: Para minimizar a transferência, você provavelmente pode fazer muito melhor que o rsync. Por exemplo, um despejo de SQL de novos registros desde a última transferência xz --best(ou mesmo gzip) seria provavelmente um pouco menor.

Teste rápido 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)

Havia muito mais mensagens de log de exemplo (2076).

Verificando quais páginas foram alteradas:

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
fonte