Desbloqueando o Postgres Lock Manager. Bruce Momjian

Transcrição da palestra de Bruce Momjian de 2020 "Unlocking the Postgres Lock Manager".

Desbloqueando o Postgres Lock Manager. Bruce Momjian

(Observação: todas as consultas SQL dos slides podem ser obtidas neste link: http://momjian.us/main/writings/pgsql/locking.sql)

Olá! É ótimo estar aqui na Rússia novamente. Lamento não ter podido ir no ano passado, mas este ano Ivan e eu temos grandes planos. Espero estar aqui muito mais vezes. Adoro vir para a Rússia. Vou visitar Tyumen, Tver. Estou muito feliz por poder visitar essas cidades.

Meu nome é Bruce Momjian. Trabalho na EnterpriseDB e trabalho com Postgres há mais de 23 anos. Eu moro na Filadélfia, EUA. Viajo cerca de 90 dias por ano. E participo de cerca de 40 conferências. Meu Local na rede Internet, que contém os slides que vou mostrar agora. Portanto, após a conferência você poderá baixá-los do meu site pessoal. Ele também contém cerca de 30 apresentações. Há também vídeos e um grande número de entradas de blog, mais de 500. Este é um recurso bastante informativo. E se você se interessou por este material, convido você a utilizá-lo.

Eu era professor antes de começar a trabalhar com Postgres. E estou muito feliz por poder agora dizer-lhe o que estou prestes a lhe dizer. Esta é uma das minhas apresentações mais interessantes. E esta apresentação contém 110 slides. Começaremos a falar de coisas simples e, no final, o relatório tornar-se-á cada vez mais complexo, e tornar-se-á bastante complexo.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Esta é uma conversa bastante desagradável. O bloqueio não é o assunto mais popular. Queremos que isso desapareça em algum lugar. É como ir ao dentista.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

  1. O bloqueio é um problema para muitas pessoas que trabalham em bancos de dados e têm vários processos em execução ao mesmo tempo. Eles precisam de bloqueio. Ou seja, hoje vou te dar conhecimentos básicos sobre bloqueio.
  2. IDs de transação. Esta é uma parte um tanto chata da apresentação, mas precisa ser compreendida.
  3. A seguir falaremos sobre os tipos de bloqueio. Esta é uma parte bastante mecânica.
  4. E a seguir daremos alguns exemplos de bloqueio. E será muito difícil de entender.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Vamos falar sobre bloqueio.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Nossa terminologia é bastante complexa. Quantos de vocês sabem de onde vem essa passagem? Duas pessoas. Isto é de um jogo chamado Colossal Cave Adventure. Era um jogo de computador baseado em texto nos anos 80, eu acho. Lá você tinha que entrar em uma caverna, em um labirinto, e o texto mudava, mas o conteúdo era sempre aproximadamente o mesmo. É assim que me lembro deste jogo.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E aqui vemos o nome dos bloqueios que nos chegaram da Oracle. Nos os utilizamos.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Aqui vemos termos que me confundem. Por exemplo, COMPARTILHE ATUALIZAÇÃO ECXLUSIVE. Próximo COMPARTILHE RAW ECXLUSIVE. Para ser sincero, esses nomes não são muito claros. Tentaremos considerá-los com mais detalhes. Alguns contêm a palavra “compartilhar”, que significa separar. Alguns contêm a palavra “exclusivo”. Alguns contêm essas duas palavras. Eu gostaria de começar explicando como essas fechaduras funcionam.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E a palavra “acesso” também é muito importante. E as palavras “linha” são uma string. Ou seja, distribuição de acesso, distribuição de linhas.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Outro assunto que precisa ser entendido no Postgres, que infelizmente não poderei abordar em minha palestra, é o MVCC. Tenho uma apresentação separada sobre este tópico em meu site. E se você acha que esta apresentação é difícil, MVCC é provavelmente a mais difícil. E se você estiver interessado, pode assistir no site. Você pode assistir ao vídeo.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Outra coisa que precisamos entender são os IDs de transação. Muitas transações não podem funcionar sem identificadores exclusivos. E aqui temos uma explicação do que é uma transação. Postgres possui dois sistemas de numeração de transações. Eu sei que esta não é uma solução muito bonita.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Lembre-se também de que os slides serão bastante difíceis de entender, então o que está destacado em vermelho é o que você precisa prestar atenção.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

http://momjian.us/main/writings/pgsql/locking.sql

Vamos ver. O número da transação é destacado em vermelho. A função SELECT pg_back é mostrada aqui. Ele retorna minha transação e o ID da transação.

Mais uma coisa, se você gostou dessa apresentação e quer rodar ela no seu banco de dados, então você pode acessar esse link em rosa e baixar o SQL dessa apresentação. E você pode simplesmente executá-lo em seu PSQL e toda a apresentação estará na sua tela imediatamente. Não conterá flores, mas pelo menos podemos ver.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Neste caso, vemos o ID da transação. Este é o número que atribuímos a ela. E há outro tipo de ID de transação no Postgres, que é chamado de ID de transação virtual

E devemos entender isso. Isso é muito importante, caso contrário não conseguiremos entender o bloqueio no Postgres.

Um ID de transação virtual é um ID de transação que não contém valores persistentes. Por exemplo, se eu executar um comando SELECT, provavelmente não alterarei o banco de dados, não bloquearei nada. Portanto, quando executamos um SELECT simples, não damos a essa transação um ID persistente. Nós apenas damos a ela uma identificação virtual lá.

E isso melhora o desempenho do Postgres, melhora os recursos de limpeza, de modo que o ID da transação virtual consiste em dois números. O primeiro número antes da barra é o ID do backend. E à direita vemos apenas um contador.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Portanto, se eu executar uma solicitação, ela informará que o ID do backend é 2.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E se eu executar uma série dessas transações, veremos que o contador aumenta cada vez que executo uma consulta. Por exemplo, quando executo a consulta 2/10, 2/11, 2/12, etc.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Lembre-se de que existem duas colunas aqui. À esquerda vemos o ID da transação virtual – 2/12. E à direita temos um ID de transação permanente. E este campo está vazio. E esta transação não modifica o banco de dados. Portanto, não forneço um ID de transação permanente.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Assim que executo o comando de análise ((ANALYZE)), a mesma consulta me fornece um ID de transação permanente. Veja como isso mudou para nós. Eu não tinha esse ID antes, mas agora tenho.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Então aqui está outro pedido, outra transação. O número da transação virtual é 2/13. E se eu solicitar um ID de transação persistente, quando executar a consulta, eu o obterei.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Então, mais uma vez. Temos um ID de transação virtual e um ID de transação persistente. Basta entender este ponto para entender o comportamento do Postgres.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Passamos para a terceira seção. Aqui iremos simplesmente percorrer os diferentes tipos de bloqueios no Postgres. Não é muito interessante. A última seção será muito mais interessante. Mas devemos considerar as coisas básicas, porque caso contrário não entenderemos o que acontecerá a seguir.

Percorreremos esta seção e veremos cada tipo de bloqueio. E vou mostrar exemplos de como eles são instalados, como funcionam, vou mostrar algumas consultas que você pode usar para ver como funciona o bloqueio no Postgres.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Para criar uma consulta e ver o que está acontecendo no Postgres, precisamos emitir a consulta na visualização do sistema. Neste caso, pg_lock está destacado em vermelho. Pg_lock é uma tabela de sistema que nos informa quais bloqueios estão atualmente em uso no Postgres.

No entanto, é muito difícil para mim mostrar o pg_lock por si só porque é bastante complexo. Então criei uma view que mostra pg_locks. E também faz algum trabalho para mim que me permite entender melhor. Ou seja, exclui meus bloqueios, minha própria sessão, etc. É apenas SQL padrão e permite mostrar melhor o que está acontecendo.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Outro problema é que essa visualização é muito ampla, então tenho que criar uma segunda - lockview2.

Desbloqueando o Postgres Lock Manager. Bruce Momjian E me mostra mais colunas da tabela. E outro que me mostra o resto das colunas. Isso é bastante complexo, então tentei apresentá-lo da forma mais simples possível.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Então criamos uma tabela chamada Lockdemo. E criamos uma linha lá. Esta é a nossa tabela de exemplo. E criaremos seções apenas para mostrar exemplos de bloqueios.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Então, uma linha, uma coluna. O primeiro tipo de bloqueio é denominado ACCESS SHARE. Este é o bloqueio menos restritivo. Isso significa que praticamente não entra em conflito com outros bloqueios.

E se quisermos definir explicitamente um bloqueio, executamos o comando “lock table”. E obviamente irá bloquear, ou seja, no modo ACCESS SHARE lançamos a tabela de bloqueio. E se eu executar o PSQL em segundo plano, inicio a segunda sessão da primeira sessão desta forma. Ou seja, o que farei aqui? Vou para outra sessão e digo "mostre-me o lockview para esta solicitação". E aqui tenho o AccessShareLock nesta tabela. Isso é exatamente o que eu solicitei. E ele diz que o bloco foi atribuído. Muito simples.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Além disso, se olharmos para a segunda coluna, não há nada lá. Eles estão vazios.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E se eu executar o comando "SELECT", esta é a maneira implícita (explícita) de solicitar o AccessShareLock. Então eu libero minha tabela e executo a consulta e a consulta retorna várias linhas. E em uma das linhas vemos AccessShareLock. Assim, SELECT chama AccessShareLock na tabela. E não entra em conflito com praticamente nada porque é um bloqueio de baixo nível.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E se eu executar um SELECT e tiver três tabelas diferentes? Anteriormente eu estava executando apenas uma tabela, agora estou executando três: pg_class, pg_namespace e pg_attribute.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E agora, quando olho para a consulta, vejo 9 AccessShareLocks em três tabelas. Por que? Três tabelas estão destacadas em azul: pg_attribute, pg_class, pg_namespace. Mas você também pode ver que todos os índices definidos por meio dessas tabelas também possuem AccessShareLock.

E esse é um bloqueio que praticamente não entra em conflito com os demais. E tudo o que isso faz é simplesmente nos impedir de redefinir a tabela enquanto a selecionamos. Faz sentido. Ou seja, se selecionarmos uma tabela, ela desaparece naquele momento, então isso está errado, então AccessShare é um bloqueio de baixo nível que nos diz "não deixe cair esta tabela enquanto estou trabalhando". Essencialmente, isso é tudo que ela faz.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

ROW SHARE - Este bloqueio é um pouco diferente.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Vejamos um exemplo. Método SELECT ROW SHARE para bloquear cada linha individualmente. Desta forma, ninguém pode excluí-los ou alterá-los enquanto os observamos.

Desbloqueando o Postgres Lock Manager. Bruce MomjianEntão, o que o SHARE LOCK faz? Vemos que o ID da transação é 681 para SELECT. E isso é interessante. O que aconteceu aqui? A primeira vez que vemos o número é no campo “Bloquear”. Pegamos o ID da transação e ele diz que está bloqueando em modo exclusivo. Tudo o que faz é dizer que tenho uma linha que está tecnicamente bloqueada em algum lugar da tabela. Mas ele não diz onde exatamente. Veremos isso com mais detalhes um pouco mais tarde.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Aqui dizemos que a fechadura é usada por nós.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Portanto, um bloqueio exclusivo diz explicitamente que é exclusivo. E também se você excluir uma linha desta tabela, é isso que vai acontecer, como você pode ver.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

SHARE EXCLUSIVE é um bloqueio mais longo.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Este é o comando do analisador (ANALYZE) que será usado.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

SHARE LOCK – você pode bloquear explicitamente no modo de compartilhamento.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Você também pode criar um índice exclusivo. E aí você pode ver o SHARE LOCK, que faz parte deles. E bloqueia a mesa e coloca um SHARE LOCK nela.

Por padrão, SHARE LOCK em uma tabela significa que outras pessoas podem ler a tabela, mas ninguém pode modificá-la. E é exatamente isso que acontece quando você cria um índice exclusivo.

Se eu criar um índice simultâneo exclusivo, terei um tipo diferente de bloqueio porque, como você se lembra, o uso de índices simultâneos reduz o requisito de bloqueio. E se eu usar um bloqueio normal, um índice normal, evitarei a gravação no índice da tabela enquanto ela está sendo criada. Se eu usar um índice simultaneamente, precisarei usar um tipo diferente de bloqueio.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

SHARE ROW EXCLUSIVE – novamente, pode ser definido explicitamente (explicitamente).

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Ou podemos criar uma regra, ou seja, pegar um caso específico em que ela será utilizada.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

O bloqueio EXCLUSIVO significa que ninguém mais pode alterar a tabela.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Aqui vemos diferentes tipos de fechaduras.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

ACCESS EXCLUSIVE, por exemplo, é um comando de bloqueio. Por exemplo, se você fizer CLUSTER table, isso significará que ninguém poderá escrever lá. E bloqueia não apenas a tabela em si, mas também os índices.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Esta é a segunda página do bloqueio do ACCESS EXCLUSIVE, onde vemos exatamente o que ele bloqueia na tabela. Ele bloqueia linhas individuais da tabela, o que é bastante interessante.

Essa é toda a informação básica que eu queria dar. Falamos sobre bloqueios, sobre IDs de transações, falamos sobre IDs de transações virtuais, sobre IDs de transações permanentes.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E agora veremos alguns exemplos de bloqueio. Esta é a parte mais interessante. Veremos casos muito interessantes. E meu objetivo nesta apresentação é dar a você uma melhor compreensão do que o Postgres está realmente fazendo quando tenta bloquear certas coisas. Acho que ele é muito bom em bloquear partes.

Vejamos alguns exemplos específicos.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Começaremos com tabelas e uma linha em uma tabela. Quando insiro algo, tenho ExclusiveLock, Transaction ID e ExclusiveLock exibidos na tabela.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

O que acontece se eu inserir mais duas linhas? E agora nossa tabela tem três linhas. E inseri uma linha e obtive isso como saída. E se eu inserir mais duas linhas, o que há de estranho nisso? Há uma coisa estranha aqui porque adicionei três linhas a esta tabela, mas ainda tenho duas linhas na tabela de bloqueio. E este é essencialmente o comportamento fundamental do Postgres.

Muitas pessoas pensam que se em um banco de dados você bloquear 100 linhas, será necessário criar 100 entradas de bloqueio. Se eu bloquear 1 linhas de uma vez, precisarei de 000 dessas consultas. E se eu precisar de um milhão ou um bilhão para bloquear. Mas se fizermos isso, não funcionará muito bem. Se você usou um sistema que cria entradas de bloqueio para cada linha individual, verá que isso é complicado. Porque você precisa definir imediatamente uma tabela de bloqueio que possa estourar, mas o Postgres não faz isso.

E o que é realmente importante neste slide é que ele demonstra claramente que existe outro sistema executado dentro do MVCC que bloqueia linhas individuais. Portanto, quando você bloqueia bilhões de linhas, o Postgres não cria um bilhão de comandos de bloqueio separados. E isso tem um efeito muito bom na produtividade.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Que tal uma atualização? Estou atualizando a linha agora e você pode ver que ela executou duas operações diferentes ao mesmo tempo. Bloqueou a tabela ao mesmo tempo, mas também bloqueou o índice. E ele precisava bloquear o índice porque existem restrições exclusivas nesta tabela. E queremos ter certeza de que ninguém o altere, por isso o bloqueamos.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

O que acontece se eu quiser atualizar duas linhas? E vemos que ele se comporta da mesma maneira. Fazemos o dobro de atualizações, mas exatamente o mesmo número de linhas de bloqueio.

Se você está se perguntando como o Postgres faz isso, você precisará ouvir minhas palestras no MVCC para saber como o Postgres marca internamente essas linhas que ele altera. E o Postgres tem uma maneira de fazer isso, mas não no nível de bloqueio da tabela, mas em um nível inferior e mais eficiente.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E se eu quiser excluir algo? Se eu excluir, por exemplo, uma linha e ainda tiver minhas duas entradas de bloqueio, e mesmo que eu queira excluir todas elas, elas ainda estarão lá.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E, por exemplo, quero inserir 1 linhas e, em seguida, excluir ou adicionar 000 linhas, então essas linhas individuais que adiciono ou altero não são registradas aqui. Eles são escritos em um nível inferior dentro da própria série. E durante a palestra do MVCC falei detalhadamente sobre isso. Mas é muito importante, ao analisar bloqueios, ter certeza de que você está bloqueando no nível da tabela e de que não está vendo como as linhas individuais estão sendo registradas aqui.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E quanto ao bloqueio explícito?

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Se eu clicar em atualizar, tenho duas linhas bloqueadas. E se eu selecionar todos e clicar em “atualizar em todos os lugares”, ainda terei dois registros de bloqueio.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Não criamos registros separados para cada linha individual. Porque então a produtividade cai, pode ser demais. E podemos nos encontrar em uma situação desagradável.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E a mesma coisa, se compartilharmos, podemos fazer tudo 30 vezes.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Restauramos nossa tabela, excluímos tudo e inserimos uma linha novamente.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Outro comportamento que você vê no Postgres que é muito conhecido e desejado é que você pode fazer uma atualização ou uma seleção. E você pode fazer isso ao mesmo tempo. E select não bloqueia atualização e a mesma coisa na direção oposta. Dizemos ao leitor para não bloquear o escritor, e o escritor não bloqueou o leitor.

Vou te mostrar um exemplo disso. Vou fazer uma escolha agora. Faremos então o INSERT. E então você pode ver - 694. Você pode ver o ID da transação que realizou esta inserção. E é assim que funciona.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E se eu olhar meu ID de back-end agora, agora é 695.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E posso ver 695 aparecendo na minha tabela.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E se eu atualizar aqui assim, terei um caso diferente. Nesse caso, 695 é um bloqueio exclusivo, e update tem o mesmo comportamento, mas não há conflito entre eles, o que é bastante incomum.

E você pode ver que na parte superior está o ShareLock e na parte inferior é o ExclusiveLock. E ambas as transações deram certo.

E você precisa ouvir minha palestra no MVCC para entender como isso acontece. Mas esta é uma ilustração de que você pode fazer isso ao mesmo tempo, ou seja, fazer um SELECT e um UPDATE ao mesmo tempo.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Vamos reiniciar e fazer mais uma operação.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Se você tentar executar duas atualizações simultaneamente na mesma linha, ela será bloqueada. E lembre-se, eu disse que o leitor não bloqueia o escritor, e o escritor não bloqueia o leitor, mas um escritor bloqueia outro escritor. Ou seja, não podemos permitir que duas pessoas atualizem a mesma linha ao mesmo tempo. Você tem que esperar até que um deles termine.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E para ilustrar isso, darei uma olhada na tabela Lockdemo. E veremos uma linha. Por transação 698.

Atualizamos isso para 2. 699 é a primeira atualização. E foi bem-sucedido ou está em uma transação pendente e aguarda nossa confirmação ou cancelamento.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Mas veja outra coisa: 2/51 é nossa primeira transação, nossa primeira sessão. 3/112 é a segunda solicitação que veio de cima que mudou esse valor para 3. E se você notar, a de cima travou sozinha, que é 699. Mas 3/112 não concedeu o bloqueio. A coluna Lock_mode indica o que está esperando. Ele espera 699. E se você olhar onde está 699, é mais alto. E o que a primeira sessão fez? Ela criou um bloqueio exclusivo em seu próprio ID de transação. É assim que o Postgres faz. Ele bloqueia seu próprio ID de transação. E se quiser esperar que alguém confirme ou cancele, então você precisa esperar enquanto há uma transação pendente. E é por isso que podemos ver uma linha estranha.

Vamos olhar novamente. À esquerda vemos nosso ID de processamento. Na segunda coluna vemos nosso ID de transação virtual e na terceira vemos lock_type. O que isto significa? Essencialmente, o que diz é que está bloqueando o ID da transação. Mas observe que todas as linhas na parte inferior indicam relação. E então você tem dois tipos de fechaduras na mesa. Existe um bloqueio de relacionamento. E depois há o bloqueio do transactionid, onde você bloqueia por conta própria, que é exatamente o que acontece na primeira linha ou na parte inferior, onde está o transactionid, onde esperamos que 699 termine sua operação.

Vou ver o que acontece aqui. E aqui duas coisas acontecem simultaneamente. Você está vendo um bloqueio de ID de transação na primeira linha que se bloqueia. E ela se bloqueia para fazer as pessoas esperarem.

Se você olhar para a 6ª linha, é a mesma entrada da primeira. E, portanto, a transação 699 está bloqueada. 700 também é autotravante. E então, na linha inferior, você verá que estamos aguardando que 699 termine sua operação.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E em lock_type, tupla você vê números.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Você pode ver que é 0/10. E este é o número da página e também o deslocamento desta linha específica.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E você vê que se torna 0/11 quando atualizamos.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Mas na realidade é 0/10, porque há uma espera por esta operação. Temos a oportunidade de ver que esta é a série que estou aguardando para confirmar.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Depois de confirmarmos e pressionarmos commit, e quando a atualização terminar, é isso que obtemos novamente. A transação 700 é o único bloqueio, não espera por mais ninguém porque foi confirmada. Ele simplesmente aguarda a conclusão da transação. Quando 699 acabar, não esperamos mais por nada. E agora a transação 700 diz que está tudo bem, que possui todos os bloqueios necessários em todas as tabelas permitidas.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E para complicar ainda mais tudo isso, criamos outra visão, que desta vez nos fornecerá uma hierarquia. Não espero que você entenda esse pedido. Mas isso nos dará uma visão mais clara do que está acontecendo.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Esta é uma visão recursiva que também possui outra seção. E então reúne tudo novamente. Vamos usar isso.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E se fizermos três atualizações simultâneas e dissermos que a linha agora é três. E vamos mudar 3 para 4.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E aqui vemos 4. E o ID da transação 702.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E então vou mudar 4 para 5. E 5 para 6, e 6 para 7. E vou alinhar um número de pessoas que estarão esperando que esta transação termine.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E tudo fica claro. Qual é a primeira linha? Este é 702. Este é o ID da transação que originalmente definiu este valor. O que está escrito na minha coluna Concedido? eu tenho marcas f. Estas são minhas atualizações que (5, 6, 7) não podem ser aprovadas porque estamos aguardando o término da transação ID 702. Aí temos bloqueio de ID de transação. E isso resulta em 5 bloqueios de ID transacional.

E se você olhar para 704, para 705, nada foi escrito lá ainda, porque eles ainda não sabem o que está acontecendo. Eles simplesmente escrevem que não têm ideia do que está acontecendo. E eles só vão dormir porque estão esperando alguém terminar e serem acordados quando houver oportunidade de trocar de linha.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Isto é o que parece. É claro que todos estão esperando a 12ª linha.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Isto é o que vimos aqui. Aqui está 0/12.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Assim que a primeira transação for aprovada, você poderá ver aqui como funciona a hierarquia. E agora tudo fica claro. Todos eles ficam limpos. E eles ainda estão esperando.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Aqui está o que está acontecendo. 702 confirmações. E agora 703 obtém esse bloqueio de linha e então 704 começa a esperar que 703 seja confirmado. E o 705 também está esperando por isso. E quando tudo isso termina, eles se limpam. E gostaria de ressaltar que todos estão fazendo fila. E isso é muito parecido com uma situação de engarrafamento quando todos estão esperando o primeiro carro. O primeiro carro para e todos fazem fila. Então ele se move, então o próximo carro pode seguir em frente e bloquear, etc.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E se isso não lhe pareceu complicado o suficiente, então falaremos agora sobre impasses. Não sei qual de vocês os encontrou. Este é um problema bastante comum em sistemas de banco de dados. Mas os impasses ocorrem quando uma sessão espera que outra sessão faça alguma coisa. E neste momento outra sessão aguarda a primeira sessão para fazer alguma coisa.

E, por exemplo, se o Ivan disser: “Dê-me alguma coisa”, e eu disser: “Não, só vou te dar se você me der outra coisa”. E ele diz: “Não, não vou dar a você se você não der para mim”. E acabamos em uma situação de impasse. Tenho certeza de que Ivan não fará isso, mas você entende o significado de que temos duas pessoas que desejam obter algo e não estão prontas para doá-lo até que a outra pessoa lhes dê o que desejam. E não há solução.

E essencialmente, seu banco de dados precisa detectar isso. E então você precisa excluir ou fechar uma das sessões, caso contrário elas permanecerão lá para sempre. E vemos isso em bancos de dados, vemos isso em sistemas operacionais. E em todos os locais onde temos processos paralelos isso pode acontecer.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E agora instalaremos dois impasses. Colocaremos 50 e 80. Na primeira linha, atualizarei de 50 para 50. Obterei o número da transação 710.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E então mudarei 80 para 81 e 50 para 51.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E é assim que será. E assim 710 tem uma linha bloqueada e 711 está aguardando confirmação. Vimos isso quando atualizamos. 710 é o dono da nossa série. E 711 espera que 710 conclua a transação.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E ainda diz em qual linha ocorrem os impasses. E é aqui que tudo começa a ficar estranho.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Agora estamos atualizando 80 para 80.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E é aqui que começam os impasses. 710 está aguardando uma resposta de 711 e 711 está aguardando 710. E isso não vai acabar bem. E não há como escapar disso. E eles esperarão uma resposta um do outro.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E vai começar a atrasar tudo. E não queremos isso.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E o Postgres tem maneiras de perceber quando isso acontece. E quando isso acontece, você recebe esse erro. E a partir disso fica claro que tal e tal processo está aguardando um SHARE LOCK de outro processo, ou seja, que está bloqueado pelo processo 711. E esse processo estava esperando que um SHARE LOCK fosse dado em tal e tal ID de transação e foi bloqueado por tal e tal processo. Portanto, há aqui uma situação de impasse.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Existem impasses de três vias? É possível? Sim.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Inserimos esses números em uma tabela. Mudamos 40 para 40, fazemos bloqueio.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Mudamos 60 para 61, 80 para 81.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E então mudamos 80 e depois bum!

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E o 714 está agora à espera do 715. O 716 está à espera do 715. E nada pode ser feito a respeito.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Já não tem duas pessoas aqui, já tem três pessoas aqui. Eu quero algo de você, este quer algo de uma terceira pessoa, e a terceira quer algo de mim. E acabamos numa espera tripla porque todos estamos esperando que a outra pessoa conclua o que precisa fazer.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E o Postgres sabe em qual linha isso acontece. E assim você receberá a seguinte mensagem, que mostra que você tem um problema onde três entradas estão bloqueando uma à outra. E não há restrições aqui. Este pode ser o caso quando 20 entradas bloqueiam umas às outras.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

O próximo problema é serializável.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Se for um bloqueio serializável especial.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E voltamos ao 719. Sua saída é bastante normal.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E você pode clicar para tornar a transação serializável.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E você percebe que agora tem um tipo diferente de bloqueio SA - significa serializável.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E assim temos um novo tipo de bloqueio chamado SARieadLock, que é um bloqueio serial e permite inserir números seriais.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E também você pode inserir índices exclusivos.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Nesta tabela temos índices únicos.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Então, se eu colocar o número 2 aqui, tenho um 2. Mas bem no topo, coloco outro 2. E você pode ver que 721 tem uma fechadura exclusiva. Mas agora 722 está aguardando que 721 complete sua operação porque não pode inserir 2 até saber o que acontecerá com 721.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E se fizermos subtransação.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Aqui temos 723.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E se salvarmos o ponto e atualizá-lo, obteremos um novo ID de transação. Este é outro padrão de comportamento do qual você precisa estar ciente. Se retornarmos isso, o ID da transação desaparecerá. 724 está saindo. Mas agora temos 725.

Então, o que estou tentando fazer aqui? Estou tentando mostrar exemplos de bloqueios incomuns que você pode encontrar: sejam bloqueios serializáveis ​​ou SAVEPOINT, esses são diferentes tipos de bloqueios que aparecerão na tabela de bloqueios.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Esta é a criação de bloqueios explícitos (explícitos), que possuem pg_advisory_lock.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E você vê que o tipo de bloqueio está listado como consultivo. E aqui diz “aconselhamento” em vermelho. E você pode bloquear simultaneamente assim com pg_advisory_unlock.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E para concluir, gostaria de mostrar mais uma coisa alucinante. Vou criar outra visualização. Mas vou juntar a tabela pg_locks com a tabela pg_stat_activity. E por que eu quero fazer isso? Porque isso me permitirá ver todas as sessões atuais e ver exatamente que tipo de bloqueio elas estão esperando. E isso é bastante interessante quando juntamos a tabela de bloqueio e a tabela de consulta.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E aqui criamos pg_stat_view.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E atualizamos a linha em um. E aqui vemos 724. E então atualizamos a nossa linha para três. E o que você vê aqui agora? Estas são solicitações, ou seja, você vê toda a lista de solicitações listadas na coluna da esquerda. E então, no lado direito, você pode ver os bloqueios e o que eles criam. E pode ficar mais claro para você, para que não precise voltar a cada sessão todas as vezes e ver se precisa participar ou não. Eles fazem isso por nós.

Outro recurso muito útil é pg_blocking_pids. Você provavelmente nunca ouviu falar dela. O que ela esta fazendo? Isso nos permite dizer para esta sessão 11740 quais IDs de processo específicos ela está aguardando. E você pode ver que 11740 está esperando por 724. E 724 está no topo. E 11306 é o seu ID do processo. Essencialmente, esta função passa pela sua tabela de bloqueio. E eu sei que é um pouco complicado, mas você consegue entender. Essencialmente, esta função percorre esta tabela de bloqueios e tenta descobrir onde esse ID de processo recebe os bloqueios que está aguardando. E também tenta descobrir qual ID de processo possui o processo que está aguardando o bloqueio. Então você pode executar esta função pg_blocking_pids.

E isso pode ser muito útil. Adicionamos isso apenas na versão 9.6, então esse recurso tem apenas 5 anos, mas é muito, muito útil. E o mesmo se aplica ao segundo pedido. Mostra exatamente o que precisamos ver.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

É sobre isso que eu queria falar com você. E como eu esperava, gastamos todo o nosso tempo porque havia muitos slides. E os slides estão disponíveis para download. Gostaria de agradecer por estar aqui. Tenho certeza que você aproveitará o resto da conferência, muito obrigado!

Perguntas:

Por exemplo, se estou tentando atualizar linhas e a segunda sessão está tentando excluir a tabela inteira. Pelo que entendi, deveria haver algo como um bloqueio intencional. Existe tal coisa no Postgres?

Desbloqueando o Postgres Lock Manager. Bruce Momjian

Voltemos ao início. Você deve se lembrar que quando você faz alguma coisa, por exemplo, quando você faz um SELECT, emitimos um AccessShareLock. E isso evita que a tabela seja descartada. Portanto, se você, por exemplo, deseja atualizar uma linha em uma tabela ou excluir uma linha, alguém não poderá excluir a tabela inteira ao mesmo tempo porque você está mantendo esse AccessShareLock sobre toda a tabela e sobre a linha. E quando terminar, eles poderão excluí-lo. Mas enquanto você mudar algo diretamente lá, eles não serão capazes de fazer isso.

Vamos fazer de novo. Vamos passar para o exemplo de exclusão. E você vê como existe um bloqueio exclusivo na linha acima de toda a tabela.

Isso vai parecer um bloqueio exclusivo, certo?

Sim, parece. Eu entendo o que você está falando. Você está dizendo que se eu fizer um SELECT então eu tenho um ShareExclusive e depois faço Row Exclusive, isso se torna um problema? Mas surpreendentemente isso não representa um problema. Parece aumentar o grau de bloqueio, mas essencialmente tenho um bloqueio que impede a exclusão. E agora, quando deixo esse bloqueio mais poderoso, ele ainda impede a exclusão. Então não é como se eu estivesse subindo. Ou seja, evitou que isso acontecesse quando estava em um nível inferior também, então quando subo o nível dele ainda evita que a tabela seja deletada.

Eu entendo o que você está falando. Não há nenhum caso de escalonamento de bloqueio aqui, onde você está tentando desistir de um bloqueio para introduzir um mais forte. Aqui apenas aumenta essa prevenção de forma generalizada, para não causar nenhum conflito. Mas é uma boa pergunta. Muito obrigado por perguntar isso!

O que precisamos fazer para evitar uma situação de impasse quando temos muitas sessões, um grande número de usuários?

O Postgres percebe automaticamente situações de impasse. E excluirá automaticamente uma das sessões. A única maneira de evitar o bloqueio morto é bloquear as pessoas na mesma ordem. Então quando você olha para sua aplicação, muitas vezes o motivo dos deadlocks... Vamos imaginar que eu queira bloquear duas coisas diferentes. Um aplicativo bloqueia a tabela 1 e outro bloqueia a 2 e, em seguida, a tabela 1. E a maneira mais fácil de evitar conflitos é examinar seu aplicativo e tentar garantir que o bloqueio ocorra na mesma ordem em todos os aplicativos. E isso geralmente elimina 80% dos problemas, porque todos os tipos de pessoas escrevem esses aplicativos. E se você bloqueá-los na mesma ordem, não encontrará uma situação de impasse.

Muito obrigado pelo seu desempenho! Você falou sobre vácuo completo e, se bem entendi, vácuo completo distorce a ordem dos registros em armazenamento separado, de modo que eles mantêm os registros atuais inalterados. Por que o vácuo completo tem acesso exclusivo ao bloqueio e por que entra em conflito com as operações de gravação?

Esta é uma boa pergunta. A razão é que o vácuo total toma conta da mesa. E estamos essencialmente criando uma nova versão da tabela. E a mesa será nova. Acontece que esta será uma versão completamente nova da tabela. E o problema é que quando fazemos isso, não queremos que as pessoas leiam porque precisamos que vejam a nova tabela. E isso se conecta à pergunta anterior. Se pudéssemos ler ao mesmo tempo, não conseguiríamos movê-lo e direcionar as pessoas para uma nova mesa. Precisaríamos esperar que todos terminassem de ler esta tabela e, portanto, é essencialmente uma situação de bloqueio exclusivo.
Dizemos apenas que bloqueamos desde o início porque sabemos que no final precisaremos de um bloqueio exclusivo para mover todos para a nova cópia. Portanto, podemos potencialmente resolver isso. E fazemos assim com indexação simultânea. Mas isso é muito mais difícil de fazer. E isso está muito relacionado à sua pergunta anterior sobre bloqueio exclusivo.

É possível adicionar tempo limite de bloqueio ao Postgres? No Oracle posso, por exemplo, escrever "select to update" e esperar 50 segundos antes de atualizar. Foi bom para a aplicação. Mas no Postgres, ou preciso fazer isso imediatamente e não esperar nada, ou esperar até algum tempo.

Sim, você pode escolher um tempo limite em seus bloqueios. Você também pode emitir um comando de proibição, o que acontecerá... se você não conseguir obter o bloqueio imediatamente. Portanto, um tempo limite de bloqueio ou outra coisa que permitirá que você faça isso. Isso não é feito no nível sintático. Isso é feito como uma variável no servidor. Às vezes isso não pode ser usado.

Você pode abrir o slide 75?

Sim.

Desbloqueando o Postgres Lock Manager. Bruce Momjian

E minha pergunta é a seguinte. Por que ambos os processos de atualização esperam 703?

E esta é uma ótima pergunta. A propósito, não entendo por que o Postgres faz isso. Mas quando o 703 foi criado, ele esperava o 702. E quando o 704 e o 705 aparecem, parece que eles não sabem o que estão esperando porque ainda não há nada lá. E o Postgres faz assim: quando você não consegue um bloqueio, ele escreve “Qual o sentido de processar você?”, porque você já está esperando por alguém. Então, vamos deixá-lo no ar, não será atualizado de forma alguma. Mas o que aconteceu aqui? Assim que 702 concluiu o processo e 703 recebeu seu bloqueio, o sistema retornou. E ela disse que agora temos duas pessoas esperando. E então vamos atualizá-los juntos. E indiquemos que ambos estão esperando.

Não sei por que o Postgres faz isso. Mas há um problema chamado f…. Parece-me que este não é um termo em russo. É quando todos estão esperando por um castelo, mesmo que haja 20 autoridades esperando pelo castelo. E de repente todos acordam ao mesmo tempo. E todos começam a tentar reagir. Mas o sistema faz com que todos fiquem esperando pelo 703. Porque estão todos esperando, e imediatamente vamos alinhá-los todos. E se aparecer alguma outra nova solicitação que foi gerada depois dessa, por exemplo, 707, então haverá vazio novamente.

E parece-me que isto é feito para que possamos dizer que nesta fase o 702 está à espera do 703, e todos os que vierem depois não terão qualquer entrada neste campo. Mas assim que o primeiro garçom sai, todos aqueles que estavam aguardando naquele momento antes da atualização recebem o mesmo token. E então eu acho que isso é feito para que possamos processar de forma que eles estejam devidamente ordenados.

Sempre considerei isso um fenômeno bastante estranho. Porque aqui, por exemplo, não os listamos de forma alguma. Mas parece-me que cada vez que damos um novo cadeado, olhamos para todos aqueles que estão em processo de espera. Então alinhamos todos eles. E então qualquer novo que chega só entra na fila quando a próxima pessoa termina de ser processada. Muito boa pergunta. Muito obrigado pela sua pergunta!

Parece-me muito mais lógico quando 705 espera 704.

Mas o problema aqui é o seguinte. Tecnicamente, você pode acordar um ou outro. E assim vamos acordar um ou outro. Mas o que acontece no sistema? Você pode ver como 703 no topo bloqueou seu próprio ID de transação. É assim que o Postgres funciona. E 703 é bloqueado por seu próprio ID de transação, então se alguém quiser esperar, então esperará por 703. E, em essência, 703 é concluído. E somente após sua conclusão um dos processos desperta. E não sabemos exatamente como será esse processo. Então processamos tudo gradualmente. Mas não está claro qual processo é despertado primeiro, porque poderia ser qualquer um destes processos. Essencialmente, tínhamos um agendador que dizia que agora podemos ativar qualquer um desses processos. Nós apenas escolhemos um aleatoriamente. Portanto, ambos precisam ser observados porque podemos despertar qualquer um deles.

E o problema é que temos CP-infinito. E, portanto, é bem provável que possamos acordar mais tarde. E se, por exemplo, despertarmos o último, esperaremos por quem acabou de receber o bloqueio, para não determinarmos quem exatamente será despertado primeiro. Simplesmente criamos tal situação e o sistema irá despertá-los em ordem aleatória.

Tem artigos sobre fechaduras por Egor Rogov. Olha, eles também são interessantes e úteis. O tema, claro, é terrivelmente complexo. Muito obrigado, Bruce!

Fonte: habr.com

Adicionar um comentário