Bloqueio otimista vs. pessimista


572

Entendo as diferenças entre o bloqueio otimista e o pessimista. Agora alguém poderia me explicar quando eu usaria qualquer um deles em geral?

E a resposta a esta pergunta muda dependendo se estou usando um procedimento armazenado ou não para executar a consulta?

Mas apenas para verificar, otimista significa "não tranque a mesa enquanto lê" e pessimista significa "tranque a mesa durante a leitura".



1
Essa é uma boa pergunta, principalmente porque na serialização eu li At any technique type conflicts should be detected and considered, with similar overhead for both materialized and non-materialized conflicts.
Little Alien

1
Aqui você encontra uma boa explicação, aqui no SO, sobre qual é o conceito raiz do Bloqueio Otimista .
Diego Mazzaro

Respostas:


812

O bloqueio otimista é uma estratégia na qual você lê um registro, anota um número de versão (outros métodos para fazer isso envolvem datas, carimbos de hora ou somas de verificação / hashes) e verifica se a versão não mudou antes de gravar o registro. Ao gravar o registro, você filtra a atualização na versão para garantir que ela seja atômica. (ou seja, não foi atualizado entre quando você verifica a versão e grava o registro no disco) e atualiza a versão em uma ocorrência.

Se o registro estiver sujo (ou seja, versão diferente da sua), você cancelará a transação e o usuário poderá reiniciá-la.

Essa estratégia é mais aplicável a sistemas de alto volume e arquiteturas de três camadas em que você não mantém necessariamente uma conexão com o banco de dados para sua sessão. Nessa situação, o cliente não pode realmente manter bloqueios de banco de dados, pois as conexões são obtidas de um pool e você pode não estar usando a mesma conexão de um acesso para o próximo.

Bloqueio pessimista é quando você bloqueia o registro para seu uso exclusivo até terminar com ele. Tem uma integridade muito melhor do que o bloqueio otimista, mas exige que você tenha cuidado com o design do aplicativo para evitar deadlocks . Para usar o bloqueio pessimista, você precisa de uma conexão direta com o banco de dados (como seria o caso em um aplicativo de servidor cliente de duas camadas ) ou um ID de transação disponível externamente que possa ser usado independentemente da conexão.

No último caso, você abre a transação com o TxID e reconecta usando esse ID. O DBMS mantém os bloqueios e permite que você selecione a sessão novamente através do TxID. É assim que as transações distribuídas usando protocolos de confirmação de duas fases (como transações XA ou COM + ) funcionam.


148
O bloqueio otimista não usa necessariamente um número de versão. Outras estratégias incluem o uso de (a) um carimbo de data / hora ou (b) todo o estado da própria linha. A última estratégia é feia, mas evita a necessidade de uma coluna de versão dedicada, nos casos em que você não pode modificar o esquema.
8139 Andrew Swan

2
@geek - Protocolos de transação distribuídos, como o XA, permitem que um identificador de transação separado seja reticulado em torno de um ou mais sistemas. Esse tipo de protocolo permite que os bloqueios sejam usados ​​por meio de conexões agrupadas, pois o identificador de transação é desacoplado das sessões e fornecido explicitamente. No entanto, isso implica alguma sobrecarga e é propenso a vazar bloqueios e identificadores de transação, se o seu aplicativo não for escrupuloso em controlá-los. É uma solução muito mais pesada.
ConcernedOfTunbridgeWells

22
@supercat - Não concorde que o bloqueio otimista seja menos de 100% preciso - desde que verifique todos os registros de entrada para transações que devem permanecer inalteradas durante o período, é tão preciso quanto o bloqueio pessimista (selecione o estilo de atualização) naqueles mesmos registros. A principal diferença é que o bloqueio otimista incorre em sobrecarga apenas se houver um conflito, enquanto o bloqueio pessimista reduz a sobrecarga em conflito. Tão otimista é o melhor caso em que a maioria das transações não entra em conflito - o que, espero, geralmente é o caso da maioria dos aplicativos.
RichVel

2
@ Legendas - Usar o bloqueio otimista certamente seria uma estratégia apropriada para uma aplicação web.
ConcernedOfTunbridgeWells

2
Você deve mencionar que a escolha depende também da taxa de leitura versus gravação: se o seu aplicativo for principalmente um aplicativo somente leitura por muitos usuários e, às vezes, você gravar dados, será necessário um bloqueio otimista. O StackOverflow, por exemplo, tem muitas pessoas lendo páginas e, às vezes, alguém edita uma: no bloqueio pessimista, quem receberia o bloqueio? o primeiro? No bloqueio otimista, a pessoa que deseja editar a página pode fazê-lo desde que tenha a última versão.
jehon 5/05

177

O bloqueio otimista é usado quando você não espera muitas colisões. Custa menos realizar uma operação normal, mas se ocorrer uma colisão, você pagaria um preço mais alto para resolvê-lo quando a transação for interrompida.

O bloqueio pessimista é usado quando uma colisão é antecipada. As transações que violariam a sincronização são simplesmente bloqueadas.

Para selecionar o mecanismo de bloqueio adequado, é necessário estimar a quantidade de leituras e gravações e planejar adequadamente.


Em casos normais, a declaração é perfeita, mas em casos especiais em que você pode gerenciar a operação do CAS , permitindo imprecisão como @skaffman mencionado na resposta, eu diria que realmente depende.
Ouvido

75

Otimista assume que nada vai mudar enquanto você estiver lendo.

Pessimista pressupõe que algo o fará e assim o trava.

Se não for essencial que os dados sejam perfeitamente lidos, use otimista. Você pode ter uma leitura estranha 'suja' - mas é muito menos provável que resulte em impasses e similares.

A maioria dos aplicativos da Web é boa com leituras sujas - nas raras ocasiões em que os dados não são exatamente iguais na próxima recarga.

Para operações exatas de dados (como em muitas transações financeiras), use pessimista. É essencial que os dados sejam lidos com precisão, sem alterações não mostradas - a sobrecarga de bloqueio extra vale a pena.

Ah, e o Microsoft SQL Server assume como padrão o bloqueio de páginas - basicamente a linha que você está lendo e algumas de cada lado. O bloqueio de linhas é mais preciso, mas muito mais lento. Muitas vezes, vale a pena definir suas transações como confirmadas por leitura ou sem bloqueio para evitar conflitos durante a leitura.


O JPA Optimistic Locking permite garantir consistência de leitura.
Gili

4
A consistência de leitura é uma preocupação separada - com o PostgreSQL, Oracle e muitos outros bancos de dados, você obtém uma visão consistente dos dados, independentemente de quaisquer atualizações ainda não confirmadas, e não é afetada nem por bloqueios de linha exclusivos.
RichVel

Eu tenho que concordar com @RichVel. Por um lado, posso ver como o bloqueio pessimista pode impedir leituras sujas se o nível de isolamento da transação for LIDO SEM COMPROMISSO. Mas é enganoso dizer que o bloqueio otimista é suscetível a leituras sujas sem mencionar que a maioria dos bancos de dados (incluindo aparentemente o MS SQL Server) tem um nível de isolamento padrão "READ COMMITTED", que impede leituras sujas e torna o bloqueio otimista tão preciso quanto pessimista.
Antinome # 7/14

Eric Brower diz que os banqueiros, ao contrário de outros, preferem operações sujas. Seus gurus parecem absolutamente fora dos carrinhos.
Little Alien

1
Eric Brewer é o guru que deu o teorema da CAP diz sobre consistência no setor bancário . É o oposto do que você honra.
Little Alien

50

Além do que já foi dito:

  • Deve-se dizer que o optimisticbloqueio tende a melhorar a simultaneidade à custa da previsibilidade.
  • Pessimistico bloqueio tende a reduzir a simultaneidade, mas é mais previsível. Você paga seu dinheiro, etc ...

3
Não vejo como a previsibilidade (como você a define) é aprimorada com o bloqueio pessimista - se você quer dizer 'a transação pode ser concluída depois que os bloqueios forem executados', você está certo, mas até que a transação tenha todos os bloqueios necessários, pode haver um atraso para obter bloqueios restantes e, na verdade, podem ser abortados devido à detecção de deadlock do banco de dados + lógica de resolução. Aplicativos que usam bloqueio pessimista podem ter tempos de execução altamente imprevisíveis - o exemplo clássico é que alguém bloqueia um registro X e depois almoça, um usuário bloqueia o registro X e Y, depois outro Y e Z e assim sucessivamente até que a maioria dos usuários seja bloqueada. ..
RichVel 23/04

40

Ao lidar com conflitos, você tem duas opções:

  • Você pode tentar evitar o conflito, e é isso que o bloqueio pessimista faz.
  • Ou você pode permitir que o conflito ocorra, mas é necessário detectá-lo ao confirmar suas transações, e é isso que o Bloqueio Otimista faz.

Agora, vamos considerar a seguinte anomalia de atualização perdida :

Atualização perdida

A anomalia de atualização perdida pode ocorrer no nível de isolamento Read Committed .

No diagrama acima, podemos ver que Alice acredita que pode retirar 40 dela, accountmas não percebe que Bob acabou de alterar o saldo da conta e agora restam apenas 20 nessa conta.

Bloqueio pessimista

O bloqueio pessimista atinge esse objetivo usando um bloqueio compartilhado ou de leitura na conta, para que Bob seja impedido de alterar a conta.

Bloqueio pessimista de atualização perdida

No diagrama acima, Alice e Bob adquirem um bloqueio de leitura na accountlinha da tabela que ambos os usuários leram. O banco de dados adquire esses bloqueios no SQL Server ao usar Leitura Repetível ou Serializável.

Como Alice e Bob leram o accountvalor PK de 1, nenhum deles pode alterá-lo até que um usuário libere o bloqueio de leitura. Isso ocorre porque uma operação de gravação requer uma aquisição de bloqueio de gravação / exclusivo, e bloqueios compartilhados / lidos impedem bloqueios de gravação / exclusivos.

Somente após Alice confirmar sua transação e o bloqueio de leitura ser liberado na accountlinha, Bob UPDATEretomará e aplicará a alteração. Até Alice liberar o bloqueio de leitura, o UPDATE de Bob bloqueia.

Para obter mais detalhes sobre como as estruturas de acesso a dados usam o suporte de bloqueio pessimista do banco de dados subjacente, consulte este artigo .

Bloqueio otimista

O bloqueio otimista permite que o conflito ocorra, mas o detecta ao aplicar o UPDATE de Alice conforme a versão foi alterada.

Transações no nível do aplicativo

Desta vez, temos uma versioncoluna adicional . A versioncoluna é incrementada toda vez que um UPDATE ou DELETE é executado e também é usado na cláusula WHERE das instruções UPDATE e DELETE. Para que isso funcione, precisamos emitir o SELECT e ler a corrente versionantes de executar o UPDATE ou DELETE, caso contrário, não saberíamos qual valor de versão passar para a cláusula WHERE ou incrementar.

Para obter mais detalhes sobre como as estruturas de acesso a dados implementam o bloqueio otimista, consulte este artigo .

Transações no nível do aplicativo

Os sistemas de banco de dados relacional surgiram no final dos anos 70 e início dos anos 80, quando um cliente normalmente se conectava a um mainframe por meio de um terminal. É por isso que ainda vemos os sistemas de banco de dados definirem termos como a configuração SESSION.

Atualmente, pela Internet, não executamos mais leituras e gravações no contexto da mesma transação de banco de dados, e o ACID não é mais suficiente.

Por exemplo, considere o seguinte caso de uso:

insira a descrição da imagem aqui

Sem o bloqueio otimista, não há como esta Atualização Perdida ter sido capturada, mesmo que as transações do banco de dados usassem Serializable. Isso ocorre porque as leituras e gravações são executadas em solicitações HTTP separadas, portanto, em diferentes transações do banco de dados.

Portanto, o bloqueio otimista pode ajudá-lo a evitar atualizações perdidas, mesmo ao usar transações no nível do aplicativo que incorporam o tempo de reflexão do usuário.

Para obter mais detalhes sobre transações lógicas ou no nível do aplicativo, consulte este artigo .

Conclusão

O bloqueio otimista é uma técnica muito útil e funciona bem mesmo quando se usa níveis de isolamento menos rigorosos, como Read Committed, ou quando leituras e gravações são executadas em transações subsequentes do banco de dados.

A desvantagem do bloqueio otimista é que uma reversão será acionada pela estrutura de acesso a dados ao capturar um OptimisticLockException, perdendo, portanto, todo o trabalho que fizemos anteriormente pela transação atualmente em execução.

Quanto mais contenção, mais conflitos e maior a chance de abortar transações. As reversões podem ser caras para o sistema de banco de dados, pois ele precisa reverter todas as alterações pendentes atuais, que podem envolver linhas de tabela e registros de índice.

Por esse motivo, o bloqueio pessimista pode ser adequado quando ocorrem conflitos com frequência, pois reduz a chance de reverter transações.


Para quais cenários você sugeriria escolher OptimisticLocking e PessimisticLocking? Depende de quantas vezes uma OptimisticLockException ocorre?
Stimpson Cat

1
Depende do caso de uso. Às vezes, o bloqueio otimista é a única solução (por exemplo, transações com várias solicitações). Outras vezes, o bloqueio pessimista é a única solução (por exemplo, bloqueios consultivos do PostgreSQL ). Às vezes, você precisa combiná-los, como é o caso PESSIMISTIC_FORCE_INCREMENT.
Vlad Mihalcea

22

Pensaria em mais um caso em que o bloqueio pessimista seria uma escolha melhor.

Para um bloqueio otimista, todo participante na modificação de dados deve concordar em usar esse tipo de bloqueio. Mas se alguém modificar os dados sem se preocupar com a coluna da versão, isso estragará toda a idéia do bloqueio otimista.


As pessoas que tentam usar o bloqueio otimista e pessimista também podem pisar nos pés umas das outras, por assim dizer. Imagine um cenário em que uma sessão otimista leia um registro e faça alguns cálculos enquanto uma sessão pessimista atualiza o registro; a sessão otimista volta e atualiza o mesmo registro sem observar as alterações feitas. Selecione ... para atualização só funciona se todas as sessões estiverem usando a mesma sintaxe.
lusional 4/08/16

Boa explicação, dê o seu voto para mim
Dulaj Kulathunga

15

Existem basicamente duas respostas mais populares. O primeiro diz basicamente

O Optimistic precisa de arquiteturas de três camadas, nas quais você não mantém necessariamente uma conexão com o banco de dados para sua sessão, enquanto o Bloqueio pessimista é quando você bloqueia o registro para seu uso exclusivo até terminar com ele. Tem uma integridade muito melhor do que o bloqueio otimista. Você precisa de uma conexão direta com o banco de dados.

Outra resposta é

otimista (controle de versão) é mais rápido devido a nenhum bloqueio, mas o bloqueio (pessimista) tem um desempenho melhor quando a contenção é alta e é melhor evitar o trabalho do que descartá-lo e recomeçar.

ou

O bloqueio otimista funciona melhor quando você tem colisões raras

Como está colocado nesta página.

Eu criei minha resposta para explicar como "manter conexão" está relacionado a "baixas colisões".

Para entender qual estratégia é melhor para você, pense não nas transações por segundo que seu banco de dados possui, mas na duração de uma única transação. Normalmente, você abre a transação, realiza a operação e fecha a transação. Essa é uma transação clássica clássica, curta, que a ANSI tinha em mente e muito bem para se livrar do bloqueio. Mas como você implementa um sistema de reserva de bilhetes em que muitos clientes reservam os mesmos quartos / assentos ao mesmo tempo?

Você navega nas ofertas, preenche o formulário com muitas opções disponíveis e preços atuais. Demora muito tempo e as opções podem se tornar obsoletas, todos os preços inválidos entre você começaram a preencher o formulário e pressione o botão "Eu concordo" porque não havia bloqueio nos dados que você acessou e que outra pessoa, mais ágil, interferiu alterando todos os preços e você precisa reiniciar com novos preços.

Você pode bloquear todas as opções enquanto as lê. Este é um cenário pessimista. Você vê por que é uma porcaria. Seu sistema pode ser derrubado por um único palhaço que simplesmente inicia uma reserva e vai fumar. Ninguém pode reservar nada antes que ele termine. Seu fluxo de caixa cai para zero. É por isso que reservas otimistas são usadas na realidade. Aqueles que demoram demais precisam reiniciar sua reserva a preços mais altos.

Nessa abordagem otimista, você deve registrar todos os dados que lê (como na minha Leitura repetida ) e chegar ao ponto de confirmação com sua versão dos dados (desejo comprar ações pelo preço que você indicou nesta cotação, não pelo preço atual ) Nesse ponto, a transação ANSI é criada, o que bloqueia o banco de dados, verifica se nada foi alterado e confirma / interrompe sua operação. Na IMO, essa é uma emulação efetiva do MVCC , que também está associada ao Optimistic CC e também pressupõe que sua transação seja reiniciada em caso de abortamento, ou seja, você fará uma nova reserva. Uma transação aqui envolve decisões de um usuário humano.

Estou longe de entender como implementar o MVCC manualmente, mas acho que transações de longa duração com opção de reinicialização são a chave para entender o assunto. Corrija-me se estiver errado em algum lugar. Minha resposta foi motivada por este capítulo de Alex Kuznecov .


12

Na maioria dos casos, o bloqueio otimista é mais eficiente e oferece maior desempenho. Ao escolher entre bloqueio pessimista e otimista, considere o seguinte:

  • O bloqueio pessimista é útil se houver muitas atualizações e chances relativamente altas de os usuários tentarem atualizar os dados ao mesmo tempo. Por exemplo, se cada operação puder atualizar um grande número de registros por vez (o banco pode adicionar juros a todas as contas no final de cada mês) e dois aplicativos estiverem executando essas operações ao mesmo tempo, eles terão conflitos .

  • O bloqueio pessimista também é mais apropriado em aplicativos que contêm pequenas tabelas que são atualizadas frequentemente. No caso desses chamados pontos de acesso, os conflitos são tão prováveis ​​que o bloqueio otimista desperdiça esforços para reverter transações conflitantes.

  • O bloqueio otimista é útil se a possibilidade de conflitos for muito baixa - existem muitos registros, mas relativamente poucos usuários, ou muito poucas atualizações e, principalmente, operações do tipo leitura.


3

Um caso de uso para bloqueio otimista é fazer com que seu aplicativo use o banco de dados para permitir que um de seus threads / hosts 'reivindique' uma tarefa. Essa é uma técnica que é útil para mim regularmente.

O melhor exemplo em que posso pensar é em uma fila de tarefas implementada usando um banco de dados, com vários threads reivindicando tarefas simultaneamente. Se uma tarefa tiver o status 'Disponível', 'Reivindicado', 'Concluído', uma consulta db poderá dizer algo como "Definir status = 'Reivindicado' em que status = 'Disponível'. Se vários encadeamentos tentarem alterar o status dessa maneira, todos, exceto o primeiro encadeamento, falharão devido a dados sujos.

Observe que este é um caso de uso que envolve apenas bloqueio otimista. Portanto, como alternativa a dizer "O bloqueio otimista é usado quando você não espera muitas colisões", também pode ser usado onde você espera colisões, mas deseja que exatamente uma transação seja bem-sucedida.


3

Muitas coisas boas foram ditas acima sobre travas otimistas e pessimistas. Um ponto importante a considerar é o seguinte:

Ao usar o bloqueio otimista, precisamos ter cuidado com o fato de como o aplicativo se recuperará dessas falhas.

Especialmente em arquiteturas assíncronas orientadas a mensagens, isso pode levar ao processamento de mensagens fora de ordem ou a atualizações perdidas.

Cenários de falhas precisam ser pensados.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.