J'ai une table PostgreSQL. select *
est très lent alors que select id
c'est gentil et rapide. Je pense que la taille de la rangée est très grande et que son transport prend du temps, ou peut-être un autre facteur.
J'ai besoin de tous les champs (ou presque tous), donc la sélection d'un sous-ensemble n'est pas une solution miracle. La sélection des champs que je veux est encore lente.
Voici mon schéma de table moins les noms:
integer | not null default nextval('core_page_id_seq'::regclass)
character varying(255) | not null
character varying(64) | not null
text | default '{}'::text
character varying(255) |
integer | not null default 0
text | default '{}'::text
text |
timestamp with time zone |
integer |
timestamp with time zone |
integer |
La taille du champ de texte peut être n'importe quelle taille. Mais toujours, pas plus de quelques kilo-octets dans le pire des cas.
Des questions
- Y a-t-il quelque chose à ce sujet qui crie "fou inefficace"?
- Existe-t-il un moyen de mesurer la taille de la page à l'aide de la ligne de commande Postgres pour m'aider à résoudre ce problème?
length(*)
plutôt que simplementlength(field)
? Je sais que ce ne sont pas des octets, mais j'ai seulement besoin d'une valeur approximative.Réponses:
Q2:
way to measure page size
PostgreSQL fournit un certain nombre de fonctions de taille d’objet de base de données . J'ai emballé les plus intéressants dans cette requête et ajouté quelques fonctions d'accès aux statistiques en bas. (Le module supplémentaire pgstattuple fournit encore plus de fonctions utiles.)
Cela va montrer que différentes méthodes pour mesurer la "taille d'une ligne" conduisent à des résultats très différents. Tout dépend de ce que vous voulez mesurer, exactement.
Cette requête nécessite Postgres 9.3 ou une version ultérieure . Pour les anciennes versions, voir ci-dessous.
En utilisant une
VALUES
expression dans uneLATERAL
sous - requête , pour éviter d'épeler les calculs pour chaque ligne.Remplacez
public.tbl
(deux fois) par le nom de votre table, éventuellement qualifié du schéma, pour obtenir une vue compacte des statistiques collectées sur la taille de vos lignes. Vous pouvez envelopper ceci dans une fonction plpgsql pour un usage répété, donner le nom de la table en tant que paramètre et utiliserEXECUTE
...Résultat:
Pour les anciennes versions (Postgres 9.2 ou plus ancien):
Même résultat.
Q1:
anything inefficient?
Vous pouvez optimiser l' ordre des colonnes pour économiser quelques octets par ligne, qui sont actuellement perdus pour le remplissage de l'alignement:
Cela économise entre 8 et 18 octets par ligne. Je l'appelle "tetris de colonne" . Détails:
Considérez également:
la source
, unnest(val) / ct
par, (LEAST(unnest(val), unnest(val) * ct)) / (ct - 1 + sign(ct))
et il ne jettera pas. La justification est que, quandct
est0
,val
sera remplacé par0
etct
sera remplacé par1
.Il est facile d'obtenir une approximation de la taille d'une ligne, y compris le contenu de TOAST , en interrogeant la longueur de la représentation TEXT de la ligne entière:
Ceci est une approximation proche du nombre d'octets qui seront récupérés côté client lors de l'exécution:
... en supposant que l'appelant de la requête demande les résultats au format texte, comme le font la plupart des programmes (le format binaire est possible, mais cela ne vaut pas la peine dans la plupart des cas).
La même technique pourrait être appliquée pour localiser les
N
rangées "plus grandes dans le texte" detablename
:la source
Il y a quelques choses qui pourraient se passer. En général, je doute que la longueur soit le problème proximal. Je suppose que vous avez plutôt un problème lié à la longueur.
Vous dites que les champs de texte peuvent atteindre quelques k. Une ligne ne peut pas dépasser 8 Ko dans la mémoire principale et il est probable que vos champs de texte les plus grands aient été TOASTés ou sortis de la mémoire principale vers une mémoire étendue dans des fichiers séparés. Cela rend votre stockage principal plus rapide (donc, id est en fait plus rapide car moins de pages de disque sont accessibles), mais select * devient plus lent car il y a plus d'entrées / sorties aléatoires.
Si la taille totale de vos lignes est toujours inférieure à 8 000 Ko, vous pouvez essayer de modifier les paramètres de stockage. Je tiens toutefois à vous avertir que l'insertion d'un attribut surdimensionné dans le stockage principal peut poser problème, il est donc préférable de ne pas y toucher si vous n'avez pas à le faire et si vous le faites, définissez des limites appropriées via des contraintes de vérification. Donc, le transport n'est probablement pas la seule chose. Il peut s'agir de rassembler de nombreux champs nécessitant des lectures aléatoires. Un grand nombre de lectures aléatoires peut également entraîner des erreurs de cache, et une grande quantité de mémoire requise peut nécessiter que les choses se matérialisent sur un disque et un grand nombre de lignes larges, si une jointure est présente (et il y en a une si TOAST est impliqué) peut être plus coûteux rejoindre des modèles, etc.
La première chose que je voudrais regarder est de sélectionner moins de lignes et de voir si cela aide. Si cela fonctionne, vous pouvez également essayer d'ajouter plus de RAM au serveur, mais je commencerais par voir où les performances commencent à chuter en raison de changements de plan et des erreurs de cache.
la source
Utilisation des fonctions de taille d’objet de base de données mentionnées ci-dessus:
la source