Documentation PostgreSQL 9.4.26 > Internes > Définition de l'interface des méthodes d'accès aux index > Considérations sur le verrouillage d'index | |
Parcours d'index | Vérification de l'unicité de l'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 lors de sa mise à jour (ce qui inclut le VACUUM simple). Comme ces types de verrous ne sont pas conflictuels, la méthode d'accès est responsable de la finesse du verrouillage dont elle a besoin. Un verrou exclusif sur l'intégralité de l'index entier n'est pris qu'à la création de l'index, sa destruction ou dans une opération REINDEX.
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, on peut lire les implication sur les décisions de conception 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, ou heap) 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 fenêtres temporelles pendant lesquelles l'index et l'en-tête peuvent être incohérents. Ce problème est géré avec les règles suivantes :
une nouvelle entrée dans l'en-tête est effectuée avant son entrée dans l'index. (Un parcours d'index concurrent peut alors ne pas voir l'entrée dans l'en-tête. Ce n'est pas gênant dans la mesure où un lecteur de l'index ne s'intéresse pas à une ligne non validée. Voir Section 55.5, « Vérification de l'unicité de l'index »);
Lorsqu'entrée de l'en-tête va ê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 des entrées de pages liées à d'autres processus. La raison de cette règle est expliquée 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, et arrive à l'entrée correspondante de l'en-tête après sa suppression par le VACUUM. Cela ne pose aucun problème sérieux si ce numéro d'élément est toujours inutilisé quand le lecteur l'atteint, car tout emplacement d'élément vide est 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'un instantané compatible MVCC, il n'y a pas de problème car le nouvel occupant de l'emplacement est certain d'être trop récent pour réussir le test de l'insstantané. En revanche, avec un instantané non-compatible MVCC (tel que SnapshotAny), une ligne qui ne correspond pas aux clés de parcours peut être acceptée ou retournée. Ce scénario peut être évité en imposant que les clés de parcours soient re-confrontées à la ligne d'en-tête dans tous les cas, mais cela est trop coûteux. À la place, un lien sur une page d'index est utilisé comme proxy pour indiquer que le lecteur peut être « en parcours » entre l'entrée de l'index et l'entrée de l'en-tête correspondante. Bloquer ambulkdelete sur un tel lien assure que VACUUM ne peut pas supprimer l'entrée de l'en-tête avant que le lecteur n'en ait terminé avec elle. Cette solution est peu coûteuse en temps d'exécution, et n'ajoute de surcharge du fait du blocage que dans de rares cas réellement un conflictuels.
Cette solution requiert que les parcours d'index soient « synchrones » : chaque ligne d'en-tête doit être récupérée immédiatement après le parcours de l'entrée d'index correspondante. C'est coûteux pour plusieurs raisons. Un parcours « asynchrone » dans lequel de nombreux TID sont récupérés de l'index, et pour lequel les en-têtes de lignes ne sont visités que plus tard, requiert moins de surcharge de verrouillage d'index et autorise un modèle d'accès à l'en-tête plus efficace. D'après l'analyse ci-dessus, l'approche synchronisée doit être utilisée pour les instantanés non compatibles avec MVCC, mais un parcours asynchrone est possible pour une requête utilisant une instantané MVCC.
Dans un parcours d'index amgetbitmap, la méthode d'accès ne conserve pas de lien à l'index sur quelque ligne renvoyée. C'est pourquoi, il est préférable d'utiliser de tels parcours avec les instantanés compatibles MVCC.
Quand le drapeau ampredlocks n'est pas configuré, tout parcours utilisant cette méthode d'accès de l'index dans une transaction sérialisable acquèrera un verrou prédicat non bloquant sur l'index complet. Ceci génèrera un conflit en lecture/écriture avec l'insertion d'une ligne dans cet index par une transaction sérialisable concurrente. Si certains motifs de conflit en lecture/écriture sont détectés parmi un ensemble de transactions sérialisables concurrentes, une de ces transactions pourrait être annulée pour protéger l'intégrité des données. Quand le drapeau est configuré, cela indique que la méthode d'accès de l'index implémente un verrou prédicat plus fin, qui tend à réduire la fréquence d'annulations de telles requêtes.