PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Como o COPY funciona e por que é muito mais rápido que o INSERT?


Há uma série de fatores em ação aqui:
  • Latência de rede e atrasos de ida e volta
  • Custos gerais por instrução no PostgreSQL
  • Mudanças de contexto e atrasos do agendador
  • COMMIT custos, se para pessoas fazendo um commit por inserção (você não está)
  • COPY -otimizações específicas para carregamento em massa

Latência de rede


Se o servidor for remoto, você pode estar "pagando" um "preço" de tempo fixo por declaração de, digamos, 50 ms (1/20 de segundo). Ou muito mais para alguns bancos de dados hospedados na nuvem. Como a próxima inserção não pode começar até que a última seja concluída com sucesso, isso significa que seu máximo a taxa de inserções é de 1.000/latência de ida e volta em linhas por segundo. Com uma latência de 50ms ("tempo de ping"), são 20 linhas/segundo. Mesmo em um servidor local, esse atraso é diferente de zero. Considerando que COPY apenas preenche as janelas de envio e recebimento do TCP e transmite as linhas tão rápido quanto o banco de dados pode escrevê-las e a rede pode transferi-las. Ele não é muito afetado pela latência e pode estar inserindo milhares de linhas por segundo no mesmo link de rede.

Custos por declaração no PostgreSQL


Há também custos para analisar, planejar e executar uma instrução no PostgreSQL. Ele deve receber bloqueios, abrir arquivos de relação, procurar índices, etc. COPY tenta fazer tudo isso uma vez, no início, então apenas se concentre em carregar as linhas o mais rápido possível.

Custos de mudança de tarefa/contexto


Há mais custos de tempo pagos devido ao sistema operacional ter que alternar entre postgres aguardando uma linha enquanto seu aplicativo a prepara e envia e, em seguida, seu aplicativo aguarda a resposta do postgres enquanto o postgres processa a linha. Toda vez que você muda de um para o outro, você perde um pouco de tempo. Mais tempo é potencialmente desperdiçado suspendendo e retomando vários estados de kernel de baixo nível quando os processos entram e saem dos estados de espera.

Perdendo otimizações de COPY


Além de tudo isso, COPY tem algumas otimizações que pode usar para alguns tipos de cargas. Se não houver nenhuma chave gerada e quaisquer valores padrão forem constantes, por exemplo, ele pode pré-calculá-los e ignorar o executor completamente, carregando rapidamente os dados na tabela em um nível inferior que ignora completamente parte do trabalho normal do PostgreSQL. Se você CREATE TABLE ou TRUNCATE na mesma transação você COPY , ele pode fazer ainda mais truques para tornar a carga mais rápida, ignorando a contabilidade de transações normal necessária em um banco de dados multicliente.

Apesar disso, o COPY do PostgreSQL ainda pode fazer muito mais para acelerar as coisas, coisas que ainda não sabe fazer. Ele pode pular automaticamente as atualizações de índice e reconstruir os índices se você estiver alterando mais do que uma certa proporção da tabela. Ele poderia fazer atualizações de índice em lotes. Muito mais.

Custos de compromisso


Uma última coisa a considerar é comprometer os custos. Provavelmente não é um problema para você porque psycopg2 padrão para abrir uma transação e não confirmar até que você diga. A menos que você tenha dito para usar o autocommit. Mas, para muitos drivers de banco de dados, o autocommit é o padrão. Nesses casos, você faria um commit para cada INSERT . Isso significa uma liberação de disco, em que o servidor garante que todos os dados da memória sejam gravados no disco e instrua os discos a gravar seus próprios caches no armazenamento persistente. Isso pode levar longo tempo, e varia muito com base no hardware. Meu laptop NVMe BTRFS baseado em SSD pode fazer apenas 200 fsyncs/segundo, contra 300.000 gravações não sincronizadas/segundo. Portanto, ele carregará apenas 200 linhas/segundo! Alguns servidores só podem fazer 50 fsyncs/segundo. Alguns podem fazer 20.000. Portanto, se você precisar confirmar regularmente, tente carregar e confirmar em lotes, fazer inserções de várias linhas etc. Porque COPY apenas um commit no final, os custos de commit são insignificantes. Mas isso também significa COPY não pode se recuperar de erros no meio dos dados; ele desfaz toda a carga em massa.