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
VACUUMy 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
SELECTqueda bloqueado mientras se espera adquirir el lock - Configura un
lock_timeoutbajo 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 validy 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 NULLa 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 NULLagregando una restricciónCHECK
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
DEFAULTes 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 usanint8.
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.