Errore PostgreSQL: out of shared memory
ERROR: out of shared memory
HINT: You might need to increase max_locks_per_transaction.
Nonostante la formulazione allarmante, quasi mai riguarda il server che esaurisce la memoria in generale. Quando arriva con l'hint max_locks_per_transaction, significa una cosa specifica: la tabella dei lock condivisa — la struttura a dimensione fissa dove PostgreSQL traccia i lock su tabelle, indici e altri oggetti — è piena.
Cosa significa questo errore
La tabella dei lock è dimensionata all'avvio del server, con spazio per circa max_locks_per_transaction × (max_connections + max_prepared_transactions) lock su oggetti. Due proprietà spiegano tutto ciò che osserverai:
- Nonostante il nome,
max_locks_per_transactionnon è un tetto per-transazione — dimensiona solo il pool condiviso. Una singola transazione può tranquillamente tenere migliaia di lock finché il pool ha spazio. - Il che significa anche che una singola transazione avida può prosciugare il pool per tutti: l'errore può colpire un passante innocente, non la transazione che ha consumato i lock.
Ogni tabella, partizione e indice toccato da una query ha bisogno del proprio lock entry, tenuto fino alla fine della transazione. È questa l'aritmetica che ti frega: una query su una tabella con 2.000 partizioni e qualche indice ciascuna può richiedere molte migliaia di entry — mentre il dimensionamento di default (64 × ~100 connessioni) te ne dà circa 6.400 per l'intero server.
Cause comuni
- Tabelle partizionate con molte partizioni: una query che non riesce a fare pruning tocca ogni partizione e ognuno dei suoi indici. Di gran lunga il trigger più comune.
pg_dump/pg_restoresu database con un numero molto grande di oggetti (blocca ogni tabella che dumpa in una sola transazione).- Migrazioni che alterano o eliminano molte tabelle in una singola transazione.
- Molte transazioni concorrenti che toccano ciascuna un numero moderato di relation — il pool è condiviso, quindi conta la somma.
Come diagnosticarlo
Mentre il carico problematico gira (o subito prima che fallisca):
-- Quanto è pieno il lock table:
SELECT count(*) FROM pg_locks;
-- Chi sta consumando le entry:
SELECT pid, count(*)
FROM pg_locks
GROUP BY pid
ORDER BY count(*) DESC
LIMIT 10;
Confronta il totale con la formula qui sopra usando le tue impostazioni. Se un solo pid tiene migliaia di lock, guarda cosa sta eseguendo — e se sono coinvolte partizioni, contale: SELECT count(*) FROM pg_inherits WHERE inhparent = 'events'::regclass;
Come risolverlo
- Alza
max_locks_per_transaction— es. da 64 a 256 o 512 su server con schemi pesantemente partizionati. Richiede un riavvio; la memoria condivisa extra è modesta.ALTER SYSTEM SET max_locks_per_transaction = 256; -- poi riavvio del server - Aiuta il partition pruning: assicurati che le query filtrino sulla chiave di partizionamento con valori che il planner può vedere, così vengono bloccate solo le partizioni rilevanti.
- Suddividi le operazioni ampie: spezza migrazioni e script di manutenzione in più transazioni invece di una gigante.
- Riconsidera il numero di partizioni: schemi con molte migliaia di partizioni minuscole pagano questa tassa ovunque, non solo qui.
