O problema de atualização perdida ocorre quando duas ou mais transações leem os mesmos dados e atualizam -os com base em suas respectivas leituras. Se as transações não forem sincronizadas adequadamente, as atualizações de uma transação poderão substituir as atualizações de outra, levando a dados perdidos. Aqui estão várias medidas que podem ser tomadas para impedir o seguinte:
1. Mecanismos de travamento: *
bloqueios exclusivos (bloqueios de gravação): Quando uma transação deseja atualizar uma linha, ele adquire um bloqueio exclusivo nessa linha. Nenhuma outra transação pode adquirir qualquer tipo de bloqueio (leia ou escreva) nessa linha até que o bloqueio exclusivo seja liberado. Isso garante que apenas uma transação possa modificar a linha a qualquer momento.
*
bloqueios compartilhados (lida bloqueios): Quando uma transação deseja ler uma linha, ele adquire um bloqueio compartilhado nessa linha. Várias transações podem manter bloqueios compartilhados na mesma linha simultaneamente. No entanto, nenhuma transação pode adquirir um bloqueio exclusivo, enquanto há bloqueios compartilhados mantidos nessa linha. Isso impede que os escritores interfiram nos leitores.
*
Bloquear granularidade: O nível em que os bloqueios são aplicados (por exemplo, nível de linha, nível de página, nível de tabela, nível de banco de dados) afeta a concorrência e o desempenho. O bloqueio de granulação mais fino (por exemplo, nível de linha) permite mais simultaneidade, mas tem uma sobrecarga mais alta, enquanto o bloqueio de granulação mais grossa (por exemplo, nível de tabela) reduz a sobrecarga, mas restringe a simultaneidade.
2. Protocolos de controle de simultaneidade: *
bloqueio bifásico (2pl): Um protocolo de controle de simultaneidade que garante a serialização (e, portanto, impede as atualizações perdidas). Tem duas fases:
*
Fase de crescimento: Uma transação pode adquirir bloqueios, mas não pode liberá -los.
*
Fase de encolhimento: Uma transação pode liberar bloqueios, mas não pode adquirir novos.
O bloqueio bifásico rigoroso (2PL rigoroso) mantém todos os bloqueios exclusivos até o final da transação (comprometimento ou reversão). Isso evita reversão em cascata (onde uma falha de uma transação pode forçar a reversão de outros).
*
Timestamp ordenando (para): Cada transação recebe um registro de data e hora exclusivos. O sistema usa esses registros de data e hora para garantir que as operações conflitantes sejam executadas na ordem de seus registros de data e hora. Se uma transação tentar escrever um valor que já foi substituído por uma transação com um registro de data e hora posterior, a gravação será rejeitada e a transação é revertida. Essa abordagem evita impasses, mas pode levar à fome.
*
Controle de concorrência otimista (OCC): As transações prosseguem sem adquirir bloqueios. Antes de uma transação se comprometer, ele verifica se alguma outra transação modificou os dados que ele leu. Se houver conflitos, a transação será revertida. OCC é adequado para situações com baixa contenção, pois as reversões podem ser caras. Normalmente tem três fases:
*
Fase de leitura: A transação lê dados e o armazena em um espaço de trabalho local.
*
Fase de validação: O sistema verifica se algum dos dados que a transação lida foi modificada por outra transação desde que foi lida.
*
Fase de gravação: Se a validação for bem -sucedida, as alterações serão aplicadas ao banco de dados. Caso contrário, a transação é revertida.
3. Usando transações: *
Propriedades do ácido: Sempre use transações e verifique se o seu sistema de banco de dados aplica as propriedades ácidas (atomicidade, consistência, isolamento, durabilidade). A propriedade de 'isolamento' é crucial para impedir que as atualizações perdidas.
*
Níveis de isolamento da transação: Os bancos de dados SQL oferecem diferentes níveis de isolamento (por exemplo, leitura não comprometida, leitura, leitura repetível, serializável). Níveis mais altos de isolamento (por exemplo, serializável) fornecem garantias mais fortes contra problemas de simultaneidade como atualizações perdidas, mas podem reduzir a simultaneidade e o desempenho. Escolha cuidadosamente o nível de isolamento apropriado para as necessidades do seu aplicativo. `Serializável 'é o mais restritivo e seguro, mas potencialmente mais lento. `Read Committed` é uma inadimplência comum e fornece proteção razoável.
4. Lógica no nível do aplicativo: *
operações atômicas (Compare-and-swap): Alguns sistemas de banco de dados e linguagens de programação fornecem operações atômicas que podem ser usadas para executar operações de leitura-modificação-gravação em uma etapa única e individual. Por exemplo, a operação "Compare-and-Swap" (CAS) pode atualizar atomicamente um valor se e somente se seu valor atual corresponder a um valor esperado especificado. Essa abordagem pode evitar a necessidade de bloqueios explícitos em alguns casos.
*
gravar versão/bloqueio otimista no aplicativo: O aplicativo pode incluir um número de versão (ou registro de data e hora) nos dados que lê. Ao atualizar os dados, o aplicativo inclui o número da versão original. O banco de dados atualiza apenas a linha se o número da versão corresponder ao número da versão atual no banco de dados. Se os números da versão não corresponderem, a atualização será rejeitada e o aplicativo deve tentar novamente a atualização. Esta é outra implementação da estratégia otimista de controle de simultaneidade.
*
considerações de lógica de negócios: Revise a lógica de negócios para ver se existem maneiras de evitar o total de operações de leitura-modificação-gravação. Você pode redesenhar o modelo de dados ou o processo para evitar o potencial de atualizações perdidas?
5. Design do banco de dados: *
Normalização: Um banco de dados adequadamente normalizado ajuda a reduzir a redundância de dados e melhorar a consistência dos dados, o que pode ajudar indiretamente a prevenir problemas de simultaneidade.
*
Tipos de dados apropriados: O uso de tipos de dados apropriados para seus dados pode ajudar a evitar erros e melhorar o desempenho, o que pode ajudar indiretamente a prevenir problemas de simultaneidade.
Cenário e soluções de exemplo: Imagine dois usuários tentando atualizar a quantidade de um produto em uma tabela de inventário.
*
Usuário A: Lê a quantidade (por exemplo, 10). O usuário A deseja vender 2, então calcula nova quantidade como 8.
*
Usuário B: Lê a quantidade (por exemplo, 10). O usuário B deseja vender 3, portanto, calcula a nova quantidade como 7.
*
Usuário A: Escreve a nova quantidade (8) no banco de dados.
*
Usuário B: Escreve a nova quantidade (7) no banco de dados.
A quantidade final no banco de dados é 7, mas deve ser 5 (10 - 2 - 3). A atualização do usuário A foi perdida.
Aqui estão algumas soluções para este cenário:
*
Bloqueio exclusivo: Quando um dos usuários lê a quantidade para atualização, adquira um bloqueio exclusivo na linha do produto. O outro usuário deve esperar até que o bloqueio seja liberado antes de ler e atualizar.
*
Nível de isolamento serializável: Defina o nível de isolamento da transação como serializável. Isso garante que as duas transações sejam efetivamente serializadas, impedindo -as de interferir um no outro.
*
bloqueio otimista (com o número da versão): 1. Quando o usuário A lê a quantidade (10), eles também leem um número de versão (por exemplo, 1).
2. O usuário A calcula a nova quantidade (8) e inclui o número da versão original (1) na instrução ATUALIZAÇÃO.
3. O banco de dados atualiza a quantidade somente se o número da versão atual ainda estiver 1. A instrução Atualização também incrementa o número da versão (por exemplo, para 2).
4. Se o Usuário B tentar atualizar após o Usuário A já ter começado, a atualização do Usuário B falhará (porque o número da versão é agora 2) e eles terão que reler os dados e novamente novamente a atualização.
*
Incremento atômico/operação de decréscimo: Use uma operação de incremento atômico/decremento específico do banco de dados, se disponível. Por exemplo:`Update Inventory Set Quantidade =Quantidade-2 Onde Product_id =X` essa abordagem evita a sequência de leitura-modificação-gravação.
Escolhendo a abordagem correta: A melhor abordagem para evitar atualizações perdidas depende de vários fatores, incluindo:
*
Nível de simultaneidade: Com que frequência as atualizações são feitas nos mesmos dados?
*
Requisitos de desempenho: Qual é a sobrecarga aceitável para o controle de simultaneidade?
*
Recursos do sistema de banco de dados: Quais mecanismos de controle de simultaneidade são suportados pelo banco de dados?
*
Complexidade do aplicativo: Quão complexa é a lógica do aplicativo e com que facilidade pode ser modificada?
A consideração cuidadosa desses fatores ajudará você a escolher a solução mais eficaz para sua situação específica. Usar uma combinação de técnicas geralmente é a estratégia mais eficaz. Por exemplo, usando transações com um nível de isolamento apropriado em conjunto com o bloqueio otimista no nível do aplicativo.