5 puntos por GN⁺ 2024-04-29 | Aún no hay comentarios. | Compartir por WhatsApp

Here is a summary of the common database schema change mistakes, translated and structured in Korean:

Errores relacionados con la concurrencia

  • Falla al adquirir locks
  • Actualizar demasiadas filas de una sola vez
  • Mantener la transacción abierta por mucho tiempo después de adquirir un lock exclusivo

Errores relacionados con la exactitud de las etapas - problemas lógicos

  • Desviación inesperada del esquema
  • Inconsistencia entre el esquema y el código de la app
  • Datos inesperados

Otros errores

  • Alcanzar statement_timeout
  • Usar una clave primaria entera de 4 bytes en una tabla que puede crecer
  • Ignorar el comportamiento de VACUUM y el riesgo de bloat

Caso 1. Inconsistencia del esquema

  • Funcionó en desarrollo/pruebas, pero falló en QA/Staging/Producción
  • Hay que identificar la causa y resolverlo mejorando el flujo de trabajo

Caso 2. Uso indebido de IF [NOT] EXISTS

  • No intentes ignorar errores de inconsistencia del esquema con IF NOT EXISTS
  • Hay que identificar y resolver la causa raíz del problema

Caso 3. Alcanzar statement_timeout

  • Prueba todos los cambios con grandes volúmenes de datos para detectarlo de antemano

Caso 4. Cambios masivos sin límite

  • Si cambias demasiadas filas en una sola transacción, afectas a otras transacciones
  • Si el checkpointer no está bien ajustado, se puede generar una cantidad excesiva de datos WAL
  • Puede haber una degradación general del rendimiento por saturación de escritura en disco
  • Pueden surgir problemas de VACUUM/bloat
  • Procésalo en lotes y gestiona VACUUM

Caso 5. Esperar dentro de una transacción después de adquirir un lock exclusivo

  • Si haces otras tareas entre BEGIN/ALTER TABLE/COMMIT, el lock se mantiene por más tiempo
  • Después de adquirir un lock exclusivo, la transacción debe terminarse lo antes posible

Caso 6. Transacción que incluye DDL + DML masivo

  • Los locks adquiridos en la etapa DDL permanecen durante mucho tiempo hasta la etapa DML
  • Separa DDL y DML en transacciones o etapas de migración distintas

Caso 7. Bloqueo de otras sesiones por esperar adquirir un lock exclusivo

  • Cuando autovacuum está en modo de prevención de wraparound, no cede ante DDL
  • Incluso SELECT queda bloqueado mientras se espera adquirir el lock
  • Configura un lock_timeout bajo y crea una lógica de reintento

Caso 8. Precauciones al crear una FK

  • Al crear una FK en una tabla grande, puede tomar tiempo por el escaneo de la tabla referenciada
  • Define la FK con la opción not valid y valida en una transacción separada

Caso 9. Precauciones al eliminar una FK

  • Como se necesitan locks en ambas tablas, hace falta lógica de reintento con lock_timeout

Caso 10. Precauciones al agregar una restricción CHECK

  • Como ocurre un escaneo completo de la tabla, usa un enfoque de dos etapas similar al de FK

Caso 11. Precauciones al agregar NOT NULL

  • Antes de Postgres 11, al agregar NOT NULL a una columna nueva se producía un escaneo completo de la tabla
  • Desde Postgres 11, se puede resolver agregando una columna NOT NULL DEFAULT
  • Desde Postgres 12, se puede establecer NOT NULL agregando una restricción CHECK

Caso 12. Precauciones al cambiar el tipo de datos de una columna

  • Puede producirse una reescritura completa de la tabla
  • Hace falta un enfoque de agregar una nueva columna y copiar los datos con triggers

Caso 13. Precauciones al usar CREATE INDEX

  • En OLTP se debe usar CREATE INDEX CONCURRENTLY
  • Si falla la creación de un índice único, hay que limpiar el índice inválido

Caso 14. Precauciones al usar DROP INDEX

  • Como hay problemas de adquisición de locks, usa DROP INDEX CONCURRENLTY

Caso 15. Precauciones al cambiar el nombre de objetos

  • Hay que ajustar el orden de despliegue para evitar inconsistencias entre el código de la app y el esquema de la DB

Caso 16. Agregar una columna con valor DEFAULT

  • Antes de PG 11 se producía una reescritura completa de la tabla
  • Desde PG 11, agregar una columna con valor DEFAULT es más rápido

Caso 17. Limpieza de residuos cuando falla CREATE INDEX CONCURRENTLY

  • Si falla, queda un índice inválido, así que hay que limpiarlo antes de reintentar

Caso 18. Usar una clave primaria entera de 4 bytes en una tabla grande

  • Hay que usar int8. La mayoría de los frameworks ya usan int8.

Recomendaciones

  • Probar con tamaños de datos realistas
  • Revisar cuánto tiempo se mantiene un lock exclusivo
  • Mejorar la automatización del despliegue
  • Aprender de otras personas y compartir conocimiento

Opinión de GN⁺

Este artículo organiza muy bien varios errores y precauciones que pueden aparecer al hacer cambios reales en el esquema de una base de datos. En particular, se mencionan mucho los problemas relacionados con locks exclusivos, y este es un tema que puede causar problemas mucho más serios cuanto más grande sea la base de datos.

También explica de forma concreta las precauciones al tratar con FK, NOT NULL, índices y otros elementos que los desarrolladores suelen pasar por alto. Entender y aprovechar las mejoras según la versión de Postgres también parece ser de ayuda.

Sobre todo, coincido en que probar a fondo con tamaños de datos realistas y mejorar la automatización del despliegue es clave para minimizar el riesgo de los cambios de esquema. También puede ser útil aprovechar herramientas como Database Lab Engine para pruebas y automatización de despliegue.

Ojalá haya más publicaciones de blogs técnicos que compartan este tipo de consejos útiles. Cuanto más se difunda esta información, más ayudará sin duda a mejorar las capacidades de los desarrolladores que trabajan con bases de datos.

Aún no hay comentarios.

Aún no hay comentarios.