Tengo problemas para agregar una nueva columna en una tabla.
Intenté ejecutarlo un par de veces, pero después de más de 10 minutos de ejecución, decidí cancelar la consulta debido al tiempo de bloqueo.
ALTER TABLE mytable ADD mycolumn VARCHAR(50);
Información útil:
- Versión PostgreSQL: 9.1
- Número de filas: ~ 250K
- Número de columnas: 38
- Número de columnas anulables: 32
- Número de restricciones: 5 (1 PK, 3 FK, 1 ÚNICO)
- Número de índices: 1
- Tipo de sistema operativo: Debian Squeeze 64
Encontré información interesante sobre la forma en que PostgreSQL administra las columnas anulables (a través de HeapTupleHeader).
Mi primera suposición es que debido a que esta tabla ya tiene 32 columnas anulables con 8 bits MAXALIGN
, HeapTupleHeader tiene una longitud de 4 bytes (no verificado, y no sé cómo hacerlo).
Por lo tanto, agregar una nueva columna anulable podría necesitar una actualización de HeapTupleHeader en cada fila para agregar un nuevo 8 bits MAXALIGN
, lo que podría causar problemas de rendimiento.
Así que traté de alterar una de las columnas anulables (que en realidad no es anulable en realidad) para disminuir a 31 el número de columnas anulables, para verificar si mi suposición podría ser cierta.
ALTER TABLE mytable ALTER myothercolumn SET NOT NULL;
Desafortunadamente, este cambio también lleva mucho tiempo, más de 5 minutos, así que también lo aborté.
¿Tiene una idea de lo que podría causar este costo de rendimiento?
fuente
SET NOT NULL
no altera el tipo, solo agrega una restricción, pero la restricción debe verificarse contra la tabla, y eso requiere un escaneo completo de la tabla. 9.4 mejora algunos de estos casos al tomar bloqueos más débiles, pero sigue siendo bastante pesado.Respuestas:
Aquí hay un par de malentendidos:
El mapa de bits nula es no parte de la cabecera montón tupla. Por documentación:
Sus 32 columnas anulables no son sospechosas por dos razones:
El mapa de bits nulo se agrega por fila , y solo si hay al menos un
NULL
valor real en la fila. Las columnas anulables no tienen impacto directo, solo losNULL
valores reales sí. Si se asigna el mapa de bits nulo, siempre se asigna completamente (todo o nada). El tamaño real del mapa de bits nulo es de 1 bit por columna, redondeado al siguiente byte . Por código de fuente actual:El mapa de bits nulo se asigna después del encabezado de la tupla de montón y seguido por un OID opcional y luego datos de fila. El inicio de un OID o datos de fila se indica mediante
t_hoff
el encabezado. Por código fuente de comentario :Hay un byte libre después del encabezado de la tupla de montón, que ocupa 23 bytes. Por lo tanto, el mapa de bits nulo para filas de hasta 8 columnas efectivamente no tiene costo adicional. Con la novena columna de la tabla,
t_hoff
se avanzan otrosMAXALIGN
(normalmente 8) bytes para proporcionar otras 64 columnas. Entonces el próximo borde estaría en 72 columnas.Para mostrar información de control de un clúster de base de datos PostgreSQL (incl.
MAXALIGN
), Ejemplo para una instalación típica de Postgres 9.3 en una máquina Debian:Actualicé las instrucciones en la respuesta relacionada que usted citó .
Aparte de eso, incluso si su
ALTER TABLE
declaración desencadena una reescritura de una tabla completa (lo que probablemente hace, cambiando un tipo de datos), 250K realmente no son tanto y serían cuestión de segundos en cualquier máquina medianamente decente (a menos que las filas sean inusualmente grandes) . 10 minutos o más indican un problema completamente diferente. Su estado de cuenta está esperando que se bloquee la mesa, muy probablemente.El creciente número de entradas en
pg_stat_activity
significa más transacciones abiertas: indica acceso concurrente en la tabla (muy probablemente) que tiene que esperar a que finalice la operación.Algunas tomas en la oscuridad
Verifique la posible hinchazón de la tabla, pruebe con una suave
VACUUM mytable
o más agresivaVACUUM FULL mytable
, que podría encontrar los mismos problemas de concurrencia, ya que este formulario también adquiere un bloqueo exclusivo. Podrías probar pg_repack en su lugar ...Comenzaría por inspeccionar posibles problemas con índices, disparadores, claves foráneas u otras restricciones, especialmente las relacionadas con la columna. ¿Especialmente un índice dañado podría estar involucrado? Pruebe
REINDEX TABLE mytable;
oDROP
todos ellos y vuelva a agregarlos despuésALTER TABLE
de la misma transacción .Intente ejecutar el comando en la noche o cuando no haya mucha carga.
Un método de fuerza bruta sería detener el acceso al servidor, luego intente nuevamente:
Sin poder precisarlo, la actualización a la versión actual o la próxima 9.4 en particular podría ayudar. Se han realizado varias mejoras para las mesas grandes y para los detalles de bloqueo. Pero si hay algo roto en su base de datos, probablemente debería resolverlo primero.
fuente