Optimizando actualizaciones concurrentes en Postgres

9

Estoy ejecutando consultas concurrentes de Postgres como esta:

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

Cada consulta afecta el número fijo K de filas, y no puedo encontrar una manera de imponer el orden en que se actualizan las filas, termino con puntos muertos. Actualmente soluciono el problema aplicando el orden a mano, pero esto significa que tengo que ejecutar muchas más consultas de las que normalmente haría al tiempo que aumentaba la complejidad de búsqueda de O (log N + K) a O (K log N).

¿Hay alguna manera de mejorar el rendimiento sin llegar a ser vulnerable a puntos muertos? Sospecho que reemplazar el (baz)índice con el (baz, id)índice podría funcionar siempre que Postgres actualice las filas en el mismo orden en que las ha escaneado, ¿vale la pena seguir este enfoque?

Alexei Averchenko
fuente
Te sugiero que agregues el CREATE TABLEcódigo.
ypercubeᵀᴹ

Respuestas:

15

No hay ORDER BYen un SQL UPDATEcomando. Postgres actualiza las filas en orden arbitrario:

Para evitar puntos muertos con absoluta certeza, puede ejecutar sus declaraciones en aislamiento de transacciones serializables . Pero eso es más costoso y debe prepararse para repetir comandos en caso de falla de serialización.

Su mejor curso de acción es probablemente bloquear explícitamente SELECT ... ORDER BY ... FOR UPDATEen una subconsulta o SELECTen una transacción independiente, en el nivel de aislamiento predeterminado "lectura confirmada". Citando a Tom Lane en pgsql-general :

Debería estar bien --- el bloqueo FOR UPDATE siempre es el último paso en la tubería SELECT.

Esto debería hacer el trabajo:

BEGIN;

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

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

COMMIT;

Un índice de varias columnas (baz, bar)puede ser perfecto para el rendimiento. Pero como barobviamente se actualiza mucho , un índice de una sola columna (baz)podría ser aún mejor. Depende de un par de factores. ¿Cuántas filas por baz? ¿Son posibles las actualizaciones HOT sin el índice de varias columnas? ...

Si baz se actualiza al mismo tiempo, todavía hay una posibilidad improbable de conflictos en el caso de la esquina (según la documentación) :

Es posible que un SELECTcomando se ejecute a READ COMMITTED nivel de aislamiento de transacción y use ORDER BYuna cláusula de bloqueo para devolver las filas fuera de servicio. ...

Además, si debe tener una restricción única que implique bar, considere una DEFERRABLErestricción para evitar violaciones únicas dentro del mismo comando. Respuesta relacionada:

Erwin Brandstetter
fuente
1
SI estoy ordenando por ido alguna otra columna única en lugar de bar, no debería haber un caso de esquina o un golpe de rendimiento, ¿verdad?
Alexei Averchenko
@AlexeiAverchenko: Sí, una columna única que nunca se actualiza sería perfecta para esto, y un índice de varias columnas que incluye esta columna en la segunda posición.
Erwin Brandstetter