Vous n'êtes pas identifié(e).

#1 Re : Optimisation » Baisse de performance sur table enfant en utilisant ORDER BY et LIMIT » 09/10/2009 11:56:23

- Ce qui me chiffonne c'est que les VACUUM ANALYSE sont effectués régulièrement sur mes tables par l'autovacuum.
Question bête : les VACUUM ANALYSE lancés par l'autovacuum sont-ils moins "performants" ? Comprendre : agissent-ils différent que ceux lancés manuellement ?

- J'ai l'impression que ma structure FSM est adaptée à mes besoins :
max_fsm_pages = 800000
max_fsm_relations = 1000

Résultat final d'un VACUUM ANALYSE sur l'ensemble de mes données :

INFO:  la structure FSM contient 54193 pages dans 89 relations
DETAIL:  Un total de 55248 emplacements de pages est utilisé (ceci incluant la
surcharge).
55248 emplacements de pages sont requis pour tracer tout l'espace libre.
Les limites actuelles sont : 800000 emplacements de pages, 1000 relations,
utilisant 4752 Ko.
VACUUM

- Pour en revenir au VACUUM FULL, quelles sont ces "très mauvaises requêtes" qui mettraient en défaut une maintenance bien faite ?
La suppression de 10% d'enregistrements sur une table de plus d'un million par exemple ?

#2 Re : Optimisation » Baisse de performance sur table enfant en utilisant ORDER BY et LIMIT » 08/10/2009 16:25:36

Merci Marc pour ces propositions.

Monter default_statistics_target à 100 ne change rien à mon problème.
Par contre, l'index composé (account_id, state) le résoud (j'ai laissé default_statistics_target à 100, je peux éventuellement essayer de le repasser à 10).

En parallèle, j'ai effectué un VACUUM FULL (hier je me suis contenté d'un ANALYZE) sur ma seconde machine sans ajouter l'index composé et j'obtiens également des performances satisfaisantes.

2 questions me viennent à l'esprit :

- Si je comprends bien l'index sur state me dessert plus qu'autre chose dans ce cas précis, car comme vous le dites PostgreSQL fait le mauvais paris. Simple curiosité, existe-t'il d'autre moyens que l'index composé pour "forcer" l'utilisation d'un index plutôt qu'un autre ? Parce que je m'attendais naturellement à ce qu'il utilise l'index sur account_id, puis state...

- Le démon autovacuum passe régulièrement sur toutes mes tables, je pensais ne plus avoir besoin de VACUUM FULL... Est-ce qu'effectuer régulièrement un VACUUM FULL est une bonne pratique ? Ou alors cela résulte d'une mauvaise configuration ?

#3 Optimisation » Baisse de performance sur table enfant en utilisant ORDER BY et LIMIT » 07/10/2009 18:03:26

paftek
Réponses : 8

Bonjour,

Je n'arrive pas à expliquer une importante différence de performance sur une requête simple, en fonction de l'évolution du seul paramètre "account_id" :

SELECT * FROM listings_5
WHERE account_id = 981
ORDER BY state ASC
LIMIT 50 OFFSET 0

"listings_5" est une table enfant de "listings". Le découpage est effectuée en fonction de la valeur d'un champ "store_id".
Les colonnes "account_id", "state" et "store_id" sont indexées.

Lorsque je lance la requête ci-dessus directement sur la table "listings_5" pour account_id = 981, j'obtiens ceci :
(la table contient 154 461 enregistrements pour le compte 981)

Limit  (cost=0.00..684.02 rows=50 width=1452) (actual time=238.055..2027.142 rows=50 loops=1)
  ->  Index Scan using listings_5_state_idx on listings_5  (cost=0.00..1960460.66 rows=143305 width=1452) (actual time=238.053..2027.093 rows=50 loops=1)
        Filter: (account_id = 981)
Total runtime: 2027.265 ms

Pour account_id = 1699 :
(alors que la table ne contient aucun enregistrement pour le compte 1699)

Limit  (cost=0.00..17029.71 rows=50 width=1452) (actual time=95497.761..95497.761 rows=0 loops=1)
  ->  Index Scan using listings_5_state_idx on listings_5  (cost=0.00..1960460.66 rows=5756 width=1452) (actual time=95497.759..95497.759 rows=0 loops=1)
        Filter: (account_id = 1699)
Total runtime: 95497.856 ms

En exécutant ces requêtes l'une après l'autre à plusieurs reprises, j'obtiens à peu près le même résultat.

J'ai effectué un VACUUM ANALYSE sur toutes les tables, j'ai redémarré la base, j'ai réindexé les tables listings, sans succès.
De plus je ne constate pas cette différence sur ma seconde machine de test, même matériel, même config, mais qui contient une version plus récente de ma base de prod (donc plus lourde).

Autres précisions :
- Si j'effectue la même requête sur la table mère, en précisant store_id = 5, la tendance s'inverse... (instantané pour 1699, quelques secondes pour 981)
- Si je supprime le critère de tri, le temps de réponse est "normal"...
- Si j'ajoute un critère de tri final (listing_id, la clé primaire), le temps de réponse est "normal" également... (instantané pour 1699, quelques secondes pour 981) :

SELECT * FROM listings_5
WHERE account_id = 1699
ORDER BY state ASC, listing_id
LIMIT 50 OFFSET 0

-------

Limit  (cost=19855.16..19855.28 rows=50 width=1468) (actual time=0.015..0.015 rows=0 loops=1)
  ->  Sort  (cost=19855.16..19869.84 rows=5872 width=1468) (actual time=0.014..0.014 rows=0 loops=1)
        Sort Key: state, listing_id
        Sort Method:  quicksort  Memory: 17kB
        ->  Index Scan using listings_ebay_account_idx on listings_ebay  (cost=0.00..19660.09 rows=5872 width=1468) (actual time=0.008..0.008 rows=0 loops=1)
              Index Cond: (account_id = 1699)
Total runtime: 0.149 ms

Bref, je vous avoue que je suis un peu perdu. Je ne sais pas quelle démarche effectuer pour traquer le problème.
C'est pourquoi je fais appel à vous...

Merci pour tout élément de réponse !

#5 Re : PL/pgSQL » Colonne en lecture seule et TRIGGERS » 13/10/2008 10:11:48

Bonjour,

Cette solution fonctionne, mais je trouve peu élégant de surcharger les valeurs des colonnes price_ht et price_ttc de la table commande.

Mais bon, si c'est le seul moyen d'assurer la cohérence des données, et étant donné qu'il y aura très peu de mises à jour des prix sur ces tables, je pense que cette solution sera validée et exploitée très bientôt.

#6 Re : PL/pgSQL » Colonne en lecture seule et TRIGGERS » 29/09/2008 15:20:41

OK merci. Voici un aperçu du code résultant :

Table commande

CREATE OR REPLACE FUNCTION commande_before_update() RETURNS TRIGGER AS $$
BEGIN
    IF OLD.price_ht <> NEW.price_ht OR OLD.price_ttc <> NEW.price_ttc
    THEN
        SELECT INTO NEW.price_ht, NEW.price_ttc
        COALESCE(sum(price_ht), 0) AS price_ht, COALESCE(sum(price_ttc), 0) AS price_ttc
        FROM produit WHERE commande_id = OLD.commande_id;
    END IF;
    
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Table Produit

CREATE OR REPLACE FUNCTION produit_after_update() RETURNS TRIGGER AS $$
BEGIN
    IF OLD.commande_id <> NEW.commande_id
    THEN
        UPDATE commande SET
            price_ht = price_ht - OLD.price_ht,
            price_ttc = price_ttc - OLD.price_ttc
        WHERE commande_id = OLD.commande_id;
        UPDATE commande SET
            price_ht = price_ht + NEW.price_ht,
            price_ttc = price_ttc + NEW.price_ttc
        WHERE commande_id = NEW.commande_id;
    ELSIF OLD.price_ht <> NEW.price_ht OR OLD.price_ttc <> NEW.price_ttc
    THEN
        UPDATE commande SET
            price_ht = price_ht - OLD.price_ht + NEW.price_ht,
            price_ttc = price_ttc - OLD.price_ttc + NEW.price_ttc
        WHERE commande_id = OLD.commande_id;
    END IF;
   
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

#7 PL/pgSQL » Colonne en lecture seule et TRIGGERS » 29/09/2008 12:09:27

paftek
Réponses : 4

Bonjour,

J'ai 2 tables, "commande" et "produit". Une commande est liée à 1..n produit(s).
J'ai ajouté une colonne "prix" à "commande", somme des prix des produits associés, calculé automatiquement par trigger.

Je souhaite maintenant que ce champ "prix" soit en lecture seule. J'ai donc ajouté un trigger sur la table "commande" pour empêcher tout UPDATE.
Le problème est que je souhaiterais quand même laisser passer l'UPDATE provenant du trigger sur la table "produit"...

Est-il possible de détecter l'origine d'un UPDATE et d'agir en conséquence ?

Merci,
Julien

Pied de page des forums

Propulsé par FluxBB