PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Correspondance de partition avancée pour une jointure par partition

Auparavant, j'avais écrit un blog sur la jointure par partition dans PostgreSQL. Dans ce blog, j'avais parlé d'une technique avancée de correspondance de partition qui permettra d'utiliser la jointure par partition dans plus de cas. Dans ce blog, nous discuterons de cette technique en détail.

Pour récapituler, la technique de correspondance de partition de base permet d'effectuer une jointure entre deux tables partitionnées à l'aide de la technique de jointure par partition si les deux tables partitionnées ont des limites de partition exactement identiques, par ex. tables partitionnées prt1 et prt2 décrites ci-dessous

psql> \d+ prt1
... [output clipped]
Partition key: RANGE (a)
Partitions: prt1_p1 FOR VALUES FROM (0) TO (5000),
prt1_p2 FOR VALUES FROM (5000) TO (15000),
prt1_p3 FOR VALUES FROM (15000) TO (30000)

et

psql>\d+ prt2
... [ output clipped ]
Partition key: RANGE (b)
Partitions: prt2_p1 FOR VALUES FROM (0) TO (5000),
prt2_p2 FOR VALUES FROM (5000) TO (15000),
prt2_p3 FOR VALUES FROM (15000) TO (30000)

Une jointure entre prt1 et prt2 sur leur clé de partition (a) est divisée en jointures entre leurs partitions correspondantes, c'est-à-dire que prt1_p1 joint prt2_p1, prt1_p2 joint prt2_p2 et prt1_p3 joint prt2_p3. Les résultats de ces trois jointures forment ensemble le résultat de la jointure entre prt1 et prt2. Cela présente de nombreux avantages, comme indiqué dans mon blog précédent. Cependant, la correspondance de partition de base ne peut pas joindre deux tables partitionnées avec des limites de partition différentes. Dans l'exemple ci-dessus, si prt1 a une partition supplémentaire prt1_p4 FOR VALUES FROM (30000) TO (50000), la correspondance de partition de base n'aiderait pas à convertir une jointure entre prt1 et prt2 en une jointure par partition, car ils n'ont pas exactement la même partition. limites.

De nombreuses applications utilisent des partitions pour séparer les données activement utilisées et les données obsolètes, une technique dont j'ai parlé dans mon autre blog. Les données obsolètes sont finalement supprimées en supprimant des partitions. De nouvelles partitions sont créées pour accueillir les nouvelles données. Une jointure entre deux tables partitionnées de ce type utilisera principalement une jointure par partition, car la plupart du temps, elles auront des partitions correspondantes. Mais lorsqu'une partition active est ajoutée à l'une de ces tables ou qu'une partition obsolète est supprimée, leurs limites de partition ne correspondent pas tant que l'autre table n'a pas subi une opération similaire. Pendant cet intervalle, une jointure entre ces deux tables n'utilisera pas la jointure par partition et peut prendre un temps inhabituellement plus long pour s'exécuter. Nous ne voulons pas qu'une jointure frappant la base de données pendant cette courte durée fonctionne mal car elle ne peut pas utiliser de jointure par partition. L'algorithme de correspondance de partition avancé aide dans ce cas et dans les cas plus compliqués où les limites de partition ne correspondent pas exactement.

Algorithme de correspondance de partition avancé

La technique de correspondance de partition avancée trouve des partitions correspondantes à partir de deux tables partitionnées même lorsque leurs limites de partition ne correspondent pas exactement. Il trouve les partitions correspondantes en comparant les limites des deux tables dans leur ordre de tri similaire à l'algorithme de jointure par fusion. Deux partitions quelconques, une de chacune des tables partitionnées, dont les limites correspondent exactement ou se chevauchent sont considérées comme des partenaires de jonction car elles peuvent contenir des lignes de jonction. En continuant avec l'exemple ci-dessus, disons qu'une nouvelle partition active prt2_p4 est ajoutée à prt4. Les tables partitionnées ressemblent maintenant à :

psql>\d+ prt1
... [output clipped]
Partition key: RANGE (a)
Partitions: prt1_p1 FOR VALUES FROM (0) TO (5000),
prt1_p2 FOR VALUES FROM (5000) TO (15000),
prt1_p3 FOR VALUES FROM (15000) TO (30000)

et

psql>\d+ prt2
... [ output clipped ]
Partition key: RANGE (b)
Partitions: prt2_p1 FOR VALUES FROM (0) TO (5000),
prt2_p2 FOR VALUES FROM (5000) TO (15000),
prt2_p3 FOR VALUES FROM (15000) TO (30000),
prt2_p4 FOR VALUES FROM (30000) TO (50000)

Il est facile de voir que les limites de partition de prt1_p1 et prt2_p1, prt1_p2 et prt2_p2, et prt1_p3 et prt2_p3 correspondent respectivement. Mais contrairement à la correspondance de partition de base, la correspondance de partition avancée saura que prt2_p4 n'a pas de partition correspondante dans prt1. Si la jointure entre prt1 et prt2 est une jointure INNER ou si prt2 est une relation INNER dans la jointure, le résultat de la jointure n'aura aucune ligne de prt2_p4. Activé avec des informations détaillées sur les partitions correspondantes et les partitions qui ne correspondent pas, par opposition simplement au fait que les limites de la partition correspondent ou non, l'optimiseur de requête peut décider d'utiliser ou non la jointure par partition. Dans ce cas, il choisira d'exécuter la jointure en tant que jointure entre les partitions correspondantes en laissant prt2_p4 de côté. Mais cela ne ressemble pas beaucoup à une correspondance de partition "avancée". Voyons un cas un peu plus compliqué en utilisant cette fois des tables partitionnées par liste :

psql>\d+ plt1
Partition key: LIST (c)
Partitions: plt1_p1 FOR VALUES IN ('0001', '0003'),
plt1_p2 FOR VALUES IN ('0004', '0006'),
plt1_p3 FOR VALUES IN ('0008', '0009')

et

psql>\d+ plt2
Partition key: LIST (c)
Partitions: plt2_p1 FOR VALUES IN ('0002', '0003'),
plt2_p2 FOR VALUES IN ('0004', '0006'),
plt2_p3 FOR VALUES IN ('0007', '0009')

Observez qu'il y a exactement trois partitions dans les deux relations, mais que les listes de valeurs de partition diffèrent. La liste correspondant à la partition plt1_p2 correspond exactement à celle de plt2_p2. En dehors de cela, aucune partition, une de chaque côté, n'a de listes identiques. L'algorithme de correspondance de partition avancé en déduit que plt1_p1 et plt2_p1 ont des listes qui se chevauchent et que leurs listes ne se chevauchent avec aucune autre partition de l'autre relation. De même pour plt1_p3 et plt2_p3. L'optimiseur de requête voit alors que la jointure entre plt1 et plt2 peut être exécutée en tant que jointure par partition en joignant les partitions correspondantes, c'est-à-dire plt1_p1 et plt2_p1, plt1_p2 et plt2_p2, et plt1_p3 et plt2_p3 respectivement. L'algorithme peut trouver des partitions correspondantes dans des ensembles de listes de partitions encore plus complexes ainsi que des tables partitionnées par plage. Mais nous ne les couvrirons pas par souci de brièveté. Les lecteurs intéressés et plus audacieux peuvent jeter un œil au commit. Il contient également de nombreux cas de test, qui montrent divers scénarios dans lesquels un algorithme avancé de correspondance de partition est utilisé.

Limites

Joints externes avec des partitions correspondantes manquantes sur le côté intérieur

Les jointures externes posent un problème particulier dans le monde PostgreSQL. Considérons prt2 LEFT JOIN prt1, dans l'exemple ci-dessus, où prt2 est une relation OUTER. prt2_p4 n'a pas de partenaire de jointure dans prt1 et pourtant les lignes de cette partition doivent faire partie du résultat de la jointure puisqu'elles appartiennent à la relation externe. Dans PostgreSQL, lorsque le côté INNER d'une jointure est vide, il est représenté par une relation "factice" qui n'émet aucune ligne mais connaît toujours le schéma de cette relation. Habituellement, une relation "factice" émerge d'une relation non factice qui n'émettra aucune ligne en raison d'une optimisation de requête telle que l'exclusion de contraintes. L'optimiseur de requête de PostgreSQL marque une telle relation non fictive comme fictive et l'exécuteur procède normalement lors de l'exécution d'une telle jointure. Mais lorsqu'il n'y a pas de partition interne correspondante pour une partition externe, il n'y a pas "d'entité existante" qui peut être marquée comme "factice". Par exemple, dans ce cas, il n'y a pas de prt1_p4 qui peut représenter une partition interne fictive joignant le prt2_p4 externe. À l'heure actuelle, PostgreSQL n'a aucun moyen de "créer" de telles relations "fictives" lors de la planification. Par conséquent, l'optimiseur de requête n'utilise pas de jointure par partition dans ce cas.

Idéalement, une telle jointure avec un intérieur vide ne nécessite qu'un schéma de la relation intérieure et non une relation entière. Ce schéma peut être dérivé de la table partitionnée elle-même. Tout ce dont il a besoin est une capacité à produire la ligne de jointure en utilisant les colonnes d'une ligne du côté extérieur jointes par des valeurs NULL pour les colonnes du côté intérieur. Une fois que nous aurons cette capacité dans PostgreSQL, l'optimiseur de requête pourra utiliser la jointure par partition même dans ces cas.

Permettez-moi de souligner que les jointures externes où il n'y a pas de partitions manquantes sur la jointure interne utilisent une jointure par partition.

Plusieurs partitions correspondantes

Lorsque les tables sont partitionnées de telle sorte que plusieurs partitions d'un côté correspondent à une ou plusieurs partitions de l'autre côté, la jointure par partition ne peut pas être utilisée car il n'y a aucun moyen d'induire une relation "Append" pendant le temps de planification qui représente deux ou plus partitions ensemble. Espérons que nous supprimerons également cette limitation un jour et autoriserons également l'utilisation de la jointure par partition dans ces cas.

Tables partitionnées par hachage

Les limites de partition de deux tables partitionnées par hachage utilisant le même modulo correspondent toujours. Lorsque le modulo est différent, une ligne d'une partition donnée d'une table peut avoir ses partenaires de jonction dans de nombreuses partitions de l'autre, ainsi une partition donnée d'un côté correspond à plusieurs partitions de l'autre table, rendant ainsi la jointure par partition inefficace.

Lorsque l'algorithme de correspondance de partition avancé ne parvient pas à trouver les partitions correspondantes ou que la jointure par partition ne peut pas être utilisée en raison des limitations ci-dessus, PostgreSQL se replie pour joindre les tables partitionnées en tant que tables normales.

Temps de correspondance de partition avancé

Simon a soulevé un point intéressant en commentant la fonctionnalité. Les partitions d'une table partitionnée ne changent pas souvent, le résultat de la correspondance de partition avancée doit donc rester le même pendant une durée plus longue. Il n'est pas nécessaire de le calculer à chaque fois qu'une requête impliquant ces tables est exécutée. Au lieu de cela, nous pourrions enregistrer l'ensemble des partitions correspondantes dans un catalogue et le rafraîchir à chaque fois que les partitions changent. C'est un peu de travail, mais cela vaut le temps passé à faire correspondre la partition pour chaque requête.

Même avec toutes ces limitations, nous avons aujourd'hui une solution très utile qui sert la plupart des cas pratiques. Inutile de dire que cette fonctionnalité fonctionne de manière transparente avec la poussée de jointure FDW, améliorant ainsi les capacités de partitionnement que PostgreSQL possède déjà !