Sé que hay preguntas similares aquí, pero me están diciendo que vuelva a los sistemas RDBMS normales si necesito transacciones o uso operaciones atómicas o confirmación en dos fases . La segunda solución parece la mejor opción. El tercero no deseo seguirlo porque parece que muchas cosas podrían salir mal y no puedo probarlo en todos los aspectos. Me está costando refactorizar mi proyecto para realizar operaciones atómicas. No sé si esto proviene de mi punto de vista limitado (hasta ahora solo he trabajado con bases de datos SQL), o si realmente no se puede hacer.
Nos gustaría probar piloto MongoDB en nuestra empresa. Hemos elegido un proyecto relativamente simple: una puerta de enlace SMS. Permite que nuestro software envíe mensajes SMS a la red celular y la puerta de enlace hace el trabajo sucio: en realidad se comunica con los proveedores a través de diferentes protocolos de comunicación. El portal también gestiona la facturación de los mensajes. Cada cliente que solicita el servicio tiene que comprar algunos créditos. El sistema disminuye automáticamente el saldo del usuario cuando se envía un mensaje y niega el acceso si el saldo es insuficiente. Además, debido a que somos clientes de proveedores de SMS de terceros, también podemos tener nuestros propios saldos con ellos. Tenemos que hacer un seguimiento de esos también.
Empecé a pensar en cómo puedo almacenar los datos requeridos con MongoDB si reduzco cierta complejidad (facturación externa, envío de SMS en cola). Viniendo del mundo SQL, crearía una tabla separada para usuarios, otra para mensajes SMS y otra para almacenar las transacciones relacionadas con el saldo de los usuarios. Digamos que creo colecciones separadas para todos aquellos en MongoDB.
Imagine una tarea de envío de SMS con los siguientes pasos en este sistema simplificado:
verificar si el usuario tiene saldo suficiente; negar el acceso si no hay suficiente crédito
envíe y almacene el mensaje en la colección de SMS con los detalles y el costo (en el sistema en vivo el mensaje tendría un
status
atributo y una tarea lo recogería para la entrega y establecería el precio del SMS de acuerdo con su estado actual)disminuir el saldo de los usuarios por el costo del mensaje enviado
registrar la transacción en la colección de transacciones
¿Cuál es el problema con eso? MongoDB puede hacer actualizaciones atómicas solo en un documento. En el flujo anterior, podría ocurrir que algún tipo de error se deslice y el mensaje se almacene en la base de datos, pero el saldo del usuario no se actualiza y / o la transacción no se registra.
Se me ocurrieron dos ideas:
Cree una colección única para los usuarios y almacene el saldo como un campo, transacciones relacionadas con el usuario y mensajes como subdocumentos en el documento del usuario. Debido a que podemos actualizar los documentos atómicamente, esto realmente resuelve el problema de la transacción. Desventajas: si el usuario envía muchos mensajes SMS, el tamaño del documento podría aumentar y alcanzar el límite de 4 MB. Tal vez pueda crear documentos históricos en tales escenarios, pero no creo que sea una buena idea. Además, no sé qué tan rápido sería el sistema si inserto cada vez más datos en el mismo documento grande.
Cree una colección para usuarios y otra para transacciones. Puede haber dos tipos de transacciones: compra de crédito con cambio de saldo positivo y mensajes enviados con cambio de saldo negativo. La transacción puede tener un subdocumento; por ejemplo, en los mensajes enviados, los detalles del SMS se pueden incrustar en la transacción. Desventajas: no almaceno el saldo actual del usuario, así que tengo que calcularlo cada vez que un usuario intenta enviar un mensaje para saber si el mensaje podría pasar o no. Me temo que este cálculo puede volverse lento a medida que aumenta el número de transacciones almacenadas.
Estoy un poco confundido acerca de qué método elegir. ¿Hay otras soluciones? No pude encontrar ninguna de las mejores prácticas en línea sobre cómo solucionar este tipo de problemas. Supongo que muchos programadores que intentan familiarizarse con el mundo NoSQL se enfrentan a problemas similares al principio.
fuente
Respuestas:
A partir de 4.0, MongoDB tendrá transacciones ACID de documentos múltiples. El plan es habilitar primero a aquellos en implementaciones de conjunto de réplicas, seguidas de los clústeres fragmentados. Las transacciones en MongoDB se sentirán como si los desarrolladores de transacciones estuvieran familiarizados con las bases de datos relacionales: serán de múltiples declaraciones, con semántica y sintaxis similares (me gusta
start_transaction
ycommit_transaction
). Es importante destacar que los cambios en MongoDB que permiten las transacciones no afectan el rendimiento de las cargas de trabajo que no los requieren.Para más detalles ver aquí .
Tener transacciones distribuidas no significa que deba modelar sus datos como en bases de datos relacionales tabulares. Aproveche el poder del modelo de documento y siga las buenas y recomendadas prácticas de modelado de datos.
fuente
Viviendo sin transacciones
Las transacciones admiten propiedades ACID , pero aunque no hay transacciones
MongoDB
, sí tenemos operaciones atómicas. Bueno, las operaciones atómicas significan que cuando trabajas en un solo documento, ese trabajo se completará antes de que alguien más vea el documento. Verán todos los cambios que hicimos o ninguno de ellos. Y utilizando operaciones atómicas, a menudo puede lograr lo mismo que hubiéramos logrado utilizando transacciones en una base de datos relacional. Y la razón es que, en una base de datos relacional, necesitamos realizar cambios en varias tablas. Por lo general, las tablas que deben unirse y, por lo tanto, queremos hacer todo de una vez. Y para hacerlo, dado que hay varias tablas, tendremos que comenzar una transacción y hacer todas esas actualizaciones y luego finalizar la transacción. Pero conMongoDB
, vamos a incrustar los datos, ya que vamos a unirlos previamente en documentos y son estos documentos ricos que tienen jerarquía. A menudo podemos lograr lo mismo. Por ejemplo, en el ejemplo de blog, si quisiéramos asegurarnos de actualizar atómicamente una publicación de blog, podemos hacerlo porque podemos actualizar toda la publicación de blog de una vez. Donde como si se tratara de un montón de tablas relacionales, probablemente tendríamos que abrir una transacción para poder actualizar la colección de publicaciones y la colección de comentarios.Entonces, ¿cuáles son nuestros enfoques que podemos adoptar
MongoDB
para superar la falta de transacciones?Update
Las operacionesfindAndModify
,$addToSet
(dentro de una actualización) y$push
(dentro de una actualización) operan atómicamente dentro de un solo documento.fuente
Mira esto , por Tokutek. Desarrollan un complemento para Mongo que promete no solo transacciones, sino también un aumento en el rendimiento.
fuente
Llévelo al punto: si la integridad transaccional es imprescindible, entonces no use MongoDB, sino que use solo componentes en el sistema que admitan transacciones. Es extremadamente difícil construir algo sobre el componente para proporcionar una funcionalidad similar a ACID para componentes que no cumplen con ACID. Dependiendo de los casos de uso individuales, puede tener sentido separar las acciones en acciones transaccionales y no transaccionales de alguna manera ...
fuente
Esto no es realmente un problema. El error que mencionó es un error lógico (error) o IO (falla de red, disco). Este tipo de error puede dejar tanto las tiendas sin transacciones como las transaccionales en un estado no coherente. Por ejemplo, si ya ha enviado SMS pero se produjo un error al almacenar el mensaje: no puede deshacer el envío de SMS, lo que significa que no se registrará, el saldo del usuario no se reducirá, etc.
El verdadero problema aquí es que el usuario puede aprovechar las condiciones de carrera y enviar más mensajes de los que permite su saldo. Esto también se aplica a RDBMS, a menos que envíe SMS dentro de la transacción con bloqueo de campo de saldo (lo que sería un gran cuello de botella). Como una posible solución para MongoDB estaría usando
findAndModify
primero para reducir el saldo y verificarlo, si es negativo, no permita el envío y reembolse la cantidad (incremento atómico). Si es positivo, continúe enviando y en caso de que falle, reembolse el importe. La recopilación del historial de saldos también se puede mantener para ayudar a corregir / verificar el campo de saldo.fuente
El proyecto es simple, pero debe respaldar las transacciones de pago, lo que hace que todo sea difícil. Entonces, por ejemplo, un sistema de portal complejo con cientos de colecciones (foro, chat, anuncios, etc.) es en cierto sentido más simple, porque si pierde un foro o una entrada de chat, a nadie realmente le importa. Si, por otro lado, pierde una transacción de pago que es un problema grave.
Entonces, si realmente desea un proyecto piloto para MongoDB, elija uno que sea simple a ese respecto.
fuente
Las transacciones están ausentes en MongoDB por razones válidas. Esta es una de esas cosas que hacen que MongoDB sea más rápido.
En su caso, si la transacción es imprescindible, mongo parece no encajar bien.
Puede ser RDMBS + MongoDB, pero eso agregará complejidades y dificultará la administración y el soporte de la aplicación.
fuente
¡Este es probablemente el mejor blog que encontré con respecto a la implementación de la transacción como característica para mongodb.!
Indicador de sincronización: mejor para copiar datos de un documento maestro
Cola de trabajo: propósito muy general, resuelve el 95% de los casos. ¡La mayoría de los sistemas necesitan tener al menos una cola de trabajos de todos modos!
Compromiso en dos fases: esta técnica garantiza que cada entidad siempre tenga toda la información necesaria para llegar a un estado coherente
Log Reconciliation: la técnica más robusta, ideal para sistemas financieros
Versionado: proporciona aislamiento y admite estructuras complejas.
Lea esto para obtener más información: https://dzone.com/articles/how-implement-robust-and
fuente
Esto es tarde, pero creo que esto ayudará en el futuro. Utilizo Redis para hacer una cola para resolver este problema.
Requisito: la
imagen a continuación muestra que 2 acciones deben ejecutarse simultáneamente, pero la fase 2 y la fase 3 de la acción 1 deben finalizar antes de iniciar la fase 2 de la acción 2 u opuesta (una fase puede ser una solicitud REST api, una solicitud de base de datos o ejecutar código javascript ... )
Cómo le ayuda una
cola. Asegúrese de que cada código de bloque entre
lock()
yrelease()
en muchas funciones no se ejecute al mismo tiempo, haga que se aíslen.Cómo construir una cola
Solo me enfocaré en cómo evitar la condición de la raza al crear una cola en el sitio de back-end. Si no conoce la idea básica de la cola, venga aquí .
El siguiente código solo muestra el concepto, debe implementarlo de la manera correcta.
Pero debe
isRunning()
setStateToRelease()
setStateToRunning()
aislarse a sí mismo o de lo contrario enfrentará la condición de raza nuevamente. Para hacer esto, elijo Redis para fines ACID y escalable.El documento de Redis habla sobre su transacción:
P / s:
uso Redis porque mi servicio ya lo usa, puede usar cualquier otra forma de soporte de aislamiento para hacerlo.
El
action_domain
en mi código está arriba para cuando solo necesita la acción 1 llamada por el usuario A bloquear la acción 2 del usuario A, no bloquee a otro usuario. La idea es poner una llave única para el bloqueo de cada usuario.fuente
Las transacciones están disponibles ahora en MongoDB 4.0. Muestra aquí
fuente