Domanda

Sto facendo alcuni test delle prestazioni su un nuovo design DB su PostgreSQL 9.4RC1 e sto vedendo alcune query piuttosto lente usando le funzioni della finestra.Ecco la mia configurazione della tabella:

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);
.

Ho inserito 30 milioni di righe in questa tabella divisa tra 3 statistiche:

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;
.

Allora cerco di classificare i giocatori per una determinata stat ( modifica ):

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;
.

Questa query richiede circa 5,5 secondi per tornare.Esecuzione spiegata su di esso restituisce quanto segue:

"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"
.

C'è qualche modo possibile accelerare questo?Il tempo impiegato sembra crescere linearmente con il numero di giocatori nel tavolo.

È stato utile?

Soluzione

.

C'è un modo in cui posso accelerare questo?

Sì. Non utilizzare una colonna varchar per un numero integer. Utilizzare integer o bigint se bruciano tanti IDS - molto più piccolo nella tabella e nell'indice e più velocemente da elaborare. Dal momento che sei Classifica 10 milioni di righe Nel tuo test, questo farà una differenza sostanziale.

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

o un uuid se devi (dubito che):

La tua query classifica 10 milioni di righe. Ci vorrà un po 'di tempo, anche quando leggi dall'indice direttamente e nessun passaggio.

Nota laterale: se si inseriscono innanzitutto le righe Inserire Bulk e aggiungi il vincolo dell'indice e PK (e il vincolo fk) dopo che sarà molto più veloce, oltre a ottenere Indici perfetti senza gonfiaggio senza eseguire REINDEX o VACUUM FULL. Assicurarsi che ANALYZE sia stato eseguito sulla tabella prima del test delle prestazioni, però.

Cosa non hai chiesto

.. ma, uscire con un arto qui, cosa probabilmente cerca.

L'uscita EXPLAIN rivela che si filtrano le prime 20 righe: (t.rank <= 20). La tua query presentata non mostra quella . La query che corrisponde in realtà l'uscita EXPLAIN sarebbe:

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;
.

che può essere migliorato drammaticamente :

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;
.

Spiegazione

    .
  • La parte importante per le prestazioni è la clausola LIMIT in combinazione con ORDER BY corrispondente all'indice: ora la query legge esattamente 20 righe dall'alto verso l'indice, dove ha dovuto leggere 10000000 nella versione originale. Utilizziamo solo player_id e value, quindi possiamo ancora avere una scansione solo in indice. Il resto è arachidi.

  • Questo è tutto dovuto al sequenza di eventi in una query SELECT : le funzioni della finestra sono applicate Prima LIMIT. Solo se l'ordinamento è d'accordo, non dobbiamo considerare il resto delle righe di 10000000 applicabili.

  • Possiamo utilizzare LIMIT 20 perché i primi 20 gradi sono garantiti per durare non più di 20 righe. Il PK su (player_id, stat_id) garantisce un player_idetagCodetagCodeTagCode unico e poiché è incluso nel stat_id, ogni rango è assegnato solo una volta - che significa anche che possiamo usare anche il ORDER BY invece.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a dba.stackexchange
scroll top