Cosa viene recuperato dal disco durante una query?


13

Domanda abbastanza semplice, probabilmente ha risposto da qualche parte, ma non riesco a formare la domanda di ricerca giusta per Google ...

Il numero di colonne in una particolare tabella influisce sulle prestazioni di una query quando si esegue una query su un sottoinsieme di quella tabella?

Ad esempio, se la tabella Foo ha 20 colonne, ma la mia query seleziona solo 5 di quelle colonne, avere 20 (contro, diciamo, 10) colonne influenza le prestazioni della query? Supponiamo per semplicità che qualsiasi cosa nella clausola WHERE sia inclusa in quelle 5 colonne.

Sono preoccupato per l'utilizzo della cache buffer di Postgres oltre alla cache del disco del sistema operativo. Ho una conoscenza molto persa del progetto di archiviazione fisica di Postgres. Le tabelle sono memorizzate su più pagine (il cui valore predefinito è 8k per pagina), ma non capisco bene come siano disposte le tuple da lì. PG è abbastanza intelligente da recuperare solo dal disco i dati che comprendono quelle 5 colonne?


Stai parlando di recuperare 50 byte ma non i restanti 150. Il tuo disco probabilmente legge con incrementi maggiori di così!
Andomar,

Da dove prendi quei numeri?
Jmoney38,

Risposte:


14

La memoria fisica per le righe è descritta nei documenti in Layout di pagina del database . I contenuti della colonna per la stessa riga sono tutti memorizzati nella stessa pagina del disco, con la notevole eccezione dei contenuti di TOAST (troppo grandi per essere inseriti in una pagina). I contenuti vengono estratti in sequenza all'interno di ogni riga, come spiegato:

Per leggere i dati è necessario esaminare ogni attributo a turno. Innanzitutto controlla se il campo è NULL in base alla bitmap null. Se lo è, vai al prossimo. Quindi assicurati di avere il giusto allineamento. Se il campo è un campo a larghezza fissa, tutti i byte vengono semplicemente inseriti.

Nel caso più semplice (senza colonne TOAST), postgres recupererà l'intera riga anche se sono necessarie poche colonne. Quindi, in questo caso, la risposta è sì, avere più colonne può avere un chiaro impatto negativo sulla cache del buffer waster, in particolare se il contenuto della colonna è grande mentre è ancora sotto la soglia TOAST.

Ora il caso TOAST: quando un singolo campo supera ~ 2kB, il motore memorizza i contenuti del campo in una tabella fisica separata. Entra anche in gioco quando l'intera riga non rientra in una pagina (8 KB per impostazione predefinita): alcuni dei campi vengono spostati nella memoria TOAST. Doc dice:

Se è un campo di lunghezza variabile (attlen = -1) è un po 'più complicato. Tutti i tipi di dati a lunghezza variabile condividono la struttura comune header varlena, che include la lunghezza totale del valore memorizzato e alcuni bit di flag. A seconda dei flag, i dati possono essere in linea o in una tabella TOAST; potrebbe anche essere compresso

I contenuti di TOAST non vengono recuperati quando non sono esplicitamente necessari, quindi il loro effetto sul numero totale di pagine da recuperare è ridotto (pochi byte per colonna). Questo spiega i risultati nella risposta di @ dezso.

Per quanto riguarda le scritture, ogni riga con tutte le sue colonne viene interamente riscritta su ogni AGGIORNAMENTO, indipendentemente dalle colonne modificate. Quindi avere più colonne è ovviamente più costoso per le scritture.


Questa è una risposta incredibile. Esattamente quello che sto cercando. Grazie.
Jmoney38,

1
Una buona risorsa che ho trovato per quanto riguarda la struttura delle righe (pageinspect e alcuni esempi di utilizzo) qui .
Jmoney38,

9

La risposta di Daniel si concentra sul costo della lettura di singole righe. In questo contesto: mettere le NOT NULLcolonne a dimensione fissa al primo posto nella tabella aiuta un po '. Mettere prima le colonne pertinenti (quelle per cui si esegue una query) aiuta un po '. Ridurre al minimo il riempimento (a causa dell'allineamento dei dati) giocando l'allineamento tetris con le colonne può aiutare un po '. Ma l'effetto più importante non è stato ancora menzionato, specialmente per i grandi tavoli.

Le colonne aggiuntive fanno ovviamente sì che una riga copra più spazio su disco, in modo che meno righe si adattino a una pagina di dati (8 KB per impostazione predefinita). Le singole righe sono distribuite su più pagine. Il motore di database deve generalmente recuperare intere pagine, non singole righe . Poco importa se le singole righe siano in qualche modo più piccole o più grandi, purché si debba leggere lo stesso numero di pagine.

Se una query recupera una porzione (relativamente) piccola di una tabella grande, in cui le righe sono distribuite più o meno casualmente sull'intera tabella, supportate da un indice, ciò comporterà all'incirca lo stesso numero di letture di pagine, con poca considerazione alle dimensioni della riga. Le colonne irrilevanti non ti rallenteranno molto in un caso (raro).

In genere, recupererai patch o cluster di righe immesse in sequenza o in prossimità e condividerai pagine di dati. Quelle righe sono distribuite a causa del disordine, più pagine del disco devono essere lette per soddisfare la tua richiesta. Dover leggere più pagine è in genere il motivo più importante per una query più lenta. E questo è il fattore più importante per cui colonne irrilevanti rendono più lente le tue query.

Con grandi database, in genere non c'è abbastanza RAM per mantenere tutto nella memoria cache. Le righe più grandi occupano più cache, più contese, meno hit nella cache, più I / O su disco. E le letture del disco sono in genere molto più costose. Meno con gli SSD, ma rimane una differenza sostanziale. Ciò si aggiunge al punto precedente sulle letture della pagina.

Può essere o meno importante se le colonne non pertinenti sono TOAST-ed. Anche le colonne pertinenti possono essere TOAST, riportando gran parte dello stesso effetto.


1

Un piccolo test:

CREATE TABLE test2 (
    id serial PRIMARY KEY,
    num integer,
    short_text varchar(32),
    longer_text varchar(1000),
    long_long_text text
);

INSERT INTO test2 (num, short_text, longer_text, long_long_text)
SELECT i, lpad('', 32, 'abcdefeghji'), lpad('', 1000, 'abcdefeghji'), lpad('', (random() * 10000)::integer, 'abcdefeghji')
FROM generate_series(1, 10000) a(i);

ANALYZE test2;

SELECT * FROM test2;
[...]
Time: 1091.331 ms

SELECT num FROM test2;
[...]
Time: 21.310 ms

Limitando la query alle prime 250 righe ( WHERE num <= 250) si ottengono rispettivamente 34.539 ms e 8.343 ms. Selezionando tutto tranne long_long_textquesto set limitato, si ottengono 18.432 ms. Questo dimostra che PG è abbastanza intelligente.


Bene, apprezzo sicuramente l'input. Tuttavia, non posso dire con certezza che questo scenario di prova dimostra ciò che ho proposto inizialmente. Ci sono alcuni problemi Per uno, quando si esegue per la prima volta "SELECT * FROM test2", questo dovrebbe aver riempito la cache del buffer condiviso. Quella query avrebbe richiesto molto più tempo per essere recuperata dal disco. Pertanto, la seconda query sarebbe stata teoricamente molto più veloce perché sarebbe stata recuperata dalla cache SB. Ma sono d'accordo sul fatto che "suggerisce" che PG recupera solo le righe di cui ha bisogno, in base ai test / confronti successivi.
Jmoney38,

Hai ragione, questo test (essendo semplice) ha i suoi difetti. Se avrò abbastanza tempo, proverò a coprire anche questi.
dezso
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.