Pregunta

Tengo la sensación de que debe haber patrones de sincronización cliente-servidor por ahí. Pero no conseguí google en absoluto.

La situación es bastante simple: el servidor es el nodo central, al que varios clientes se conectan y manipulan los mismos datos. Los datos se pueden dividir en átomos, en caso de conflicto, cualquiera que sea el servidor, tiene prioridad (para evitar que el usuario entre en la resolución de conflictos). Se prefiere la sincronización parcial debido a cantidades de datos potencialmente grandes.

¿Existen patrones / buenas prácticas para tal situación, o si no conoce alguna, cuál sería su enfoque?

A continuación se muestra cómo pienso resolverlo: Paralelamente a los datos, se llevará a cabo un diario de modificación, con todas las transacciones marcadas con la fecha. Cuando el cliente se conecta, recibe todos los cambios desde la última verificación, en forma consolidada (el servidor recorre las listas y elimina las adiciones seguidas de eliminaciones, combina actualizaciones para cada átomo, etc.). Et voila, estamos al día.

La alternativa sería mantener la fecha de modificación para cada registro, y en lugar de realizar eliminaciones de datos, simplemente márquelas como eliminadas.

¿Algún pensamiento?

¿Fue útil?

Solución

Debes ver cómo funciona la gestión distribuida de cambios. Mire SVN, CVS y otros repositorios que administran el trabajo de deltas.

Tienes varios casos de uso.

  • Sincronizar cambios. El enfoque de cambio de registro (o historial delta) se ve bien para esto. Los clientes envían sus deltas al servidor; El servidor consolida y distribuye los deltas a los clientes. Este es el caso típico. Las bases de datos llaman a esta " replicación de transacciones " ;.

  • El cliente ha perdido la sincronización. Ya sea a través de una copia de seguridad / restauración o debido a un error. En este caso, el cliente necesita obtener el estado actual del servidor sin pasar por los deltas. Se trata de una copia de master a detalle, deltas y performance maldita sea. Es una cosa de una sola vez; el cliente esta roto no intente optimizar esto, solo implemente una copia confiable.

  • El cliente es sospechoso. En este caso, debe comparar el cliente con el servidor para determinar si el cliente está actualizado y necesita deltas.

Debe seguir el patrón de diseño de la base de datos (y SVN) de numeración secuencial de cada cambio. De esa manera, un cliente puede realizar una solicitud trivial (" ¿Qué revisión debería tener? & Quot;) antes de intentar sincronizar. Y aún así, la consulta (" Todos los deltas desde 2149 ") es deliciosamente simple para que el cliente y el servidor la procesen.

Otros consejos

Como parte del equipo, hice muchos proyectos que involucraban la sincronización de datos, por lo que debería ser competente para responder esta pregunta.

La sincronización de datos es un concepto bastante amplio y hay demasiado que discutir. Cubre una gama de diferentes enfoques con sus ventajas y desventajas. Aquí está una de las posibles clasificaciones basadas en dos perspectivas: Sincrónica / Asincrónica, Cliente / Servidor / Peer-to-Peer. La implementación de la sincronización depende en gran medida de estos factores, la complejidad del modelo de datos, la cantidad de datos transferidos y almacenados, y otros requisitos. Por lo tanto, en cada caso particular, la elección debe ser a favor de la implementación más simple que cumpla con los requisitos de la aplicación.

Sobre la base de una revisión de las soluciones disponibles en el mercado, podemos delinear varias clases principales de sincronización, diferentes en la granularidad de los objetos sujetos a sincronización:

  • La sincronización de un documento completo o una base de datos se usa en aplicaciones basadas en la nube, como Dropbox, Google Drive o Yandex.Disk. Cuando el usuario edita y guarda un archivo, la nueva versión del archivo se carga completamente en la nube, sobrescribiendo la copia anterior. En caso de conflicto, ambas versiones de los archivos se guardan para que el usuario pueda elegir qué versión es más relevante.
  • La sincronización de pares clave-valor se puede usar en aplicaciones con una estructura de datos simple, donde las variables se consideran atómicas, es decir, no se dividen en componentes lógicos. Esta opción es similar a la sincronización de documentos completos, ya que tanto el valor como el documento pueden sobrescribirse completamente. Sin embargo, desde la perspectiva del usuario, un documento es un objeto complejo compuesto de muchas partes, pero un par clave-valor no es más que una cadena corta o un número. Por lo tanto, en este caso, podemos usar una estrategia más simple de resolución de conflictos, considerando el valor más relevante, si ha sido el último en cambiar.
  • La sincronización de datos estructurados como un árbol o un gráfico se utiliza en aplicaciones más sofisticadas donde la cantidad de datos es lo suficientemente grande como para enviar la base de datos en su totalidad en cada actualización. En este caso, los conflictos deben resolverse a nivel de objetos, campos o relaciones individuales. Nos centramos principalmente en esta opción.

Entonces, captamos nuestro conocimiento en este artículo que creo que podría ser muy útil para todos los interesados ??en el tema = > Sincronización de datos en aplicaciones de iOS basadas en datos básicos ( http://blog.denivip.ru/index.php/2014/04/data-syncing-in-core-data-based-ios-apps/?lang=en )

Lo que realmente necesita es Transformación operativa (OT). Esto puede incluso atender los conflictos en muchos casos.

Esto sigue siendo un área activa de investigación, pero hay implementaciones de varios algoritmos OT alrededor. He estado involucrado en dicha investigación durante varios años, así que avíseme si esta ruta le interesa y estaré encantado de brindarle recursos relevantes.

La pregunta no es muy clara, pero vería bloqueo optimista si fuera tú. Se puede implementar con un número de secuencia que el servidor devuelve para cada registro. Cuando un cliente intenta volver a guardar el registro, incluirá el número de secuencia que recibió del servidor. Si el número de secuencia coincide con lo que hay en la base de datos en el momento en que se recibe la actualización, se permite la actualización y se incrementa el número de secuencia. Si los números de secuencia no coinciden, la actualización no está permitida.

Construí un sistema como este para una aplicación hace unos 8 años, y puedo compartir un par de maneras en que ha evolucionado a medida que el uso de la aplicación ha crecido.

Comencé registrando todos los cambios (insertar, actualizar o eliminar) de cualquier dispositivo en un " historial " mesa. Entonces, si, por ejemplo, alguien cambia su número de teléfono en " contacto " En la tabla, el sistema editará el campo contact.phone y también agregará un registro de historial con action = update, field = phone, record = [ID de contacto], value = [nuevo número de teléfono]. Luego, cuando un dispositivo se sincroniza, descarga los elementos del historial desde la última sincronización y los aplica a su base de datos local. Esto suena como la " replicación de transacciones " patrón descrito anteriormente.

Un problema es mantener las identificaciones únicas cuando se pueden crear elementos en diferentes dispositivos. No sabía sobre los UUID cuando comencé esto, así que utilicé ID de incremento automático y escribí un código complejo que se ejecuta en el servidor central para verificar las nuevas ID cargadas desde los dispositivos, cambiarlas a una ID única en caso de conflicto, y Indique al dispositivo de origen que cambie la ID en su base de datos local. El solo hecho de cambiar las ID de los registros nuevos no fue tan malo, pero si creo, por ejemplo, un nuevo elemento en la tabla de contactos, luego creo un nuevo elemento relacionado en la tabla de eventos, ahora tengo claves externas que también necesito comprobar y actualizar.

Finalmente, supe que los UUID podían evitar esto, pero para entonces mi base de datos se estaba volviendo bastante grande y temía que una implementación completa de UUID creara un problema de rendimiento. Entonces, en lugar de usar UUID completos, comencé a usar claves alfanuméricas de 8 caracteres generadas aleatoriamente como ID, y dejé mi código existente en su lugar para manejar los conflictos. En algún lugar entre mis claves de 8 caracteres actuales y los 36 caracteres de un UUID, debe haber un punto dulce que elimine los conflictos sin un aumento innecesario, pero como ya tengo el código de resolución de conflictos, no ha sido una prioridad experimentar con eso. .

El siguiente problema fue que la tabla de historial era aproximadamente 10 veces más grande que el resto de la base de datos. Esto hace que el almacenamiento sea costoso, y cualquier mantenimiento en la tabla de historial puede ser doloroso. Mantener esa tabla completa permite a los usuarios deshacer cualquier cambio anterior, pero eso comenzó a parecer una exageración. Así que agregué una rutina al proceso de sincronización donde si el elemento del historial que un dispositivo descargó por última vez ya no existe en la tabla de historial, el servidor no le da los elementos del historial reciente, sino que le da un archivo que contiene todos los datos para esa cuenta. Luego agregué un cronjob para eliminar elementos del historial con más de 90 días. Esto significa que los usuarios aún pueden revertir los cambios con menos de 90 días de antigüedad, y si se sincronizan al menos una vez cada 90 días, las actualizaciones serán incrementales como antes. Pero si esperan más de 90 días, la aplicación reemplazará toda la base de datos.

Ese cambio redujo el tamaño de la tabla de historial en casi un 90%, por lo que ahora mantener la tabla de historial solo hace que la base de datos sea el doble de grande en lugar de diez veces más grande. Otra ventaja de este sistema es que la sincronización podría seguir funcionando sin la tabla de historial si es necesario, como si tuviera que hacer un mantenimiento que lo desconectó temporalmente. O podría ofrecer diferentes periodos de tiempo de reversión para cuentas a diferentes precios. Y si hay más de 90 días de cambios para descargar, el archivo completo suele ser más eficiente que el formato incremental.

Si volviera a empezar hoy, me saltearía la verificación de conflictos de ID y solo buscaría una longitud de clave que sea suficiente para eliminar conflictos, con algún tipo de verificación de errores por si acaso. Pero la tabla de historial y la combinación de descargas incrementales para actualizaciones recientes o una descarga completa cuando es necesario ha funcionado bien.

Para la sincronización delta (cambio), puede usar el patrón pubsub para publicar los cambios a todos los clientes suscritos, servicios como botón de empuje puede hacer esto.

Para el reflejo de la base de datos, algunos marcos web usan una mini base de datos local para sincronizar la base de datos del lado del servidor con la local en la base de datos del navegador, se admite la sincronización parcial. Consulte meteror .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top