49.4. Considérations pour le verrouillage d'index
Les méthodes d'accès aux index doivent gérer des mises à jour
concurrentes de l'index par plusieurs processus. Le système principal
PostgreSQL™ obtient AccessShareLock sur l'index lors d'un parcours d'index
et RowExclusiveLock quand il le met à jour
(ceci incluant le
VACUUM
simple). Comme ces types de
verrous ne sont pas en conflit, la méthode d'accès est responsable de
la gestion d'un verrouillage plus précis si nécessaire. Un verrou
exclusif sur l'index entier sera seulement pris lors de la création,
destruction de l'index, dans une opération REINDEX ou lors d'un
VACUUM FULL
.
Construire un type d'index qui supporte les mises à jour concurrentes
requiert une analyse complète et subtile du comportement requis. Pour
les types d'index B-tree et hash, vous pouvez lire les décisions du
concept dans src/backend/access/nbtree/README et src/backend/access/hash/README.
En plus des besoins de cohérence interne de l'index, les mises à jour
concurrentes créent des problèmes de cohérence entre la table parent
(l'en-tête) et l'index. Comme PostgreSQL™ sépare les accès et les mises
à jour de l'en-tête de ceux de l'index, il existe des possibilités
pour que l'index ne soit pas à jour par rapport à l'en-tête. Nous
gérons ce problème avec les règles suivantes :
-
Une nouvelle entrée dans l'en-tête est effectuée avant sa
contrepartie dans l'index (du coup, un parcours d'index
concurrent pourrait ne pas voir l'entrée dans l'en-tête ; ceci
est bon car le lecteur de l'index ne sera pas intéressé par une
ligne non validée... voir Section 49.5,
« Vérification de l'unicité de l'index »).
-
Quand une entrée de l'en-tête doit être supprimée (par
VACUUM
), toutes
les entrées de l'index doivent d'abord être supprimées.
-
Un parcours d'index doit maintenir un lien sur la page d'index
contenant le dernier élément renvoyé par amgettuple, et ambulkdelete ne peut pas supprimer les entrées
de pages qui sont liées à d'autres processus. Le besoin de
cette règle est expliqué plus bas.
Sans la troisième règle, il est possible qu'un lecteur d'index voit
une entrée dans l'index juste avant qu'elle ne soit supprimée par un
VACUUM
, puis d'arriver
à l'entrée correspondante de l'en-tête après qu'elle soit supprimée
par le
VACUUM
. Ceci ne
crée pas de problèmes sérieux si ce numéro d'élément est toujours
inutilisé quand le lecteur l'atteint car un emplacement d'élément
vide sera ignoré par heap_fetch(). Mais que
se passe-t'il si un troisième moteur a déjà ré-utilisé l'emplacement
de l'élément pour quelque chose d'autre ? Lors de l'utilisation d'une
image compatible MVCC, il n'y a pas de problème car le nouvel
occupant de l'emplacement est certain d'être trop nouveau pour
accepter ou renvoyer une ligne qui, en fait, ne correspond pas aux
clés de parcours. Nous pouvons nous défendre contre ce scénario en
réclamant que les clés de parcours soient de nouveau vérifiées avec
la ligne d'en-tête dans tous les cas mais c'est bien trop coûteux. À
la place, nous utilisons un lien sur une page d'index comme un proxy,
pour indiquer que le lecteur pourrait toujours être
« en parcours » à partir de
l'entrée de l'index jusqu'à l'entrée correspondante. Faire bloquer
ambulkdelete sur un tel lien nous assure
qu'un
VACUUM
ne peut
pas supprimer l'entrée de l'en-tête avant que le lecteur n'en ait
terminé avec lui. Cette solution coûte peu en temps d'exécution mais
ajoute un délai dans le blocage dans de rares cas où il existe
réellement un conflit.
Cette solution requiert que les parcours d'index soient
« synchrones » : nous devons
récupérer chaque ligne d'en-tête immédiatement après avoir parcouru
l'entrée d'index correspondante. Ceci est coûteux pour plusieurs
raisons. Un parcours « asynchrone » dans lequel nous récupérons les TID
de l'index et dans lequel nous visitons seulement les en-têtes de
lignes un peu plus tard, requiert moins de temps de verrouillage de
l'index et pourrait autoriser un modèle d'accès à l'en-tête plus
efficace. En plus de l'analyse ci-dessus, nous devons utiliser
l'approche synchronisée pour les images non compatibles avec MVCC
mais un parcours asynchrone est possible pour une requête utilisant
une image MVCC.
Dans un parcours d'index amgetmulti, la
méthode d'accès n'a pas besoin de garantir la conservation d'un lien
à l'index sur aucune des lignes renvoyées, ce qui est impraticable
pour toutes sauf la dernière). Du coup, il est plus sage d'utiliser
plusieurs parcours avec des images compatibles MVCC.