L'aumento di work_mem e shared_buffers su Postgres 9.2 rallenta significativamente le query


39

Ho un'istanza PostgreSQL 9.2 in esecuzione su RHEL 6.3, macchina a 8 core con 16 GB di RAM. Il server è dedicato a questo database. Dato che il postgresql.conf predefinito è piuttosto prudente per quanto riguarda le impostazioni di memoria, ho pensato che sarebbe una buona idea consentire a Postgres di usare più memoria. Con mia sorpresa, seguire i consigli su wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server ha notevolmente rallentato praticamente ogni query che eseguo, ma è ovviamente più evidente nelle query più complesse.

Ho anche provato a eseguire pgtune che ha dato la seguente raccomandazione con più parametri sintonizzati, ma ciò non ha cambiato nulla. Suggerisce shared_buffers di 1/4 della dimensione della RAM che sembra in linea con i consigli altrove (e in particolare su PG wiki).

default_statistics_target = 50
maintenance_work_mem = 960MB
constraint_exclusion = on
checkpoint_completion_target = 0.9
effective_cache_size = 11GB
work_mem = 96MB
wal_buffers = 8MB
checkpoint_segments = 16
shared_buffers = 3840MB
max_connections = 80

Ho provato a reindicizzare l'intero database dopo aver modificato le impostazioni (usando reindex database), ma neanche questo mi ha aiutato. Ho giocato con shared_buffers e work_mem. Modificandoli gradualmente dai valori predefiniti molto conservativi (128 k / 1 MB), le prestazioni diminuivano gradualmente.

Ho fatto EXPLAIN (ANALYZE,BUFFERS)alcune domande e il colpevole sembra essere che Hash Join sia significativamente più lento. Non mi è chiaro il perché.

Per fare un esempio specifico, ho la seguente domanda. Funziona in ~ 2100ms sulla configurazione predefinita e ~ 3300ms sulla configurazione con dimensioni del buffer aumentate:

select count(*) from contest c
left outer join contestparticipant cp on c.id=cp.contestId
left outer join teammember tm on tm.contestparticipantid=cp.id
left outer join staffmember sm on cp.id=sm.contestparticipantid
left outer join person p on p.id=cp.personid
left outer join personinfo pi on pi.id=cp.personinfoid
where pi.lastname like '%b%' or pi.firstname like '%a%';

EXPLAIN (ANALYZE,BUFFERS) per la query sopra:

La domanda è: perché osservo una riduzione delle prestazioni quando aumento le dimensioni del buffer? La macchina sicuramente non sta esaurendo la memoria. Allocazione se la memoria condivisa nel sistema operativo è ( shmmaxe shmall) impostata su valori molto grandi, ciò non dovrebbe costituire un problema. Non sto riscontrando errori nemmeno nel registro di Postgres. Sto eseguendo autovacuum nella configurazione predefinita ma non mi aspetto che abbia nulla a che fare con esso. Tutte le query sono state eseguite sulla stessa macchina a pochi secondi di distanza, solo con una configurazione modificata (e riavviato PG).

Modifica: ho appena trovato un fatto particolarmente interessante: quando eseguo lo stesso test sul mio iMac di metà 2010 (OSX 10.7.5) anche con Postgres 9.2.1 e 16 GB di RAM, non avverto il rallentamento. In particolare:

set work_mem='1MB';
select ...; // running time is ~1800 ms
set work_mem='96MB';
select ...' // running time is ~1500 ms

Quando eseguo esattamente la stessa query (quella sopra) con esattamente gli stessi dati sul server, ottengo 2100 ms con work_mem = 1 MB e 3200 ms con 96 MB.

Il Mac ha SSD quindi è comprensibilmente più veloce, ma mostra un comportamento che mi aspetterei.

Vedi anche la discussione di follow-up su pgsql-performance .


1
Sembra che nel caso più lento ogni passo sia costantemente più lento. Le altre impostazioni sono rimaste le stesse?
dezso,

1
Probabilmente vale la pena chiederlo su un forum più specializzato piuttosto che su quello generico. In questo caso suggerisco la mailing list pgsql-general archives.postgresql.org/pgsql-general
Colin 't Hart

1
Oh, e rispondi e rispondi alla tua domanda se trovi la risposta! (Questo è permesso, anche incoraggiato).
Colin 't Hart,

1
Mi chiedo quanto Postgres sia simile a Oracle in questo senso: ricordo un corso di Jonathan Lewis (guru Oracle) in cui ha dimostrato che l'allocazione di più memoria a volte le ha rese più lente. Dimentico i dettagli, ma è stato qualcosa a che fare con Oracle che fa una sorta di parziale, quindi li scrive nella memoria temporanea e poi li combina in seguito. In qualche modo più memoria ha rallentato questo processo.
Colin 't Hart,

2
La domanda è ora pubblicata su pgsql-performance: archives.postgresql.org/pgsql-performance/2012-11/msg00004.php
Petr Praus

Risposte:


28

Prima di tutto, tieni presente che work_mem è per operazione e quindi può diventare eccessivo abbastanza rapidamente. In generale, se non si verificano problemi con l'ordinamento lento, lascerei da solo work_mem finché non ne avrai bisogno.

Guardando i tuoi piani di query, una cosa che mi colpisce è che gli hit del buffer sono molto diversi rispetto ai due piani e che anche le scansioni sequenziali sono più lente. Ho il sospetto che il problema abbia a che fare con la cache read-ahead e con meno spazio per questo. Ciò significa che stai influenzando la memoria per il riutilizzo degli indici e contro la lettura di tabelle sul disco.


La mia comprensione è che PostgreSQL cercherà una pagina nella cache prima di leggerla dal disco perché non sa davvero se la cache del sistema operativo conterrà quella pagina. Poiché le pagine rimangono nella cache e poiché tale cache è più lenta della cache del sistema operativo, ciò modifica il tipo di query che sono veloci rispetto a quelle che sono lente. In effetti, leggendo i piani, a parte i problemi di work_mem, sembra che tutte le informazioni sulla query provengano dalla cache, ma si tratta di quale cache.

work_mem : quanta memoria possiamo allocare per un ordinamento o un'operazione di join relativa. Questo è per operazione, non per istruzione o per back-end, quindi una singola query complessa può utilizzare molte volte questa quantità di memoria. Non è chiaro che stai raggiungendo questo limite, ma vale la pena notare ed essere consapevoli. se si aumenta troppo, si perde memoria che potrebbe essere disponibile per la cache di lettura e i buffer condivisi.

shared_buffers : quanta memoria assegnare alla coda della pagina PostgreSQL effettiva. Ora, idealmente, l'interessante set del database rimarrà nella memoria memorizzata nella cache qui e nei buffer di lettura. Tuttavia, ciò che fa è garantire che le informazioni utilizzate più di frequente in tutti i backend vengano memorizzate nella cache e non scaricate sul disco. Su Linux questa cache è significativamente più lenta della cache del disco del sistema operativo, ma offre garanzie che la cache del disco del sistema operativo non lo faccia e sia trasparente per PostgreSQL. Questo è abbastanza chiaramente dove si trova il tuo problema.

Quindi quello che succede è che quando abbiamo una richiesta, controlliamo prima i buffer condivisi poiché PostgreSQL ha una profonda conoscenza di questa cache e cerca le pagine. Se non ci sono, chiediamo al sistema operativo di aprirli dal file e se il sistema operativo ha memorizzato nella cache il risultato restituisce la copia memorizzata nella cache (è più veloce dei buffer condivisi, ma Pg non può dire se è memorizzato nella cache o acceso disco e il disco è molto più lento, quindi PostgreSQL in genere non avrà questa possibilità). Tieni presente che ciò influisce anche sull'accesso casuale o sequenziale alle pagine. Quindi potresti ottenere prestazioni migliori con impostazioni condivise inferiori.

Il mio intuito è che probabilmente otterrai prestazioni migliori, o almeno più coerenti, in ambienti ad alta concorrenza con impostazioni share_buffer più grandi. Inoltre, tieni presente che PostgreSQL prende questa memoria e la tiene così se hai altre cose in esecuzione sul sistema, i buffer di lettura manterranno i file letti da altri processi. È un argomento molto ampio e complesso. Le impostazioni di buffer condiviso più grandi offrono migliori garanzie di prestazioni ma in alcuni casi possono offrire prestazioni inferiori.


10

A parte l'effetto apparentemente paradossale che l'aumento delle work_memprestazioni diminuisce ( @Chris potrebbe avere una spiegazione), puoi migliorare la tua funzione in almeno due modi.

  • Riscrivi due falsi LEFT JOINcon JOIN. Ciò potrebbe confondere il pianificatore di query e portare a piani inferiori.

SELECT count(*) AS ct
FROM   contest            c
JOIN   contestparticipant cp ON cp.contestId = c.id
JOIN   personinfo         pi ON pi.id = cp.personinfoid
LEFT   JOIN teammember    tm ON tm.contestparticipantid = cp.id
LEFT   JOIN staffmember   sm ON sm.contestparticipantid = cp.id
LEFT   JOIN person        p  ON p.id = cp.personid
WHERE (pi.firstname LIKE '%a%'
OR     pi.lastname  LIKE '%b%')
  • Supponendo che i modelli di ricerca effettivi siano più selettivi, utilizzare gli indici trigram su pi.firstnamee pi.lastnameper supportare LIKEricerche non ancorate . ( '%a%'Sono supportati anche modelli più brevi come , ma è probabile che un indice non aiuti per i predicati non selettivi.):

CREATE INDEX personinfo_firstname_gin_idx ON personinfo USING gin (firstname gin_trgm_ops);
CREATE INDEX personinfo_lastname_gin_idx  ON personinfo USING gin (lastname gin_trgm_ops);

O un indice a più colonne:

CREATE INDEX personinfo_name_gin_idx ON personinfo USING gin (firstname gin_trgm_ops, lastname gin_trgm_ops);

Dovrebbe rendere la tua query un po 'più veloce. Per questo è necessario installare il modulo aggiuntivo pg_trgm . Dettagli sotto queste domande correlate:


Inoltre, hai provato a impostare work_mem localmente - solo per la transazione corrente ?

SET LOCAL work_mem = '96MB';

Ciò impedisce alle transazioni simultanee di consumare anche più RAM, probabilmente affamandosi.


3
Voglio dare un secondo suggerimento sul work_mem locale di Erwin. Poiché work_mem modifica il tipo di query più veloci, potrebbe essere necessario modificarlo per alcune query. I livelli di work_mem bassi sono i migliori per le query che ordinano / uniscono piccoli numeri di record in modi complessi (ad es. Molti join) mentre i livelli di work_mem elevati sono i migliori per le query che hanno alcuni tipi ma che ordinano o uniscono un gran numero di righe contemporaneamente .
Chris Travers,

Nel frattempo ho migliorato la query (la domanda è dell'ottobre dello scorso anno) ma grazie :) Questa domanda riguarda più l'effetto imprevisto che la query specifica. La query serve principalmente per dimostrare l'effetto. Grazie per il suggerimento sull'indice, ci proverò!
Petr Praus,
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.