Otimizando atualizações simultâneas no Postgres

9

Estou executando consultas simultâneas do Postgres como esta:

UPDATE foo SET bar = bar + 1 WHERE baz = 1234

Cada consulta afeta o número fixo de K linhas e, não consigo encontrar uma maneira de impor a ordem na qual as linhas são atualizadas, acabo com impasses. Atualmente, eu corrijo o problema aplicando a ordem manualmente, mas isso significa que tenho que executar muito mais consultas do que normalmente faria, além de aumentar a complexidade da pesquisa de O (log N + K) para O (K log N).

Existe uma maneira de melhorar o desempenho sem acabar vulnerável a conflitos? Eu suspeito que a substituição do (baz)índice pelo (baz, id)índice possa funcionar, desde que o Postgres atualize as linhas na mesma ordem em que foram verificadas. É uma abordagem que vale a pena seguir?

Alexei Averchenko
fonte
Eu sugiro que você adicione o CREATE TABLEcódigo.
ypercubeᵀᴹ

Respostas:

15

Não existe ORDER BYum SQL UPDATEcomando. O Postgres atualiza as linhas em ordem arbitrária:

Para evitar conflitos com certeza absoluta, você pode executar suas instruções em isolamento de transação serializável . Mas isso é mais caro e você precisa se preparar para repetir comandos na falha de serialização.

Seu melhor curso de ação é provavelmente bloquear explicitamente SELECT ... ORDER BY ... FOR UPDATEem uma subconsulta ou autônomo SELECTem uma transação - no nível de isolamento "leitura confirmada" padrão. Citando Tom Lane no pgsql-general :

Deve estar tudo bem: o bloqueio FOR UPDATE é sempre a última etapa no pipeline SELECT.

Isso deve fazer o trabalho:

BEGIN;

SELECT 1
FROM   foo 
WHERE  baz = 1234
ORDER  BY bar
FOR    UPDATE;

UPDATE foo
SET    bar = bar + 1
WHERE  baz = 1234;

COMMIT;

Um índice de várias colunas ativado (baz, bar)pode ser perfeito para desempenho. Mas como baré obviamente atualizado muito , um índice de coluna única (baz)pode ser ainda melhor. Depende de alguns fatores. Quantas linhas por baz? As atualizações HOT são possíveis sem o índice de várias colunas? ...

Se baz for atualizado simultaneamente, ainda há uma chance improvável de ocorrência de conflitos (por documentação) :

É possível que um SELECTcomando em execução no READ COMMITTED nível de isolamento da transação e usando ORDER BYuma cláusula de bloqueio retorne as linhas fora de ordem. ...

Além disso, se você tiver uma restrição exclusiva bar, considere uma DEFERRABLErestrição para evitar violações exclusivas dentro do mesmo comando. Resposta relacionada:

Erwin Brandstetter
fonte
11
Se estou pedindo uma idou outra coluna única em vez de bar, não deve haver uma caixa de canto ou uma ocorrência de desempenho, certo?
Alexei Averchenko
@AlexeiAverchenko: Sim, uma coluna única que nunca é atualizada seria perfeita para isso - e um índice de várias colunas incluindo essa coluna na segunda posição.
Erwin Brandstetter