Antipatrones de PostgreSQL: luchando contra las hordas de “muertos”

Las peculiaridades de los mecanismos internos de PostgreSQL le permiten ser muy rápido en algunas situaciones y “no muy rápido” en otras. Hoy nos centraremos en un ejemplo clásico de conflicto entre cómo funciona un DBMS y lo que hace el desarrollador con él: ACTUALIZACIÓN vs principios MVCC.

Breve historia de Excelente artículo:

Cuando se modifica una fila mediante un comando ACTUALIZAR, en realidad se realizan dos operaciones: ELIMINAR e INSERTAR. EN versión actual de la cadena xmax se establece igual al número de la transacción que realizó la ACTUALIZACIÓN. Entonces se crea nueva versión la misma línea; su valor xmin coincide con el valor xmax de la versión anterior.

Algún tiempo después de que se complete esta transacción, la versión antigua o nueva, dependiendo de COMMIT/ROOLBACK, será reconocido "muerto" (tuplas muertas) al pasar VACUUM según la tabla y despejado.

Antipatrones de PostgreSQL: luchando contra las hordas de “muertos”

Pero esto no sucederá de inmediato, pero los problemas con los "muertos" pueden adquirirse muy rápidamente, con repetidas o actualización masiva de registros en una mesa grande, y un poco más tarde te encontrarás con la misma situación VACUUM no podrá ayudar.

#1: Me gusta moverlo

Digamos que su método está funcionando en la lógica de negocios y de repente se da cuenta de que sería necesario actualizar el campo X en algún registro:

UPDATE tbl SET X = <newX> WHERE pk = $1;

Luego, a medida que avanza la ejecución, resulta que el campo Y también debe actualizarse:

UPDATE tbl SET Y = <newY> WHERE pk = $1;

... y luego también Z: ¿por qué perder el tiempo en nimiedades?

UPDATE tbl SET Z = <newZ> WHERE pk = $1;

¿Cuántas versiones de este registro tenemos ahora en la base de datos? ¡Sí, 4 piezas! De estos, uno es relevante y 3 deberán limpiarse después con [auto]VACUUM.

¡No lo hagas de esta manera! Usar actualizando todos los campos en una sola solicitud — casi siempre la lógica del método se puede cambiar así:

UPDATE tbl SET X = <newX>, Y = <newY>, Z = <newZ> WHERE pk = $1;

#2: ¡El uso ES DISTINTO DE Luke!

Entonces, todavía querías actualizar muchos, muchos registros en una tabla (durante el uso de un script o convertidor, por ejemplo). Y algo como esto aparece en el guión:

UPDATE tbl SET X = <newX> WHERE pk BETWEEN $1 AND $2;

Una solicitud en aproximadamente este formulario ocurre con bastante frecuencia y casi siempre no para completar un campo nuevo vacío, sino para corregir algunos errores en los datos. Al mismo tiempo, ella misma la exactitud de los datos existentes no se tiene en cuenta en absoluto - ¡pero en vano! Es decir, el registro se reescribe, incluso si contenía exactamente lo que se quería, pero ¿por qué? Arreglemoslo:

UPDATE tbl SET X = <newX> WHERE pk BETWEEN $1 AND $2 AND X IS DISTINCT FROM <newX>;

Mucha gente no es consciente de la existencia de un operador tan maravilloso, por eso aquí hay una hoja de trucos sobre IS DISTINCT FROM y otros operadores lógicos para ayudar:
Antipatrones de PostgreSQL: luchando contra las hordas de “muertos”
... y un poco sobre operaciones en complejos. ROW()-expresiones:
Antipatrones de PostgreSQL: luchando contra las hordas de “muertos”

#3: Reconozco a mi amada por... bloquear

se están lanzando dos procesos paralelos idénticos, cada uno de los cuales intenta marcar la entrada que está “en progreso”:

UPDATE tbl SET processing = TRUE WHERE pk = $1;

Incluso si estos procesos realmente hacen cosas independientes entre sí, pero dentro del mismo ID, el segundo cliente quedará "bloqueado" en esta solicitud hasta que se complete la primera transacción.

Decisión No. 1: la tarea se reduce a la anterior

Agreguémoslo de nuevo IS DISTINCT FROM:

UPDATE tbl SET processing = TRUE WHERE pk = $1 AND processing IS DISTINCT FROM TRUE;

De esta forma, la segunda solicitud simplemente no cambiará nada en la base de datos, todo ya está como debería ser; por lo tanto, no se producirá el bloqueo. A continuación, procesamos el hecho de “no encontrar” el registro en el algoritmo aplicado.

Decisión No. 2: cerraduras de aviso

Un gran tema para un artículo aparte, en el que puedes leer sobre métodos de aplicación y "rastrillo" del bloqueo recomendatorio.

Decisión No. 3: llamadas estúpidas

Pero esto es exactamente lo que debería pasarte. trabajo simultáneo con el mismo registro? ¿O se equivocó con los algoritmos para llamar a la lógica empresarial en el lado del cliente, por ejemplo? ¿Y si lo piensas bien?...

Fuente: habr.com

Añadir un comentario