Qu'est-ce qui est récupéré du disque pendant une requête?


13

Question assez simple, probablement répondu quelque part, mais je n'arrive pas à former la bonne question de recherche pour Google ...

Le nombre de colonnes dans une table particulière affecte-t-il les performances d'une requête, lors d'une requête sur un sous-ensemble de cette table?

Par exemple, si la table Foo a 20 colonnes, mais que ma requête ne sélectionne que 5 de ces colonnes, le fait d'avoir 20 colonnes (contre, disons, 10) affecte-t-il les performances de la requête? Supposons par souci de simplicité que tout élément de la clause WHERE soit inclus dans ces 5 colonnes.

Je suis préoccupé par l'utilisation du cache tampon de Postgres en plus du cache disque du système d'exploitation. J'ai très mal compris la conception du stockage physique de Postgres. Les tableaux sont stockés sur plusieurs pages (par défaut à 8 Ko en taille par page), mais je ne comprends pas très bien comment les tuples sont organisés à partir de là. PG est-il assez intelligent pour extraire uniquement du disque les données qui comprennent ces 5 colonnes?


Vous parlez de récupérer 50 octets mais pas les 150 restants. Votre disque lit probablement par plus gros incréments que cela!
Andomar

D'où tirez-vous ces chiffres?
Jmoney38

Réponses:


14

Le stockage physique des lignes est décrit dans les documents de la mise en page de la base de données . Le contenu de la colonne pour la même ligne est tous stockés dans la même page de disque, à l'exception notable du contenu éd de TOAST (trop grand pour tenir dans une page). Le contenu est extrait séquentiellement dans chaque ligne, comme expliqué:

Pour lire les données, vous devez examiner chaque attribut tour à tour. Vérifiez d'abord si le champ est NULL selon le bitmap nul. Si c'est le cas, passez à la suivante. Assurez-vous ensuite d'avoir le bon alignement. Si le champ est un champ à largeur fixe, tous les octets sont simplement placés.

Dans le cas le plus simple (pas de colonnes TOAST), postgres récupérera la ligne entière même si quelques colonnes sont nécessaires. Donc, dans ce cas, la réponse est oui, avoir plus de colonnes peut avoir un impact négatif clair sur le cache de tampon de gaspillage, en particulier si le contenu des colonnes est important tout en restant sous le seuil TOAST.

Maintenant, le cas TOAST: lorsqu'un champ individuel dépasse ~ 2 Ko, le moteur stocke le contenu du champ dans une table physique distincte. Il entre également en jeu lorsque la ligne entière ne tient pas dans une page (8 Ko par défaut): certains des champs sont déplacés vers le stockage TOAST. Doc dit:

Si c'est un champ de longueur variable (attlen = -1), c'est un peu plus compliqué. Tous les types de données de longueur variable partagent la structure de structure d'en-tête commune varlena, qui inclut la longueur totale de la valeur stockée et certains bits d'indicateur. Selon les indicateurs, les données peuvent être soit en ligne, soit dans une table TOAST; il pourrait aussi être compressé

Les contenus TOAST ne sont pas récupérés lorsqu'ils ne sont pas explicitement nécessaires, leur effet sur le nombre total de pages à récupérer est donc faible (quelques octets par colonne). Cela explique les résultats dans la réponse de @ dezso.

En ce qui concerne les écritures, chaque ligne avec toutes ses colonnes est entièrement réécrite à chaque MISE À JOUR, quelles que soient les colonnes modifiées. Donc, avoir plus de colonnes est évidemment plus coûteux pour les écritures.


C'est une réponse à couper le souffle. Exactement ce que je recherche. Je vous remercie.
Jmoney38

1
Une bonne ressource que j'ai trouvée en ce qui concerne la structure des lignes (pageinspect et quelques exemples d'utilisation) ici .
Jmoney38

9

La réponse de Daniel se concentre sur le coût de lecture des lignes individuelles. Dans ce contexte: placer les NOT NULLcolonnes de taille fixe en premier dans votre tableau aide un peu. Mettre les colonnes pertinentes en premier (celles que vous recherchez) aide un peu. La minimisation du remplissage (en raison de l'alignement des données) en jouant à l'alignement tetris avec vos colonnes peut vous aider un peu. Mais l'effet le plus important n'a pas encore été mentionné, en particulier pour les grandes tables.

Des colonnes supplémentaires font évidemment qu'une ligne couvre plus d'espace disque, de sorte que moins de lignes tiennent sur une seule page de données (8 Ko par défaut). Les lignes individuelles sont réparties sur plusieurs pages. Le moteur de base de données doit généralement récupérer des pages entières, pas des lignes individuelles . Peu importe que les lignes individuelles soient un peu plus petites ou plus grandes - tant que le même nombre de pages doit être lu.

Si une requête récupère une portion (relativement) petite d'une grande table, où les lignes sont réparties de manière plus ou moins aléatoire sur l'ensemble de la table, prises en charge par un index, cela se traduira par environ le même nombre de lectures de page, sans trop de considération à la taille de ligne. Les colonnes non pertinentes ne vous ralentiront pas beaucoup dans un cas (rare).

En règle générale, vous récupérerez des correctifs ou des groupes de lignes qui ont été saisis dans l'ordre ou la proximité et partagerez des pages de données. Ces lignes sont réparties en raison de l'encombrement, davantage de pages de disque doivent être lues pour satisfaire votre requête. Devoir lire plus de pages est généralement la raison la plus importante pour laquelle une requête est plus lente. Et c'est le facteur le plus important pour lequel les colonnes non pertinentes ralentissent vos requêtes.

Avec les grandes bases de données, il n'y a généralement pas assez de RAM pour tout garder en mémoire cache. De plus grandes lignes occupent plus de cache, plus de conflits, moins de hits de cache, plus d'E / S de disque. Et les lectures sur disque sont généralement beaucoup plus chères. Moins avec les SSD, mais une différence substantielle demeure. Cela ajoute au point ci-dessus sur les lectures de page.

Il peut ou non être important que les colonnes non pertinentes soient éditées TOAST. Les colonnes pertinentes peuvent également être éditées TOAST, ramenant en grande partie le même effet.


1

Un petit test:

CREATE TABLE test2 (
    id serial PRIMARY KEY,
    num integer,
    short_text varchar(32),
    longer_text varchar(1000),
    long_long_text text
);

INSERT INTO test2 (num, short_text, longer_text, long_long_text)
SELECT i, lpad('', 32, 'abcdefeghji'), lpad('', 1000, 'abcdefeghji'), lpad('', (random() * 10000)::integer, 'abcdefeghji')
FROM generate_series(1, 10000) a(i);

ANALYZE test2;

SELECT * FROM test2;
[...]
Time: 1091.331 ms

SELECT num FROM test2;
[...]
Time: 21.310 ms

Limiter la requête aux 250 premières lignes ( WHERE num <= 250) donne respectivement 34,539 ms et 8,343 ms. La sélection de tout sauf long_long_textde cet ensemble limité donne 18,432 ms. Cela montre que, selon vos termes, PG est assez intelligent.


Eh bien, j'apprécie certainement la contribution. Cependant, je ne peux pas dire avec certitude que ce scénario de test prouve ce que j'ai proposé à l'origine. Il y a quelques problèmes. D'une part, lorsque vous exécutez pour la première fois "SELECT * FROM test2", cela aurait dû remplir votre cache de tampon partagé. Cette requête aurait pris beaucoup plus de temps à récupérer à partir du disque. Ainsi, la 2e requête aurait théoriquement été beaucoup plus rapide car elle aurait été extraite du cache SB. Mais je suis d'accord que cela «suggère» que PG ne récupère que les lignes dont il a besoin, en fonction de vos tests / comparaisons ultérieurs.
Jmoney38

Vous avez raison, ce test (étant simple) a ses défauts. Si j'ai assez de temps, je vais essayer de les couvrir également.
dezso
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.