Eu sei que existem perguntas semelhantes aqui, mas elas estão me dizendo para voltar aos sistemas RDBMS regulares se eu precisar de transações ou usar operações atômicas ou confirmação de duas fases . A segunda solução parece a melhor escolha. O terceiro que não desejo seguir, porque parece que muitas coisas podem dar errado e não posso testá-lo em todos os aspectos. Estou tendo dificuldade em refatorar meu projeto para executar operações atômicas. Não sei se isso vem do meu ponto de vista limitado (só trabalhei com bancos de dados SQL até agora) ou se realmente não pode ser feito.
Gostaríamos de testar o MongoDB em nossa empresa. Escolhemos um projeto relativamente simples - um gateway SMS. Ele permite que nosso software envie mensagens SMS para a rede celular e o gateway faz o trabalho sujo: na verdade, comunicando-se com os provedores por meio de diferentes protocolos de comunicação. O gateway também gerencia o faturamento das mensagens. Todo cliente que solicita o serviço precisa comprar alguns créditos. O sistema diminui automaticamente o saldo do usuário quando uma mensagem é enviada e nega o acesso se o saldo for insuficiente. Também porque somos clientes de provedores de SMS de terceiros, também podemos ter nossos próprios saldos com eles. Temos que acompanhar isso também.
Comecei a pensar em como posso armazenar os dados necessários com o MongoDB se reduzir alguma complexidade (cobrança externa, envio de SMS em fila). Vindo do mundo SQL, eu criaria uma tabela separada para usuários, outra para mensagens SMS e outra para armazenar as transações relacionadas ao saldo dos usuários. Digamos que eu crie coleções separadas para todos aqueles no MongoDB.
Imagine uma tarefa de envio de SMS com as seguintes etapas neste sistema simplificado:
verifique se o usuário possui saldo suficiente; negar acesso se não houver crédito suficiente
envie e armazene a mensagem na coleção do SMS com os detalhes e o custo (no sistema
status
ativo , a mensagem teria um atributo e uma tarefa a buscaria para entrega e definiria o preço do SMS de acordo com seu estado atual)diminuir o saldo do usuário pelo custo da mensagem enviada
registrar a transação na coleção de transações
Agora, qual é o problema com isso? O MongoDB pode fazer atualizações atômicas apenas em um documento. No fluxo anterior, pode acontecer que algum tipo de erro entre e a mensagem seja armazenada no banco de dados, mas o saldo do usuário não é atualizado e / ou a transação não é registrada.
Eu vim com duas idéias:
Crie uma única coleção para os usuários e armazene o saldo como um campo, transações e mensagens relacionadas ao usuário como sub-documentos no documento do usuário. Como podemos atualizar documentos atomicamente, isso realmente resolve o problema da transação. Desvantagens: se o usuário enviar muitas mensagens SMS, o tamanho do documento poderá aumentar e o limite de 4 MB poderá ser atingido. Talvez eu possa criar documentos históricos em tais cenários, mas não acho que seja uma boa ideia. Também não sei quão rápido o sistema seria se eu enviasse cada vez mais dados para o mesmo grande documento.
Crie uma coleção para usuários e outra para transações. Pode haver dois tipos de transações: compra de crédito com alteração positiva do saldo e mensagens enviadas com alteração negativa do saldo. A transação pode ter um subdocumento; por exemplo, nas mensagens enviadas, os detalhes do SMS podem ser incorporados na transação. Desvantagens: eu não armazeno o saldo atual do usuário, por isso tenho que calculá-lo toda vez que um usuário tenta enviar uma mensagem para saber se a mensagem pode passar ou não. Receio que esse cálculo possa ficar lento à medida que o número de transações armazenadas aumenta.
Estou um pouco confuso sobre qual método escolher. Existem outras soluções? Não consegui encontrar práticas recomendadas on-line sobre como solucionar esses tipos de problemas. Eu acho que muitos programadores que estão tentando se familiarizar com o mundo NoSQL estão enfrentando problemas semelhantes no começo.
fonte
Respostas:
A partir da versão 4.0, o MongoDB terá transações ACID com vários documentos. O plano é habilitar aqueles em implantações de conjuntos de réplicas primeiro, seguidos pelos clusters fragmentados. As transações no MongoDB parecerão exatamente como os desenvolvedores de transações estão familiarizadas com os bancos de dados relacionais - elas serão de múltiplas instruções, com semântica e sintaxe semelhantes (como
start_transaction
ecommit_transaction
). É importante ressaltar que as alterações no MongoDB que permitem transações não afetam o desempenho de cargas de trabalho que não as exigem.Para mais detalhes, consulte aqui .
Ter transações distribuídas, não significa que você deve modelar seus dados como em bancos de dados relacionais tabulares. Aproveite o poder do modelo de documento e siga as boas e recomendadas práticas de modelagem de dados.
fonte
Vivendo sem transações
As transações suportam propriedades ACID , mas, embora não haja transações
MongoDB
, temos operações atômicas. Bem, operações atômicas significam que, quando você trabalha em um único documento, esse trabalho será concluído antes que alguém mais veja o documento. Eles verão todas as alterações que fizemos ou nenhuma delas. E usando operações atômicas, muitas vezes você pode realizar o mesmo que realizaríamos usando transações em um banco de dados relacional. E a razão é que, em um banco de dados relacional, precisamos fazer alterações em várias tabelas. Geralmente tabelas que precisam ser unidas e, portanto, queremos fazer isso de uma só vez. E para fazer isso, como existem várias tabelas, teremos que iniciar uma transação, fazer todas essas atualizações e finalizar a transação. Mas comMongoDB
, vamos incorporar os dados, já que os pré-juntamos aos documentos e eles são esses documentos avançados que têm hierarquia. Muitas vezes podemos realizar a mesma coisa. Por exemplo, no exemplo do blog, se quisermos ter certeza de que atualizamos uma publicação de blog atomicamente, podemos fazer isso porque podemos atualizar a publicação inteira de uma só vez. Onde, como se fosse um monte de tabelas relacionais, provavelmente teríamos que abrir uma transação para poder atualizar a coleção de postagens e a coleção de comentários.Então, quais são as nossas abordagens que podemos adotar
MongoDB
para superar a falta de transações?Update
,findAndModify
,$addToSet
(Dentro de uma atualização) e$push
(dentro de uma atualização) operações operar atomicamente dentro de um único documento.fonte
Veja isto , por Tokutek. Eles desenvolvem um plug-in para o Mongo que promete não apenas transações, mas também um aumento no desempenho.
fonte
Traga direto ao ponto: se a integridade transacional é uma obrigação , não use o MongoDB, mas use apenas componentes no sistema que suporta transações. É extremamente difícil criar algo sobre o componente para fornecer funcionalidade semelhante a ACID para componentes não compatíveis com ACID. Dependendo dos casos de uso individuais, pode fazer sentido separar ações em ações transacionais e não transacionais de alguma forma ...
fonte
Isso não é realmente um problema. O erro que você mencionou é um erro lógico (bug) ou de E / S (rede, falha no disco). Esse tipo de erro pode deixar os armazenamentos sem transação e transacionais em um estado não consistente. Por exemplo, se ele já enviou SMS, mas durante o armazenamento de um erro de mensagem, ele não pode reverter o envio de SMS, o que significa que não será registrado, o saldo do usuário não será reduzido etc.
O verdadeiro problema aqui é que o usuário pode tirar proveito das condições da corrida e enviar mais mensagens do que seu saldo permite. Isso também se aplica ao RDBMS, a menos que você envie SMS dentro de uma transação com bloqueio de campo de saldo (o que seria um grande gargalo). Como uma possível solução para o MongoDB, seria o
findAndModify
primeiro a reduzir o saldo e verificar, se for negativo, não permitir o envio e o reembolso da quantia (incremento atômico). Se positivo, continue enviando e, caso falhe, devolva o valor. A coleção do histórico de saldo também pode ser atualizada para ajudar a corrigir / verificar o campo de saldo.fonte
O projeto é simples, mas você precisa oferecer suporte a transações para pagamento, o que dificulta tudo. Assim, por exemplo, um sistema de portal complexo com centenas de coleções (fórum, chat, anúncios, etc ...) é, de certa forma, mais simples, porque se você perder um fórum ou entrada no chat, ninguém realmente se importa. Por outro lado, se você perder uma transação de pagamento, isso é um problema sério.
Portanto, se você realmente deseja um projeto piloto para o MongoDB, escolha um que seja simples a esse respeito.
fonte
As transações estão ausentes no MongoDB por motivos válidos. Essa é uma daquelas coisas que tornam o MongoDB mais rápido.
No seu caso, se a transação é obrigatória, o mongo parece não ser um bom ajuste.
Pode ser RDMBS + MongoDB, mas isso adicionará complexidades e dificultará o gerenciamento e o suporte ao aplicativo.
fonte
Este é provavelmente o melhor blog que eu encontrei sobre a implementação de transações como recurso para o mongodb.!
Sinalizador de sincronização: ideal para copiar dados de um documento mestre
Fila de tarefas: uso geral, resolve 95% dos casos. A maioria dos sistemas precisa ter pelo menos uma fila de tarefas de qualquer maneira!
Confirmação em duas fases: essa técnica garante que cada entidade sempre tenha todas as informações necessárias para chegar a um estado consistente
Reconciliação de log: a técnica mais robusta, ideal para sistemas financeiros
Controle de versão: fornece isolamento e suporta estruturas complexas
Leia isto para obter mais informações: https://dzone.com/articles/how-implement-robust-and
fonte
É tarde, mas acho que isso ajudará no futuro. Eu uso o Redis para fazer uma fila para resolver esse problema.
Requisito: a
imagem abaixo mostra 2 ações que precisam ser executadas simultaneamente, mas as fases 2 e 3 da ação 1 precisam ser concluídas antes do início da fase 2 da ação 2 ou oposta (uma fase pode ser uma solicitação REST api, uma solicitação de banco de dados ou executar código javascript ... )
Como uma fila o ajuda na
fila Certifique-se de que todos os códigos de bloco entre
lock()
erelease()
em muitas funções não sejam executados ao mesmo tempo, faça-os isolar.Como construir uma fila
Vou focar apenas em como evitar a parte de condições de corrida ao criar uma fila no site de back-end. Se você não conhece a idéia básica da fila, venha aqui .
O código abaixo mostra apenas o conceito, você precisa implementar da maneira correta.
Mas você precisa se
isRunning()
setStateToRelease()
setStateToRunning()
isolar ou então enfrenta a condição de raça novamente. Para fazer isso, escolho o Redis para fins de ACID e escalável.O documento Redis fala sobre sua transação:
P / s:
eu uso o Redis porque meu serviço já o usa, você pode usar qualquer outro meio de suporte ao isolamento para fazer isso.
O
action_domain
código em meu código acima está indicado para quando você precisar apenas da ação 1 chamada do usuário A, bloquear a ação 2 do usuário A, não bloquear outro usuário. A ideia é colocar uma chave exclusiva para o bloqueio de cada usuário.fonte
As transações estão disponíveis agora no MongoDB 4.0. Amostra aqui
fonte