J'ai des problèmes pour ajouter une nouvelle colonne sur une table.
J'ai essayé de l'exécuter plusieurs fois, mais après plus de 10 minutes de fonctionnement, j'ai décidé d'annuler la requête en raison du temps de verrouillage.
ALTER TABLE mytable ADD mycolumn VARCHAR(50);
Informations utiles:
- Version PostgreSQL: 9.1
- Nombre de lignes: ~ 250 K
- Nombre de colonnes: 38
- Nombre de colonnes nullables: 32
- Nombre de contraintes: 5 (1 PK, 3 FK, 1 UNIQUE)
- Nombre d'index: 1
- Type de système d'exploitation: Debian Squeeze 64
J'ai trouvé des informations intéressantes sur la façon dont PostgreSQL gère les colonnes nullables (via HeapTupleHeader).
Ma première supposition est que parce que cette table a déjà 32 colonnes nullables avec 8 bits MAXALIGN
, HeapTupleHeader a une longueur de 4 octets (non vérifié, et je ne sais pas comment le faire).
Ainsi, l'ajout d'une nouvelle colonne nullable peut nécessiter une mise à jour de HeapTupleHeader sur chaque ligne pour ajouter un nouveau 8 bits MAXALIGN
, ce qui pourrait entraîner des problèmes de performances.
J'ai donc essayé de modifier l'une des colonnes nullable (qui n'est pas vraiment nullable en réalité) afin de réduire à 31 le nombre de colonnes nullable, pour vérifier si ma supposition pouvait être vraie.
ALTER TABLE mytable ALTER myothercolumn SET NOT NULL;
Malheureusement, cette modification prend également très longtemps, plus de 5 minutes, je l'ai donc également abandonnée.
Avez-vous une idée de ce qui pourrait entraîner ce coût de performance?
la source
SET NOT NULL
ne modifie pas le type, il ajoute simplement une contrainte - mais la contrainte doit être vérifiée par rapport à la table, et cela nécessite une analyse complète de la table. 9.4 améliore certains de ces cas en prenant des verrous plus faibles, mais il est toujours assez lourd.Réponses:
Il y a quelques malentendus ici:
Le bitmap nul ne fait pas partie de l'en-tête de tuple de tas. Par documentation:
Vos 32 colonnes annulables ne sont pas suspectes pour deux raisons:
Le bitmap nul est ajouté par ligne , et uniquement s'il existe au moins une
NULL
valeur réelle dans la ligne. Les colonnes nulles n'ont pas d'impact direct, seules lesNULL
valeurs réelles le font. Si le bitmap nul est alloué, il est toujours alloué complètement (tout ou rien). La taille réelle du bitmap nul est de 1 bit par colonne, arrondie à l'octet suivant . Par code souce actuel:Le bitmap nul est alloué après l'en-tête de tuple de tas et suivi d'un OID facultatif, puis de données de ligne. Le début d'un OID ou d'une ligne de données est indiqué par
t_hoff
dans l'en-tête. Par code source de commentaire :Il y a un octet libre après l'en-tête de tuple de tas, qui occupe 23 octets. Ainsi, le bitmap nul pour les lignes jusqu'à 8 colonnes est effectivement sans frais supplémentaires. Avec la 9e colonne du tableau,
t_hoff
un autreMAXALIGN
octet (généralement 8) est avancé pour fournir 64 autres colonnes. La prochaine frontière serait donc à 72 colonnes.Pour afficher les informations de contrôle d'un cluster de base de données PostgreSQL (incl.
MAXALIGN
), Exemple pour une installation typique de Postgres 9.3 sur une machine Debian:J'ai mis à jour les instructions dans la réponse connexe que vous avez citée .
Tout cela mis à part, même si votre
ALTER TABLE
instruction déclenche une réécriture de table entière (ce qu'elle fait probablement, en changeant un type de données), 250 Ko ne sont vraiment pas beaucoup et seraient une question de secondes sur n'importe quelle machine à mi-chemin décente (à moins que les lignes soient anormalement grandes) . 10 minutes ou plus indiquent un problème complètement différent. Votre déclaration attend très probablement un verrou sur la table.Le nombre croissant d'entrées dans
pg_stat_activity
signifie des transactions plus ouvertes - indique un accès simultané sur la table (le plus probable) qui doit attendre la fin de l'opération.Quelques clichés dans le noir
Vérifiez l'éventuel ballonnement de la table, essayez une solution douce
VACUUM mytable
ou plus agressiveVACUUM FULL mytable
- qui pourrait rencontrer les mêmes problèmes de concurrence, car ce formulaire acquiert également un verrou exclusif. Vous pouvez essayer pg_repack à la place ...Je commencerais par inspecter les problèmes possibles avec les index, les déclencheurs, la clé étrangère ou d'autres contraintes, en particulier ceux impliquant la colonne. En particulier, un index corrompu pourrait être impliqué? Essayez-les
REINDEX TABLE mytable;
ouDROP
tous et ré-ajoutez-les aprèsALTER TABLE
dans la même transaction .Essayez d'exécuter la commande dans la nuit ou chaque fois qu'il n'y a pas beaucoup de charge.
Une méthode de force brute serait d'arrêter l'accès au serveur, puis de réessayer:
Sans pouvoir le cerner, la mise à niveau vers la version actuelle ou la prochaine 9.4 en particulier pourrait aider. Plusieurs améliorations ont été apportées aux grandes tables et aux détails de verrouillage. Mais s'il y a quelque chose de cassé dans votre base de données, vous devriez probablement le découvrir en premier.
la source