Estrategia de clave principal de Android (aplicación distribuida)

Voy a implementar una aplicación distribuida con múltiples clientes móviles y una aplicación de servidor basada en web. Así que cada cliente y también el servidor están autorizados a generar entradas de tabla. Por lo tanto, necesito claves primarias únicas sobre todos los participantes y quiero ser capaz de generar claves sin conexión .

¿Cuál es el mejor enfoque para generar claves primarias que está utilizando en entornos distribuidos? Para una pregunta similar, ¿Cuál es la mejor estrategia de clave primaria para una aplicación móvil multi-cliente en línea / sin conexión con base de datos SQLite y Azure SQL como almacén central?

Soy consciente de que la generación de claves UUID es un buen enfoque para ese escenario, pero quiero mantener una clave con el nombre _id y el tipo largo como sugiere la plataforma Android.

No quiero tener un ID compuesto con dispositivo (también servidor es un dispositivo) id e id local. Este enfoque no funcionaría bien de todos modos ya que el servidor debería ser capaz de generar entradas para un determinado cliente. En ese caso, tendría que utilizar el id de dispositivo también en el servidor.

Por lo tanto, mi favorito actual es construir mi clave con tipo de datos largo (lo hice antes en otro proyecto). Creo que voy a utilizar el enfoque de alta / baja (véase por ejemplo aquí ¿Qué es el algoritmo Hi / Lo? ) Y tienen una clave que consiste en:

  • ID de cliente (por ejemplo, ~ 28 bits) generados desde el servidor
  • Valor bajo (por ejemplo ~ 4 bits) incrementado en el cliente, nunca persistió
  • Alto valor (por ejemplo, ~ 32 bits) incrementado en el cliente, persistió sólo en el cliente

El ID de cliente debe ser recuperado del servidor al inicio de la aplicación móvil. Así que el primer inicio necesita una conexión de red. Esto podría ser un inconveniente de este enfoque. Al tener la identificación del cliente en el dispositivo puedo generar llaves sin una conexión de red.

Normalmente, el ID alto es un valor único sobre la base de datos. Cuando un usuario desinstala la aplicación e instala de nuevo, tengo que tratarlo como un nuevo cliente y tengo que darle un nuevo ID de cliente. De lo contrario tendría que guardar la identificación actual alta en el servidor para poder restaurar en la pérdida o en la reinstalación – no vale la pena el esfuerzo.

¿Cuál es el mejor enfoque para obtener la identificación alta en Android? Una clave de autoincremento no es una solución. Necesito algo como una función de generador. Y tiene que ser ejecutado dentro de su propia transacción (no la transacción "usuario"). ¿Alguien experimenta con ese enfoque en Android y puede alguien apuntarme en la dirección correcta? (Sólo encontré esta respuesta ).

¿Qué estrategia clave está utilizando para su aplicación multi-cliente (en línea y sin conexión)?

Esto es más preguntas entonces respuestas …

Facilita las cosas si puedes generar automáticamente todos los id's, por lo que no tienes que buscarlos desde el servidor y preocuparte por si tienes una conexión. Usted menciona que no puede tomar el enfoque común (UUID o ANDROID_ID) porque va a usar un largo "como lo sugiere la plataforma Android".

¿Se está refiriendo al hecho de que Android asume que sus tablas SQLite tendrán una clave primaria _id larga?

¿Está utilizando un almacén de datos o una base de datos SQL en su servidor?

Si está utilizando un almacén de datos con claves jerárquicas (por ejemplo, almacén de datos de google), ¿qué tal si utiliza UUID / ANDROID_ID como ID de cliente y, a continuación, un identificador de elemento de datos largo. Entonces en el cliente usted apenas almacena el largo, y en el servidor sus entidades se almacenan con una trayectoria dominante de UUID / long.

¿Por qué escribe que el "high id debe ser un valor único sobre la base de datos"? Dado que se prepended con el ID de cliente, tal vez usted quiere decir que debe ser único en la base de datos local?

Para manejar el problema de que el usuario podría desinstalar y reinstalar la aplicación, ¿por qué no perseguir su idea de "guardar la identificación actual alta en el servidor para poder restaurarlo en caso de pérdida o reinstalación". Puesto que ya planeas recuperar el ID de cliente en la primera ejecución (y no puedes asignar los identificadores hasta que lo tengas), también podrías pedir al servidor la siguiente identificación alta disponible.

¿Sus entidades tienen algún otro material clave tal que podría generar un hash de 32 bits de ese material para su alta identificación? Suponiendo que la identificación alta sólo necesita ser única en un cliente en particular (y suponiendo que no tendrá un número masivo de entidades en un cliente), entonces creo que nunca tendría una colisión si usted tiene material clave decente y utilizar un hash Función que minimiza las colisiones.

Déjeme ver si consigo esto recto: usted necesita un número de 32 pedacitos que sea único al dispositivo? De acuerdo:

  1. Cree el número de forma aleatoria o por hash el nanotime actual. Eso te llevará una cadena bastante singular como es.
  2. A continuación, pregunte al servidor si ese número ya se ha utilizado. Si lo tiene, genere el número de nuevo y vuelva a preguntar.

Si hash el nanotime, es tan prácticamente imposible (no es totalmente imposible, la resistencia a la colisión no es prueba de colisión) para obtener el mismo número. Dado el resto de su cadena, que lo haría totalmente único. Este método no requiere interacciones con el servidor hasta que realmente necesita utilizar el servidor. Digamos que el cliente no está conectado al inicio: genere el número, guárdelo y, cuando se conecte, antes de que ocurra nada más, compruebe si el dispositivo existe. Si lo hace, empiece de cero. De esta manera puede obtener un ID de dispositivo realmente único.

No hay manera de saber con certeza que las claves que está generando en el cliente son únicas en el DB de servidor hasta que se comunique con el servidor.

Si se comunica por adelantado con el servidor, antes de crear cualquier registro en el lado del cliente, puede reservar un rango de claves en el servidor. Por ejemplo, el servidor podría distribuir las claves en lotes de 10.000. El cliente se comunica con el servidor y reserva el inicio del siguiente lote de claves disponibles, digamos 60.000. El cliente es entonces libre de crear registros con ids de 60.000 a 69.999. Una vez que el cliente se queda sin llaves, necesita solicitar una nueva gama de claves. Si todos los clientes y el servidor reservan las claves para sí mismos de este modo, todos los identificadores generados serán únicos en la base de datos del servidor.

Ahora, si crea registros en el lado del cliente antes de comunicarse con el servidor, entonces se quedaría atorado teniendo que corregir esos identificadores una vez que obtenga un rango reservado del servidor para que se encuentren dentro de ese rango antes de sincronizarlos con el servidor .

No estoy seguro de por qué usted también está tratando de incluir un ID de cliente en la clave; El servidor está asignando el valor alto, y esto es suficiente para obtener las claves únicas generadas en el cliente.

Desde mi experiencia: use IDs locales en el dispositivo y separe IDs en el servidor . Cada vez que se comunican datos a través del cable, se convierten de uno a otro. Esto aclarará el proceso y facilitará la depuración. Las rutinas de conversión permanecen pequeñas, están bien aisladas y representan un elemento natural en la arquitectura de la aplicación. Los datos que viajan por el cable se espera que sea relativamente pequeño, de todos modos, y la conversión de ID no será una gran sobrecarga. Además, la cantidad de datos que se mantienen en el dispositivo móvil es, presumiblemente, pequeña (el volumen está en el servidor).

Propongo hacer la conversión en el dispositivo con una simple tabla local_ID <-> server_ID . El servidor sólo debe proporcionar un procedimiento: generar un lote de claves, digamos 444 nuevas claves, que presumiblemente el dispositivo móvil asignará a sus IDs locales y enviará datos al servidor sólo con ID_servidor. La tabla de conversión puede ser eliminada de vez en cuando de ID sin usar, y los IDs locales pueden ser reutilizados, enteros de 32 bits serán suficientes.

Motivación

Las tablas se mantienen pequeñas, la implementación se mantiene óptima a la arquitectura del dispositivo nativo, aislada de cambios arquitectónicos impredecibles en otros lugares y hay un buen punto para depurar y rastrear, a través del cual pasan todos los datos.

Tenía una aplicación regenerar todos los IDs en cada archivo de datos guardar y cargar. Fue inesperadamente simple, rápido y abrió otras posibilidades elegantes como la desfragmentación y la consolidación de ID-space.

En su caso, puede cambiar la tecnología del servidor con cambios mínimos en la aplicación cliente. Dado que el cliente puede operar sin conexión de todos modos, podría utilizar sólo los ID locales en la mayoría de las funciones. Sólo el módulo de sincronización buscaría y convertiría los ID de servidor.

He ofrecido dos primas sobre esta cuestión y no encontré la respuesta que busco. Pero pasé un tiempo pensando en la mejor solución y tal vez la pregunta no era lo suficientemente abierta y se centró mucho en la solución que tenía en mente.

Sin embargo, hay un montón de diferentes estrategias disponibles, ahora (después de la segunda recompensa) Creo que la primera pregunta para responder es qué modelo (s) de datos que tiene en su entorno distribuido? Es posible que usted tenga

  1. El mismo (o un subconjunto) modelo de datos en el cliente y el servidor
  2. Modelo de datos cliente differnet y modelo de datos del servidor

Si responde con 1) entonces usted puede elegir para su estrategia clave de

  • Utilizando GUID
  • Usando mi enfoque Alto / Bajo
  • Mapeo de claves como @ user3603546 sugerido

Si respondes con 2) entonces sólo viene lo siguiente en mi mente

  • ID compuesto

Nunca me gustó id compuesto, pero cuando pienso en ello (y no lo llaman id de compuesto de todos modos), entonces podría ser una posible solución. A continuación quiero dibujar esta solución:

Glosario:

  • <Clave del cliente> … clave primaria generada en el lado del cliente, por lo que el cliente elige la implementación (largo _id para Android)
  • <Clave del servidor> … clave primaria generada en el lado del servidor, por lo que el servidor elige la implementación
  • <ID de cliente> … ID para identificar el cliente
  • <ID de dispositivo> … ID para identificar el dispositivo, existe una relación 1-n entre el cliente y el dispositivo

Solución:

  • Úselo sólo si tiene un modelo de datos de cliente y un modelo de datos de servidor
  • El modelo de datos del cliente tiene los campos
    • <Clave del cliente> clave principal
    • <Clave del servidor> campo de datos anulable
  • El modelo de datos del servidor tiene los campos
    • <Clave del servidor> como clave principal
    • <Clave de cliente> campo de datos anulable
    • <Id cliente> como campo de datos obligatorio para distinguir el cliente
  • Al sincronizar desde el servidor al cliente, genere la <clave del cliente> que falta en el cliente y marque la entrada como sucia (de modo que la identificación del cliente llegue al servidor al final del día)
  • Al sincronizar desde el cliente al servidor, genere la <clave del servidor> que falta en el servidor antes de guardarla
  • El mapeo entre el modelo de datos cliente y servidor puede ser manejado por marcos especializados como Dozer o Orika , sin embargo, la generación de claves debe ser integrada al realizar la asignación.

Nunca me ha gustado esta solución porque siempre he pensado en los términos del modelo de datos de servidor. Tengo entidades que viven sólo en el servidor y siempre quise crear estas entidades en el cliente que no sería posible. Pero cuando pienso en el modelo de datos del cliente podría tener una entidad, por ejemplo. Producto que da como resultado dos entidades (Product y ClientProduct) en el servidor.

  • GetWritableDatabase llamado recursivamente
  • Android: ¿Dónde se almacenan los archivos de base de datos?
  • Ejemplos de proyectos sobre la copia de seguridad del archivo y las fotos de Sqlite en Google Drive mediante programación en Android
  • Android usando SQLCipher - ¿cómo se puede descifrar?
  • Android - Observer DB cambia con GreenDao ORM
  • Uso de la palabra clave IN en android sqlite
  • Android cómo obtener el elemento seleccionado de data spinner
  • ¿Cuál es el tamaño máximo de la base de datos SQLite en Android?
  • Android sqlite rollback
  • SQLiteConstraintException: código de error 19: falla de restricción
  • Evite borrar los datos de la base de datos al hacer clic en el botón "borrar datos" en la configuración de la aplicación en el dispositivo Android
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.