AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 334904
Accepted
James Hay
James Hay
Asked: 2024-01-16 19:39:32 +0800 CST2024-01-16 19:39:32 +0800 CST 2024-01-16 19:39:32 +0800 CST

Existe uma penalidade de desempenho para violações de restrições de chave exclusivas no Postgres?

  • 772

Na minha API, o usuário pode enviar uma solicitação que tenta criar uma nova linha quando existe uma linha com essa chave exclusiva.

Atualmente, estou detectando o erro de chave exclusiva e retornando uma mensagem informando que X já existe. Mas é mais eficiente pesquisar a linha primeiro (na mesma conexão) e executar a instrução INSERT apenas se essa linha não existir?

Minha intuição diz que ler o erro do Postgres deveria ser mais eficiente, mas gostaria de ter certeza de que estou fazendo as coisas de maneira idiomática.

Postgres é a versão 12

postgresql
  • 3 3 respostas
  • 457 Views

3 respostas

  • Voted
  1. J.D.
    2024-01-16T21:48:11+08:002024-01-16T21:48:11+08:00

    Mas é mais eficiente pesquisar a linha primeiro (na mesma conexão) e executar a instrução INSERT apenas se essa linha não existir?

    Independentemente de ter melhor desempenho ou não, não há garantia de precisão sem bloquear a tabela inteira desde o momento da pesquisa até o momento do INSERT. Sem bloquear a tabela, alguém pode, teoricamente, usar INSERTa mesma chave de dados entre o momento da verificação e o momento da verificação INSERT(mesmo que eles estejam separados por nanossegundos). Nesse ritmo, provavelmente terá menos desempenho para todo o sistema, de uma perspectiva holística, do que confiar apenas na restrição de chave única.

    • 7
  2. Best Answer
    bobflux
    2024-01-17T04:57:35+08:002024-01-17T04:57:35+08:00

    é mais eficiente pesquisar a linha primeiro (na mesma conexão) e executar a instrução INSERT apenas se essa linha não existir?

    Se alguém inserir uma duplicata simultaneamente, não há problema: o select não a verá, mas a restrição exclusiva ainda a capturará. Você ainda precisa duplicar seu código de tratamento de erros. No entanto, se alguém excluir a duplicata depois que o select a viu, ela não será inserida.

    Executei um benchmark python, o código fonte está disponível em pastebin . Este é um exemplo simples usando uma tabela com apenas uma chave primária e uma coluna de texto fictícia. Para cada id no intervalo 0..99, ele insere 100 vezes. Somente a primeira vez funcionará, o restante será rejeitado pela restrição única.

    Os candidatos são:

    • insert_only: envia a inserção, então funciona ou falha na restrição exclusiva.

    • select_then_insert: seleciona para verificar e depois insere.

    • insert_select combina as duas consultas anteriores em uma, o que também remove a condição de corrida:

      INSERT INTO testins (id,t) SELECT %s,'olá, mundo' WHERE NOT EXISTS( SELECT FROM testins WHERE id=%s ) RETURNING id

    • on_conflict usa o recurso upsert:

      INSERT INTO testins (id,t) VALUES (%s,'hello, world') ON CONFLICT (id) NÃO FAÇA NADA RETURNING id

    "RETURNING id" simplesmente retorna o id se a linha foi inserida, então você sabe que foi. Se a consulta não retornar nada, significa que houve uma duplicata.

    Resultados (com tamanho de tabela):

    on_conflict: 0.683s 100 rows      8.000 kB
    insert_only: 0.850s 100 rows    512.000 kB
    select_then_insert: 0.736s 100 rows      8.000 kB
    insert_select: 0.615s 100 rows      8.000 kB
    

    Este teste possui 99 duplicatas para cada inserção, então vamos tentar uma quantidade mais razoável de 1 duplicata por inserção:

    on_conflict: 0.743s 5000 rows    256.000 kB
    insert_only: 0.782s 5000 rows    512.000 kB
    select_then_insert: 0.943s 5000 rows    256.000 kB
    insert_select: 0.668s 5000 rows    256.000 kB
    

    Sem duplicatas:

    on_conflict: 0.816s 10000 rows    512.000 kB
    insert_only: 0.691s 10000 rows    512.000 kB
    select_then_insert: 1.184s 10000 rows    512.000 kB
    insert_select: 0.777s 10000 rows    512.000 kB
    

    Em todos os casos, a maior parte do tempo é gasta em viagens de ida e volta na conexão e na confirmação de transações.

    Conclusão:

    O problema com o INSERT direto é o fato de que ele ainda grava a linha na tabela, depois tenta escrevê-la no índice e falha em uma duplicata, depois reverte a transação. Isso resulta em gravações em disco (tabela e WAL) e incha a tabela com linhas mortas que precisarão de VACUUMing. Fazer tudo isso explica a pequena penalidade de desempenho.

    As outras soluções não inserem a linha se houver duplicata, o que evita gravações inúteis e inchaço da tabela.

    O mais idiomático para o postgres seria ON CONFLICT.

    Portanto, se você espera ter muitas duplicatas, ou seja, na maioria das vezes o INSERT falhará e o tráfego nesta consulta for alto, seria vantajoso usar ON CONFLICT.

    Se você espera poucas duplicatas, ou seja, na maioria das vezes o INSERT funcionará, então você pode simplesmente deixar que ele gere o erro.

    Se isso fizer parte de uma transação maior que você prefere não falhar, reverter e fazer todo o trabalho novamente, ON CONFLICT pode ajudar, pois não gerará um erro em caso de duplicata.

    • 5
  3. Quassnoi
    2024-01-17T05:28:47+08:002024-01-17T05:28:47+08:00

    No PostgreSQL, restrições exclusivas são implementadas inserindo primeiro o registro e, em seguida, revertendo-o se violar a restrição.

    Se sua restrição for adiada, a entrada B-Tree duplicada também será inserida por enquanto e um gatilho interno chamado unique_key_recheckserá executado para verificar se os registros recém-inseridos não violam a restrição.

    Se parece com isso:

    test=# CREATE TABLE mytable (id INT NOT NULL, value TEXT NOT NULL, CONSTRAINT ux_mytable_id UNIQUE (id) DEFERRABLE INITIALLY DEFERRED);
    CREATE TABLE
    test=# INSERT INTO mytable VALUES (1, 'test');
    INSERT 0 1
    test=# INSERT INTO mytable VALUES (1, 'test2');
    ERROR:  duplicate key value violates unique constraint "ux_mytable_id"
    DETAIL:  Key (id)=(1) already exists.
    test=# SELECT * FROM heap_page_items(get_raw_page('mytable', 0));
     lp | lp_off | lp_flags | lp_len | t_xmin | t_xmax | t_field3 | t_ctid | t_infomask2 | t_infomask | t_hoff | t_bits | t_oid |         t_data
    ----+--------+----------+--------+--------+--------+----------+--------+-------------+------------+--------+--------+-------+------------------------
      1 |   8152 |        1 |     33 |    759 |      0 |        0 | (0,1)  |           2 |       2306 |     24 |        |       | \x010000000b74657374
      2 |   8112 |        1 |     34 |    760 |      0 |        0 | (0,2)  |           2 |       2050 |     24 |        |       | \x010000000d7465737432
    (2 rows)
    
    test=# SELECT * FROM bt_page_items('ux_mytable_id', 1);
     itemoffset | ctid  | itemlen | nulls | vars |          data           | dead | htid  | tids
    ------------+-------+---------+-------+------+-------------------------+------+-------+------
              1 | (0,1) |      16 | f     | f    | 01 00 00 00 00 00 00 00 | f    | (0,1) |
              2 | (0,2) |      16 | f     | f    | 01 00 00 00 00 00 00 00 | f    | (0,2) |
    (2 rows)
    

    Os segundos registros nesses conjuntos de resultados são registros inativos cujo backup foi revertido quando sua restrição falhou. Esses registros sobrecarregam o espaço de tabela e o WAL.

    Então, respondendo diretamente à sua pergunta: sim, há coisas que afetam o desempenho geral que acontecem quando você viola a restrição e não acontecem quando você não o faz.

    Minha intuição diz que ler o erro do Postgres deveria ser mais eficiente

    Isso depende da frequência com que essas violações de restrições acontecem.

    Ler o registro com antecedência requer percorrer a árvore B duas vezes, e se sua taxa de erro for muito baixa (como deveria ser), pode valer a pena sofrer o impacto de desempenho de entrada morta de vez em quando, em vez de fazer a verificação em cada inserção.

    Observe que em um sistema projetado adequadamente, a restrição exclusiva deve existir independentemente de você verificá-la antecipadamente ou não.

    • 3

relate perguntas

  • Posso ativar o PITR depois que o banco de dados foi usado

  • Práticas recomendadas para executar a replicação atrasada do deslocamento de tempo

  • Os procedimentos armazenados impedem a injeção de SQL?

  • Sequências Biológicas do UniProt no PostgreSQL

  • Qual é a diferença entre a replicação do PostgreSQL 9.0 e o Slony-I?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve