martedì 20 agosto 2013

L'uso del SeqScan

Il sequential scan o scansione sequenziale e' uno dei metodo di accesso ai dati in una tabella che ogni database,
e conseguentemente PostgreSQL, fornisce. Tale metodo di accesso non e' assolutamente ottimizzato e prevede la lettura "stupida" del contenuto di una tabella in maniera sequenziale, ovvero dalla prima tupla fino all'ultima.

PostgreSQL non supporta i query hints, ovvero non consente di specificare a livello di query quale indice utilizzare per recuperare le tuple. Tuttavia PostgreSQL fornisce alcuni parametri di configurazione che possono essere configurati a livello di cluster per abilitare o disabilitare alcuni metodi di accesso. Ne consegue che per forzare l'accesso tramite indice ai dati si possono disabilitare gli altri metodi di accesso.

Questo non vale per il sequential scan: tale metodo di accesso e' da considerarsi una sorta di "ultima spiaggia" per l'accesso ai dati. Di conseguenza il sequential scan non puo' essere evitato se non c'e' altro modo di recuperare i dati.

Per meglio comprendere questo concetto si consideri una piccola tabella (ossia con poche tuple) e un indice sulla chiave primaria definita come segue:


demodb=# CREATE TABLE foo( pk SERIAL NOT NULL, description TEXT, PRIMARY KEY( pk ) );
NOTICE: CREATE TABLE will create implicit sequence "foo_pk_seq" for serial column "foo.pk"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo"
CREATE TABLE
demodb=# INSERT INTO foo( description ) VALUES( 'Test ' || generate_series(1, 100 ) );
INSERT 0 100
demodb=# ANALYZE foo;
ANALYZE
demodb=# SELECT relname, relpages, reltuples FROM pg_class
WHERE relname = 'foo' AND relkind = 'r';

relname | relpages | reltuples
---------+----------+-----------
foo | 1 | 100
(1 row)



Come ci si puo' aspettare, essendo la relazione piuttosto piccola (solo una pagina dati), l'accesso ai dati sara' di tipo sequenziale anche in presenza di una query risolvibile tramite l'indice:


demodb=# SHOW enable_seqscan;
enable_seqscan
----------------
on
(1 row)

demodb=# EXPLAIN ANALYZE SELECT * FROM foo WHERE pk = 10;
QUERY PLAN
---------------------------------------------------------------
Seq Scan on foo (cost=0.00..2.25 rows=1 width=11)
(actual time=0.024..0.053 rows=1 loops=1)
Filter: (pk = 10)
Total runtime: 0.097 ms
(3 rows)



Essendo presente un indice, se si disabilita il sequential scan si ottiene che l'accesso ai dati viene forzatamente fatto tramite il suddetto indice:



demodb=# SET enable_seqscan TO off;
SET
demodb=# SHOW enable_seqscan;
enable_seqscan
----------------
off
(1 row)

demodb=# EXPLAIN ANALYZE SELECT * FROM foo WHERE pk = 10;
QUERY PLAN
----------------------------------------------------------------------------
Index Scan using foo_pkey on foo (cost=0.00..8.27 rows=1 width=11)
(actual time=0.031..0.034 rows=1 loops=1)
Index Cond: (pk = 10)
Total runtime: 0.086 ms
(3 rows)



Come si puo' facilmente vedere il costo dell'accesso via indice e' superiore a quello sequenziale, poiché la relazione e' troppo piccola per trarre giovamento dal caricamento delle pagine di indice.
Ma cosa succede se l'indice non e' presente? E' sufficiente ripetere l'esperimento senza creare l'indice sulla chiave primaria per verificare il comportamento:



demodb=# DROP TABLE foo;
DROP TABLE
demodb=# CREATE TABLE foo( pk SERIAL NOT NULL, description TEXT );
NOTICE: CREATE TABLE will create implicit sequence "foo_pk_seq" for serial column "foo.pk"
CREATE TABLE
demodb=# INSERT INTO foo( description ) VALUES( 'Test ' || generate_series(1, 100 ) );
INSERT 0 100
demodb=# ANALYZE foo;
ANALYZE
demodb=# SHOW enable_seqscan;
enable_seqscan
----------------
off
(1 row)

demodb=# EXPLAIN ANALYZE SELECT * FROM foo WHERE pk = 10;
QUERY PLAN
-----------------------------------------------------------------------------
Seq Scan on foo (cost=10000000000.00..10000000002.25 rows=1 width=11)
(actual time=0.025..0.054 rows=1 loops=1)
Filter: (pk = 10)
Total runtime: 0.100 ms
(3 rows)



Come si puo' notare l'accesso ai dati e' ancora di tipo sequenziale, e non potrebbe essere altrimenti visto che non vi sono altri "percorsi" per raggiungere i dati. La cosa interessante pero' e' che il costo di accesso ai dati e' notevolmente piu' grande del caso precedente, e si tratta solo di un costo presunto, visto che quello effettivo e' rimasto invariato.
L'idea e' quindi quella di aumentare notevolmente il costo presunto di accesso per invogliare l'ottimizzatore a cercare altre strade per l'accesso ai dati.
Da notare che il costo di accesso sequenziale non viene mutato, di fatto e' il query planner che incrementa il costo presunto quando trova un metodo di accesso disabilitato.

Nessun commento: