Scansione sequenziale PostgreSQL anziché Scansione indice Perché?


11

Ciao a tutti Ho un problema con la mia query sul database PostgreSQL e mi chiedo se qualcuno può aiutarmi. In alcuni scenari la mia query sembra ignorare l'indice che ho creato che viene utilizzato per unire le due tabelle datae data_area. Quando ciò accade, utilizza una scansione sequenziale e risulta in una query molto più lenta.

Scansione sequenziale (~ 5 minuti)

Unique  (cost=15368261.82..15369053.96 rows=200 width=1942) (actual time=301266.832..301346.936 rows=153812 loops=1)
   CTE data
     ->  Bitmap Heap Scan on data  (cost=6086.77..610089.54 rows=321976 width=297) (actual time=26.286..197.625 rows=335130 loops=1)
           Recheck Cond: (datasetid = 1)
           Filter: ((readingdatetime >= '1920-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2013-03-11 00:00:00'::timestamp without time zone) AND (depth >= 0::double precision) AND (depth <= 99999::double precision))
           ->  Bitmap Index Scan on data_datasetid_index  (cost=0.00..6006.27 rows=324789 width=0) (actual time=25.462..25.462 rows=335130 loops=1)
                 Index Cond: (datasetid = 1)
   ->  Sort  (cost=15368261.82..15368657.89 rows=158427 width=1942) (actual time=301266.829..301287.110 rows=155194 loops=1)
         Sort Key: data.id
         Sort Method: quicksort  Memory: 81999kB
         ->  Hash Left Join  (cost=15174943.29..15354578.91 rows=158427 width=1942) (actual time=300068.588..301052.832 rows=155194 loops=1)
               Hash Cond: (data_area.area_id = area.id)
               ->  Hash Join  (cost=15174792.93..15351854.12 rows=158427 width=684) (actual time=300066.288..300971.644 rows=155194 loops=1)
                     Hash Cond: (data.id = data_area.data_id)
                     ->  CTE Scan on data  (cost=0.00..6439.52 rows=321976 width=676) (actual time=26.290..313.842 rows=335130 loops=1)
                     ->  Hash  (cost=14857017.62..14857017.62 rows=25422025 width=8) (actual time=300028.260..300028.260 rows=26709939 loops=1)
                           Buckets: 4194304  Batches: 1  Memory Usage: 1043357kB
                           ->  Seq Scan on data_area  (cost=0.00..14857017.62 rows=25422025 width=8) (actual time=182921.056..291687.996 rows=26709939 loops=1)
                                 Filter: (area_id = ANY ('{28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11}'::integer[]))
               ->  Hash  (cost=108.49..108.49 rows=3349 width=1258) (actual time=2.256..2.256 rows=3349 loops=1)
                     Buckets: 1024  Batches: 1  Memory Usage: 584kB
                     ->  Seq Scan on area  (cost=0.00..108.49 rows=3349 width=1258) (actual time=0.007..0.666 rows=3349 loops=1)
 Total runtime: 301493.379 ms

Scansione indice (~ 3 secondi) ( su spiega.depesz.com )

Unique  (cost=17352256.47..17353067.50 rows=200 width=1942) (actual time=3603.303..3681.619 rows=153812 loops=1)
   CTE data
     ->  Bitmap Heap Scan on data  (cost=6284.60..619979.56 rows=332340 width=297) (actual time=26.201..262.314 rows=335130 loops=1)
           Recheck Cond: (datasetid = 1)
           Filter: ((readingdatetime >= '1920-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2013-03-11 00:00:00'::timestamp without time zone) AND (depth >= 0::double precision) AND (depth <= 99999::double precision))
           ->  Bitmap Index Scan on data_datasetid_index  (cost=0.00..6201.51 rows=335354 width=0) (actual time=25.381..25.381 rows=335130 loops=1)
                 Index Cond: (datasetid = 1)
   ->  Sort  (cost=17352256.47..17352661.98 rows=162206 width=1942) (actual time=3603.302..3623.113 rows=155194 loops=1)
         Sort Key: data.id
         Sort Method: quicksort  Memory: 81999kB
         ->  Hash Left Join  (cost=1296.08..17338219.59 rows=162206 width=1942) (actual time=29.980..3375.921 rows=155194 loops=1)
               Hash Cond: (data_area.area_id = area.id)
               ->  Nested Loop  (cost=0.00..17334287.66 rows=162206 width=684) (actual time=26.903..3268.674 rows=155194 loops=1)
                     ->  CTE Scan on data  (cost=0.00..6646.80 rows=332340 width=676) (actual time=26.205..421.858 rows=335130 loops=1)
                     ->  Index Scan using data_area_pkey on data_area  (cost=0.00..52.13 rows=1 width=8) (actual time=0.006..0.008 rows=0 loops=335130)
                           Index Cond: (data_id = data.id)
                           Filter: (area_id = ANY ('{28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11}'::integer[]))
               ->  Hash  (cost=1254.22..1254.22 rows=3349 width=1258) (actual time=3.057..3.057 rows=3349 loops=1)
                     Buckets: 1024  Batches: 1  Memory Usage: 584kB
                     ->  Index Scan using area_primary_key on area  (cost=0.00..1254.22 rows=3349 width=1258) (actual time=0.012..1.429 rows=3349 loops=1)
 Total runtime: 3706.630 ms

Struttura della tabella

Questa è la struttura della data_areatabella per la tabella. Posso fornire gli altri tavoli se necessario.

CREATE TABLE data_area
(
  data_id integer NOT NULL,
  area_id integer NOT NULL,
  CONSTRAINT data_area_pkey PRIMARY KEY (data_id , area_id ),
  CONSTRAINT data_area_area_id_fk FOREIGN KEY (area_id)
      REFERENCES area (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT data_area_data_id_fk FOREIGN KEY (data_id)
      REFERENCES data (id) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE CASCADE
);

QUERY

WITH data AS (
    SELECT * 
    FROM data 
    WHERE 
        datasetid IN (1) 
        AND (readingdatetime BETWEEN '1920-01-01' AND '2013-03-11') 
        AND depth BETWEEN 0 AND 99999
)
SELECT * 
FROM ( 
    SELECT DISTINCT ON (data.id) data.id, * 
    FROM 
        data, 
        data_area 
        LEFT JOIN area ON area_id = area.id 
    WHERE 
        data_id = data.id 
        AND area_id IN (28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11) 
) as s;

Restituisce le 153812righe. Ho set enable_seqscan= false;disabilitato la scansione sequenziale e ottenuto il risultato dell'indice.

Ho provato a fare una ricerca ANALYSEsul database e ad aumentare le statistiche raccolte sulle colonne utilizzate nella query, ma nulla sembra aiutare.

Qualcuno potrebbe diffondersi e fare luce su questo o suggerire qualcos'altro che dovrei provare?


Sarebbe utile a me , se hai incluso le query che hanno generato ciascuno di questi piani di esecuzione.
Mike Sherrill 'Cat Recall'

Una differenza di 2 ordini di grandezza nel numero stimato di righe e nel numero effettivo di righe? Lo sto leggendo bene?
Mike Sherrill 'Cat Recall'

@Catcall Ho aggiunto la query (un po 'fondamentale per riuscire a capire cosa sta succedendo). Quando fai riferimento alle righe stimate è che il 200 e quindi il suo effettivo ritorno 153812?
Mark Davidson,

2
Sì, 200 vs 150k sembrano strane a colpo d'occhio. C'è un motivo convincente per mescolare un join sinistro con un prodotto cartesiano ( FROM data, data_area)? A prima vista, usare DISTINCT ON senza una clausola ORDER BY sembra essere una cattiva idea.
Mike Sherrill 'Cat Recall'

spiegato.depesz.com/s/Uzin potrebbe essere informativo.
Craig Ringer,

Risposte:


7

Nota questa riga:

->  Index Scan using data_area_pkey on data_area  (cost=0.00..52.13 rows=1 width=8) 
    (actual time=0.006..0.008 rows=0 loops=335130)

Se si calcola il costo totale, considerando i loop, lo è 52.3 * 335130 = 17527299. Questo è maggiore di 14857017.62 per l' seq_scanalternativa. Questo è il motivo per cui non utilizza l'indice.

Quindi l'ottimizzatore sta sopravvalutando il costo della scansione dell'indice. Immagino che i tuoi dati siano ordinati sull'indice (o a causa di un indice cluster o di come sono stati caricati) e / o hai molta memoria cache e / o un bel disco veloce. Quindi c'è un piccolo I / O casuale in corso.

Si dovrebbe anche controllare l' correlationin pg_stats, che viene utilizzato da l'ottimizzatore per valutare il clustering nel calcolo dell'indice del costo, e, infine, provare a cambiare random_page_coste cpu_index_tuple_cost, per abbinare il vostro sistema.


A meno che non mi manchi qualcosa, penso che @jop non significhi 52.13, il 52.3che comporterebbe 17470326.9 (ancora più grande del seq_scan)
BotNet

2

Il CTE in realtà non fa altro che "esternalizzare" alcune WHEREcondizioni, molte delle quali sembrano equivalenti WHERE TRUE. Dato che i CTE sono generalmente dietro una barriera di ottimizzazione (il che significa che è ottimizzato da solo), possono aiutare molto con determinate query. In questo caso, tuttavia, mi aspetto l'effetto esatto opposto.

Quello che vorrei provare è di riscrivere la query per essere il più semplice possibile:

SELECT d.id, * 
FROM 
    data d 
    JOIN data_area da ON da.data_id = d.id
    LEFT JOIN area a ON da.area_id = a.id 
WHERE 
    d.datasetid IN (1) 
    AND da.area_id IN (28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11) 
    AND (readingdatetime BETWEEN '1920-01-01' AND '2013-03-11') -- this and the next condition don't do anything, I think
    AND depth BETWEEN 0 AND 99999
;

e quindi controlla se l'indice viene utilizzato o meno. È ancora possibile che non siano necessarie tutte le colonne di output (almeno le due colonne della tabella di giunzione sono superflue).

Si prega di riferire e dirci quale versione PostgreSQL si utilizza.


Grazie per il tuo suggerimento, mi scuso per la ritardata risposta al tuo post, ho lavorato su altri progetti. Il tuo suggerimento significa in effetti che la query ora sembra utilizzare in modo affidabile l'indice per tutte le query, ma non riesco ancora a ottenere le prestazioni che mi aspetterei da esso. Ho effettuato un'analisi su una query che contiene molti più dati spiegazione.depesz.com/s/1yu richiede circa 4 minuti con il 95% del tempo impiegato per la scansione INDEX.
Mark Davidson,

Ho dimenticato di menzionare che sto usando la versione 9.1.4
Mark Davidson

Fondamentalmente la scansione dell'indice è abbastanza veloce, il problema è che si ripete qualche milione di volte. Cosa ottieni se SET enable_nestloop=offprima di eseguire la query?
dezso

-1

Per i follower, ho avuto un problema simile che era simile

select * from table where bigint_column between x and y and mod(bigint_column, 10000) == z

Il problema era che il mio bigint_column "tra xey" aveva un indice, ma la mia query era sostanzialmente "tutte le righe" in quella tabella, quindi non stava usando l'indice [dato che doveva comunque scansionare l'intera tabella] ma stava eseguendo una scansione sequenziale seq_scan. Una soluzione per me era creare un nuovo indice per il lato "mod" dell'equazione, in modo che potesse usarlo su un'espressione .


i downvoter si sentono liberi di lasciare commenti sul perché :)
rogerdpack il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.