Pergunta

Eu tenho um sentimento que deve haver padrões de sincronização de cliente-servidor lá fora. Mas eu falhou totalmente ao google-se um.

Situação é bastante simples - servidor é o nó central, que vários clientes se conectar e manipular mesmos dados. Os dados podem ser divididos em átomos, em caso de conflito, o que está em servidor, tem prioridade (para evitar usuário em resolução de conflitos). sincronização parcial é preferida devido à potencialmente grandes quantidades de dados.

Existem padrões / boas práticas para tal situação, ou se você não sabe de qualquer - o que seria a sua abordagem

A seguir é como eu agora pensar para resolvê-lo: Paralelo aos dados, um jornal modificação será realizada, tendo todas as transações timestamped. Quando se conecta cliente, que recebe todas as alterações desde a última verificação, em forma consolidada (servidor passa por listas e remove os acréscimos que são seguidos por exclusões, atualizações Mescla para cada átomo, etc.). Et voila, que são até à data.

Alternativa seria manter data de modificação para cada registro, e em vez de executar exclui dados, basta marcá-los como excluída.

Qualquer pensamento?

Foi útil?

Solução

Você deve olhar como funciona o gerenciamento de mudanças distribuído. Olhe para SVN, CVS e outros repositórios que gerenciam deltas de trabalho.

Você tem vários casos de uso.

  • Sincronizar alterações. Sua abordagem change-log (ou história delta) parece ser bom para isso. Os clientes enviam seus deltas para o servidor; consolida servidores e distribui os deltas para os clientes. Este é o caso típico. Bases de dados chamam isso de "replicação de transação".

  • Cliente tem sincronização perdida. Seja através de um backup / restore ou por causa de um bug. Neste caso, o cliente precisa para obter o estado atual do servidor sem passar pelos deltas. Esta é uma cópia do mestre para o detalhe, deltas e desempenho que se dane. É uma coisa de uma só vez; o cliente está quebrada; não tentar otimizar isso, basta implementar uma cópia confiável.

  • cliente é suspeito. Neste caso, você precisa comparar cliente contra o servidor para determinar se o cliente é up-to-date e precisa de quaisquer deltas.

Você deve seguir o banco de dados (e SVN) padrão de design de numeração sequencial cada mudança. Dessa forma, um cliente pode fazer um pedido trivial ( "Que revisão devo ter?") Antes de tentar sincronizar. E mesmo assim, a consulta ( "Todos os deltas desde 2149") é deliciosamente simples para o cliente e servidor para processo.

Outras dicas

Como parte da equipe, eu fiz um monte de projetos que os dados envolvidos sincronização, por isso deve ser competente para responder a esta pergunta.

Os dados de sincronização é um conceito bastante amplo e há demais para discutir. Ele cobre uma gama de diferentes abordagens com as suas vantagens e desvantagens. Aqui está uma das possíveis classificações baseadas em duas perspectivas: Synchronous / Asynchronous, cliente / servidor / peer-to-peer. Sincronizando implementação é severamente dependentes destes factores, a complexidade do modelo de dados, a quantidade de dados transferidos e armazenados, e outros requisitos. Assim, em cada caso específico, a escolha deve ser a favor da implementação mais simples atender aos requisitos de aplicativos.

Com base na revisão de soluções existente off-the-shelf, podemos delinear várias grandes classes de sincronização, diferente na granularidade dos objetos sujeitos a sincronização:

  • A sincronização de um documento inteiro ou banco de dados é usado em aplicações baseadas na nuvem, como o Dropbox, Google Drive ou Yandex.Disk. Quando o usuário edita e salva um arquivo, a nova versão do arquivo é enviado para a nuvem completamente, substituindo a cópia anterior. Em caso de conflito, ambas as versões de arquivos são salvos para que o usuário pode escolher qual versão é mais relevante.
  • A sincronização de pares chave-valor pode ser usado em aplicativos com uma estrutura de dados simples, onde as variáveis ??são consideradas atômica, ou seja, não dividido em componentes lógicos. Esta opção é semelhante à sincronização de documentos inteiros, tanto como o valor eo documento pode ser substituído por completo. No entanto, a partir de uma perspectiva do usuário um documento é um objeto complexo composto de muitas partes, mas um par chave-valor é apenas uma pequena corda ou um número. Portanto, neste caso, podemos usar uma estratégia mais simples de resolução de conflitos, considerando o valor mais relevante, se tiver sido o último a mudança.
  • A sincronização de dados estruturados como uma árvore ou um gráfico é usado em aplicações mais sofisticadas, onde a quantidade de dados é grande o suficiente para enviar o banco de dados em sua totalidade a cada atualização. Neste caso, os conflitos devem ser resolvidos no nível de objetos individuais, campos ou relacionamentos. Estamos focados principalmente sobre esta opção.

Então, nós pegamos nosso conhecimento neste artigo que eu acho que pode ser muito útil para todos os interessados ??no tema => Dados Sincronização no Core Data aplicativos baseados em iOS ( http://blog.denivip.ru/index.php/ 2014/04 / dados em sincronizar-in-core-data-ios baseados em apps /? lang = en )

O que você realmente precisa é Operacional Transform (OT). Isso pode até mesmo servir para os conflitos em muitos casos.

Esta é ainda uma área ativa de pesquisa, mas existem implementações de diversos algoritmos OT ao redor. Eu estive envolvido em tal pesquisa para um número de anos agora, então deixe-me saber se este percurso interesse e eu vou ser feliz para colocá-lo para recursos relevantes.

A questão não é clara, mas eu olhar para otimista bloqueio se eu fosse vocês. Ele pode ser implementado com um número de seqüência que o servidor retorna para cada registro. Quando um cliente tenta salvar a volta registro, ele irá incluir o número de seqüência que recebeu do servidor. Se o número de sequência coincide com o que está no banco de dados no momento em que a atualização é recebida, a atualização é permitido e o número de seqüência é incrementado. Se os números de sequência não corresponderem, a atualização não é permitido.

Eu construí um sistema como este para uma taxa de cerca de 8 anos atrás, e eu posso compartilhar algumas maneiras ele evoluiu como o uso de aplicativos cresceu.

Eu comecei registrando cada mudança (inserção, atualização ou exclusão) a partir de qualquer dispositivo em uma tabela "história". Assim, se, por exemplo, alguém muda seu número de telefone na tabela de "contato", o sistema irá editar o campo contact.phone, e também adicionar um registro histórico com action = atualização, field = telefone, ficha = [ID de contato], valor = [novo número de telefone]. Em seguida, sempre que um dispositivo sincroniza, ele baixa os itens do histórico desde a última sincronização e aplica-los para seu banco de dados local. Isso soa como o padrão "transação replicação" descrito acima.

Uma questão é manter IDs único quando itens podem ser criados em diferentes dispositivos. Eu não sabia sobre UUIDs quando eu comecei isso, então eu usei IDs de autoincremento e escreveu algum código complicado que é executado no servidor central para verificar novas IDs enviados a partir de dispositivos, alterá-las para uma identificação única, se houver um conflito, e dizer o dispositivo de origem para mudar o ID em seu banco de dados local. Apenas mudando os IDs de novos registros não era tão ruim, mas se eu criar, por exemplo, um novo item na tabela de contato, em seguida, criar um novo item relacionado na tabela de eventos, agora eu tenho as chaves estrangeiras que também precisam verificar e atualização.

Eventualmente eu aprendi que UUIDs poderia evitar isso, mas então meu banco de dados estava ficando muito grande e eu estava com medo de uma implementação UUID completa criaria um problema de desempenho. Então, em vez de usar UUIDs completos, eu comecei a usar teclas alfanuméricas 8 caracteres gerados aleatoriamente, como IDs, e eu deixei meu código existente no local para conflitos punho. Em algum lugar entre meus atuais chaves de 8 caracteres e os 36 caracteres de um UUID deve haver um ponto doce que eliminaria os conflitos sem inchaço desnecessário, mas desde que eu já tenho o código de resolução de conflitos, não tem sido uma prioridade para experimentar com que .

O próximo problema era que a tabela de histórico foi cerca de 10 vezes maior do que todo o resto do banco de dados. Isso torna o armazenamento caro, e qualquer manutenção na mesa da história pode ser doloroso. Mantendo-se que toda a tabela permite aos usuários reverter qualquer alteração anterior, mas que começou a se sentir como um exagero. Então eu adicionei uma rotina para o processo de sincronização, onde se o item história que um último dispositivo baixado não existe mais na tabela de histórico, o servidor não dar-lhe os itens história recente, mas em vez disso dá-lhe um arquivo contendo todos os dados para essa conta. Então eu adicionei um cron para itens Excluir Histórico de mais de 90 dias. Isso significa que os usuários podem ainda reverter as alterações menos de 90 dias de idade, e se eles sincronizar pelo menos uma vez a cada 90 dias, as atualizações serão incrementais como antes. Mas se eles aguardar mais de 90 dias, o aplicativo irá substituir todo o banco de dados.

Essa mudança reduziu o tamanho da tabela de história em quase 90%, então agora manter a tabela de histórico só torna o banco de dados duas vezes maior, em vez de dez vezes tão grande. Outra vantagem deste sistema é que a sincronização ainda podia trabalhar sem a tabela de história, se necessário - como se eu precisava fazer alguma manutenção que levou-lo offline temporariamente. Ou eu poderia oferecer diferentes períodos de tempo de reversão para contas em pontos diferentes do preço. E se há mais de 90 dias de mudanças para download, o arquivo completo é geralmente mais eficiente do que o formato incremental.

Se eu estivesse começando de novo hoje, eu ignorar a verificação de ID conflito e apenas apontar para um comprimento de chave que é suficiente para eliminar os conflitos, com algum tipo de verificação de erro apenas no caso. Mas a tabela de histórico ea combinação de transferências incrementais para atualizações recentes ou um download completo quando necessário tem vindo a trabalhar bem.

Para delta (alteração) sync, você pode usar o padrão pubsub publicar alterações de volta para todos os clientes inscritos, serviços como empurrador pode fazer isso.

Para espelho de banco de dados, alguns frameworks web usar um mini banco de dados local ao banco de dados do lado do servidor de sincronização ao local no banco de dados do navegador, sincronização parcial é suportado. Verifique meteror .

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top