Question

Je suis en train de faire quelques tests de performance sur une nouvelle conception bd PostgreSQL 9.4rc1 et je vois une assez lent requêtes à l'aide de fonctions de la fenêtre.Voici ma table de configuration:

CREATE TABLE player_stat (
  player_id    VARCHAR(200) NOT NULL,
  stat_id      BIGINT NOT NULL,
  value        BIGINT NOT NULL DEFAULT 0,
  last_updated TIMESTAMP WITH TIME ZONE NOT NULL,
  last_active  TIMESTAMP WITH TIME ZONE DEFAULT NULL,

  CONSTRAINT player_stat_pk PRIMARY KEY (player_id, stat_id),
  CONSTRAINT player_stat_fk1 FOREIGN KEY(stat_id) REFERENCES stat (id)
);
CREATE INDEX player_stat_stat_value_player_desc
  ON player_stat (stat_id, value DESC, player_id ASC);

J'ai inséré 30 millions de lignes dans cette table réparti entre les 3 stats:

INSERT INTO player_stat (player_id, stat_id, value, last_updated) SELECT x.id, 1, x.v, now() FROM (SELECT generate_series(1,10000000) as id, trunc(random() * (1900-1200) + 1200) as v) AS x;
INSERT INTO player_stat (player_id, stat_id, value, last_updated) SELECT x.id, 2, x.v, now() FROM (SELECT generate_series(1,10000000) as id, trunc(random() * (1900-1200) + 1200) as v) AS x;
INSERT INTO player_stat (player_id, stat_id, value, last_updated) SELECT x.id, 3, x.v, now() FROM (SELECT generate_series(1,10000000) as id, trunc(random() * (1900-1200) + 1200) as v) AS x;

Ensuite, j'essaie de classer les joueurs pour une stat (MODIFIER):

SELECT * FROM 
( SELECT player_id
       , rank() OVER (ORDER BY value DESC, player_id ASC)  as rank 
  FROM player_stat 
  WHERE stat_id = 1
) as t 
WHERE rank <= 20 
ORDER BY rank ASC;

Cette requête prend environ 5,5 secondes pour revenir.L'exécution Expliquer sur il renvoie les éléments suivants:

"Sort  (cost=1167612.28..1176082.26 rows=3387993 width=15) (actual time=9726.132..9726.135 rows=20 loops=1)"
"  Sort Key: t.rank"
"  Sort Method: quicksort  Memory: 25kB"
"  ->  Subquery Scan on t  (cost=0.56..684349.57 rows=3387993 width=15) (actual time=0.080..9726.116 rows=20 loops=1)"
"        Filter: (t.rank <= 20)"
"        Rows Removed by Filter: 9999980"
"        ->  WindowAgg  (cost=0.56..557299.83 rows=10163979 width=15) (actual time=0.077..8351.124 rows=10000000 loops=1)"
"              ->  Index Only Scan using player_stat_stat_value_player_desc on player_stat  (cost=0.56..379430.20 rows=10163979 width=15) (actual time=0.054..2319.007 rows=10000000 loops=1)"
"                    Index Cond: (stat_id = 1)"
"                    Heap Fetches: 0"
"Planning time: 0.187 ms"
"Execution time: 9726.172 ms"

Est-il possible que puis-je accélérer les choses?Le temps semble être de plus en plus de façon linéaire avec le nombre de joueurs à la table.

Était-ce utile?

La solution

Est il possible que je peux accélérer les choses?

Oui. Ne pas utiliser de varchar colonne pour un integer numéro.Utilisation integer ou bigint si vous brûler que de nombreuses Id - beaucoup plus petit dans la table et l'index et rapide à traiter.Puisque vous êtes classement de 10 millions de lignes dans votre test, cela va faire une différence substantielle.

player_id VARCHAR(200) NOT NULL,
player_id int NOT NULL,

Ou un uuid si vous devez (j'en doute):

Votre requête rangs de 10 millions de lignes.Cela va prendre un certain temps, même lorsque la lecture à partir de l'index directement et pas de tri étape.

Note de côté:Si vous en vrac-insérer des lignes et ajouter des index et PK contrainte (et FK contrainte) après, ça va être beaucoup plus rapide, plus vous obtenez une parfaite index sans ballonnement sans courir REINDEX ou VACUUM FULL.Assurez-vous que ANALYZE a été exécuté sur la table avant de tester les performances, bien que.

Ce que vous ne demandez pas

..mais, aller sur une branche ici, ce sont probablement à la recherche pour.

L' EXPLAIN sortie révèle que vous filtrez le top 20 lignes: (t.rank <= 20). A présenté votre requête n'est pas de montrer que.La requête en réalité correspondant à votre EXPLAIN la sortie serait:

SELECT * FROM (
   SELECT player_id
        , rank() OVER (ORDER BY value DESC, player_id ASC) AS rank
   FROM   player_stat
   WHERE  stat_id = 1
   ) t
WHERE t.rank <= 20;

Ce qui peut être amélioré façon spectaculaire:

SELECT row_number() OVER (ORDER BY value DESC, player_id ASC) AS rank
     , player_id
FROM   player_stat
WHERE  stat_id = 1
ORDER  BY value DESC, player_id
LIMIT  20;

Explication

  • La partie importante pour les performances est l' LIMIT la clause en combinaison avec ORDER BY correspondant à l'index:maintenant la requête lit exactement 20 lignes à partir du haut de l'index, où il a dû lire 10000000 dans votre version d'origine.Nous n'utilisons que des player_id et value, de sorte que nous pouvons encore avoir un indice de la seule analyse.Le reste est de l'arachide.

  • C'est tout en raison de la la séquence des événements dans un SELECT requête:les fonctions de la fenêtre sont appliquées avant LIMIT.Seulement si l'ordre de tri est d'accord, nous n'avons pas à examiner le reste de la 10000000 lignes.

  • Nous pouvons utiliser LIMIT 20 parce que le top 20 des classements de garantie de durée pas plus de 20 lignes.Le PK sur (player_id, stat_id) garanties unique player_id par stat_id et vu qu'il est inclus dans le ORDER BY, chaque rang n'est attribué qu'une fois - ce qui signifie également que nous pouvons utiliser le légèrement moins cher row_number() au lieu de cela.

Licencié sous: CC-BY-SA avec attribution
Non affilié à dba.stackexchange
scroll top