Memorizzazione nella cache dell'indice PostgreSQL


16

Sto incontrando difficoltà nel trovare spiegazioni "semplici" su come gli indici vengono memorizzati nella cache di PostgreSQL, quindi mi piacerebbe un controllo della realtà su una o tutte queste ipotesi:

  1. Gli indici PostgreSQL, come le righe, vivono su disco ma possono essere memorizzati nella cache.
  2. Un indice può essere interamente nella cache o per niente.
  3. La memorizzazione nella cache dipende dalla frequenza con cui viene utilizzata (come definito dal pianificatore di query).
  4. Per questo motivo la maggior parte degli indici "sensibili" saranno sempre nella cache.
  5. Gli indici vivono nella stessa cache (il buffer cache?) Delle righe e quindi lo spazio cache utilizzato da un indice non è disponibile per le righe.


La mia motivazione per comprendere ciò deriva da un'altra domanda che mi è stata posta dove è stato suggerito che gli indici parziali possono essere utilizzati su tabelle in cui la maggior parte dei dati non sarà mai accessibile.

Prima di intraprendere questo, vorrei essere chiaro che l'utilizzo di un indice parziale offre due vantaggi:

  1. Riduciamo le dimensioni dell'indice nella cache, liberando più spazio per le righe stesse nella cache.
  2. Riduciamo le dimensioni dell'albero B, ottenendo una risposta più rapida alle query.

4
L'uso di un indice parziale non è utile solo quando si accede raramente a gran parte dei dati, ma anche quando alcuni valori sono molto comuni. Quando un valore è molto comune, il pianificatore utilizzerà comunque una scansione della tabella anziché l'indice, quindi includere il valore nell'indice non ha alcun scopo.
Eelke,

Risposte:


19

Giocando un po 'con pg_buffercache , potrei ottenere risposte ad alcune delle tue domande.

  1. Questo è abbastanza ovvio, ma i risultati per (5) mostrano anche che la risposta è
  2. Devo ancora fare un buon esempio per questo, per ora è più sì che no :) (Vedi la mia modifica di seguito, la risposta è NO .)
  3. Poiché il planner è chi decide se utilizzare o meno un indice, possiamo dire , decide la memorizzazione nella cache (ma questo è più complicato)
  4. I dettagli esatti della memorizzazione nella cache potrebbero essere derivati ​​dal codice sorgente, non ho trovato troppo su questo argomento, tranne questo (vedi anche la risposta dell'autore ). Tuttavia, sono abbastanza sicuro che questo sia di nuovo molto più complicato di un semplice sì o no. (Ancora una volta, dalla mia modifica puoi avere qualche idea - poiché la dimensione della cache è limitata, quegli indici "sensibili" competono per lo spazio disponibile. Se sono troppi, si daranno un calcio a vicenda dalla cache - quindi la risposta è piuttosto NO . )
  5. Come una semplice query con pg_buffercachespettacoli, la risposta è definitivo . Vale la pena notare che i dati della tabella temporanea non vengono memorizzati nella cache qui.

MODIFICARE

Ho trovato il fantastico articolo di Jeremiah Peschka sulla memorizzazione di tabelle e indici. Con le informazioni da lì, ho potuto rispondere anche (2) . Ho impostato un piccolo test, in modo da poterli controllare da soli.

-- we will need two extensions
CREATE EXTENSION pg_buffercache;
CREATE EXTENSION pageinspect;


-- a very simple test table
CREATE TABLE index_cache_test (
      id serial
    , blah text
);


-- I am a bit megalomaniac here, but I will use this for other purposes as well
INSERT INTO index_cache_test
SELECT i, i::text || 'a'
FROM generate_series(1, 1000000) a(i);


-- let's create the index to be cached
CREATE INDEX idx_cache_test ON index_cache_test (id);


-- now we can have a look at what is cached
SELECT c.relname,count(*) AS buffers
FROM 
    pg_class c 
    INNER JOIN pg_buffercache b ON b.relfilenode = c.relfilenode 
    INNER JOIN pg_database d ON (b.reldatabase = d.oid AND d.datname = current_database())
GROUP BY c.relname
ORDER BY 2 DESC LIMIT 10;

             relname              | buffers
----------------------------------+---------
 index_cache_test                 |    2747
 pg_statistic_relid_att_inh_index |       4
 pg_operator_oprname_l_r_n_index  |       4
... (others are all pg_something, which are not interesting now)

-- this shows that the whole table is cached and our index is not in use yet

-- now we can check which row is where in our index
-- in the ctid column, the first number shows the page, so 
-- all rows starting with the same number are stored in the same page
SELECT * FROM bt_page_items('idx_cache_test', 1);

 itemoffset |  ctid   | itemlen | nulls | vars |          data
------------+---------+---------+-------+------+-------------------------
          1 | (1,164) |      16 | f     | f    | 6f 01 00 00 00 00 00 00
          2 | (0,1)   |      16 | f     | f    | 01 00 00 00 00 00 00 00
          3 | (0,2)   |      16 | f     | f    | 02 00 00 00 00 00 00 00
          4 | (0,3)   |      16 | f     | f    | 03 00 00 00 00 00 00 00
          5 | (0,4)   |      16 | f     | f    | 04 00 00 00 00 00 00 00
          6 | (0,5)   |      16 | f     | f    | 05 00 00 00 00 00 00 00
...
         64 | (0,63)  |      16 | f     | f    | 3f 00 00 00 00 00 00 00
         65 | (0,64)  |      16 | f     | f    | 40 00 00 00 00 00 00 00

-- with the information obtained, we can write a query which is supposed to
-- touch only a single page of the index
EXPLAIN (ANALYZE, BUFFERS) 
    SELECT id 
    FROM index_cache_test 
    WHERE id BETWEEN 10 AND 20 ORDER BY id
;

 Index Scan using idx_test_cache on index_cache_test  (cost=0.00..8.54 rows=9 width=4) (actual time=0.031..0.042 rows=11 loops=1)
   Index Cond: ((id >= 10) AND (id <= 20))
   Buffers: shared hit=4
 Total runtime: 0.094 ms
(4 rows)

-- let's have a look at the cache again (the query remains the same as above)
             relname              | buffers
----------------------------------+---------
 index_cache_test                 |    2747
 idx_test_cache                   |       4
...

-- and compare it to a bigger index scan:
EXPLAIN (ANALYZE, BUFFERS) 
SELECT id 
    FROM index_cache_test 
    WHERE id <= 20000 ORDER BY id
;


 Index Scan using idx_test_cache on index_cache_test  (cost=0.00..666.43 rows=19490 width=4) (actual time=0.072..19.921 rows=20000 loops=1)
   Index Cond: (id <= 20000)
   Buffers: shared hit=4 read=162
 Total runtime: 24.967 ms
(4 rows)

-- this already shows that something was in the cache and further pages were read from disk
-- but to be sure, a final glance at cache contents:

             relname              | buffers
----------------------------------+---------
 index_cache_test                 |    2691
 idx_test_cache                   |      58

-- note that some of the table pages are disappeared
-- but, more importantly, a bigger part of our index is now cached

Tutto sommato, questo dimostra che gli indici e le tabelle possono essere memorizzati nella cache pagina per pagina, quindi la risposta per (2) è NO .

E un ultimo per illustrare le tabelle temporanee che non sono memorizzate nella cache qui:

CREATE TEMPORARY TABLE tmp_cache_test AS 
SELECT * FROM index_cache_test ORDER BY id FETCH FIRST 20000 ROWS ONLY;

EXPLAIN (ANALYZE, BUFFERS) SELECT id FROM tmp_cache_test ORDER BY id;

-- checking the buffer cache now shows no sign of the temp table

1
+1 Risposta molto bella. Ha senso che le tabelle temporanee che vivono nella RAM non siano memorizzate nella cache. Mi chiedo, tuttavia, se la memorizzazione nella cache si verifica non appena le tabelle temporanee si riversano sul disco (per mancanza di sufficiente temp_buffers) - per l'intera tabella o solo per la parte sul disco. Mi aspetterei quest'ultimo. Potrebbe essere un test interessante ..
Erwin Brandstetter,

9

Le pagine dell'indice vengono recuperate quando una query decide che saranno utili per ridurre la quantità di dati della tabella necessari per rispondere a una query. Solo i blocchi dell'indice hanno navigato per ottenere ciò che vengono letti. Sì, vanno nello stesso pool shared_buffers in cui sono archiviati i dati della tabella. Entrambi sono inoltre supportati dalla cache del sistema operativo come secondo livello di memorizzazione nella cache.

Puoi facilmente avere lo 0,1% di un indice in memoria o il 100% di esso. L'idea che la maggior parte degli indici "sensibili" rimarranno nella cache per tutto il tempo "fallisce quando si hanno domande che toccano solo un sottoinsieme di una tabella. Un esempio comune è se si dispone di dati orientati al tempo. Spesso quelli navigano comunemente verso la fine recente della tabella, raramente visitando la vecchia storia. Lì potresti trovare tutti i blocchi di indice necessari per navigare in memoria e verso l'estremità recente in memoria, mentre sono pochi quelli necessari per navigare nei record precedenti.

Le parti complicate dell'implementazione non sono il modo in cui i blocchi entrano nella cache del buffer. Sono le regole su quando se ne vanno. Il mio discorso Inside the PostgreSQL Buffer Cache e le query di esempio incluse possono aiutarti a capire cosa sta succedendo lì e vedere cosa sta realmente accumulando su un server di produzione. Può essere sorprendente. C'è molto di più su tutti questi argomenti anche nel mio libro PostgreSQL 9.0 ad alte prestazioni .

Gli indici parziali possono essere utili perché riducono le dimensioni dell'indice e quindi sono entrambi più veloci da navigare e lasciano più RAM per la memorizzazione nella cache di altre cose. Se la tua navigazione nell'indice è tale che le parti che tocchi sono sempre nella RAM, comunque, ciò potrebbe non comportare un reale miglioramento.

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.