PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Quei maledetti oggetti grandi

Introduzione

PostgreSQL offre agli sviluppatori la possibilità di scegliere tra due possibili strutture di archiviazione per dati binari di grandi dimensioni:Bytea e LargeObjects.

Gli oggetti di grandi dimensioni sono in circolazione da molto tempo e PostgreSQL ha un modo intelligente per archiviare dati binari di grandi dimensioni. Lo fa suddividendolo in blocchi di LOBLKSIZE (un quarto di BLCKSZ). In questo modo le tuple di pg_largeobject non si riversano sul tavolo dei toast.

D'altra parte bytea memorizza i dati binari direttamente nella tupla, il che potrebbe portare a scarse prestazioni a seconda dell'aspetto dello schema.

Sembra fantastico se hai un'interfaccia intelligente per gestire la manipolazione di questi file binari, specialmente se l'aggiornamento modifica solo una piccola parte dell'intero file binario.

Ma normalmente non ci preoccupiamo di scrivere codice che ne tragga vantaggio, e invece riscriviamo di nuovo l'intero dato binario.

Una delle cose che credo induca le persone ad adottare oggetti di grandi dimensioni sono le funzioni disponibili per importare ed esportare file direttamente dal server di database al suo filesystem. C'è un problema:se l'applicazione si trova su un server diverso, avrai bisogno di più codice per spostare il file nella posizione in cui è necessario.

Un problema che potresti incontrare

Nei giorni scorsi ho dovuto esaminare un database utilizzato per memorizzare informazioni sulle sessioni utente da un sistema Java CAS. Ho scoperto che nel database c'erano quasi 100 milioni di oggetti di grandi dimensioni, non molto grandi.

Ho esaminato le tabelle utente controllando i campi che avevano un oid campo, quindi ho incrociato i valori in quei campi con pg_largeobject_metadata tavolo. Ho scoperto che il 96% di quegli oggetti grandi erano orfani. Questi sono oggetti di grandi dimensioni a cui non è stato fatto riferimento da alcuna tupla dalle tabelle utente.

Ulteriori indagini hanno concluso che Hibernate non si è occupato di eliminare i largeobjects creati durante l'eliminazione o l'aggiornamento di tuple con campi oid. Quindi stava generando una grande quantità di rigonfiamento che non poteva essere ripulito passando l'aspirapolvere, ma doveva essere eliminato manualmente dalla tabella pg_largeobjects.

Nel caso particolare del database CAS, questa query è servita per identificare i largeobjects ancora in uso:

SELECT unnest(array[expiration_policy,
                    authentication,
                    services_granted_access_to])
       FROM public.ticketgrantingticket
UNION
SELECT unnest(array[expiration_policy, 
                    service])
       FROM public.serviceticket

La query può essere utilizzata per escludere dall'elenco gli oggetti di grandi dimensioni quali rimuovere. Qualcosa del genere:

SELECT lo_unlink(pg_largeobject_metadata.oid)
       FROM pg_largeobject_metadata
       WHERE pg_largeobject_metadata.oid NOT IN (
             SELECT unnest(array[expiration_policy,
                                 authentication,
                                 services_granted_access_to])
             FROM public.ticketgrantingticket
             UNION
             SELECT unnest(array[expiration_policy, 
                                 service])
             FROM public.serviceticket
)

Conclusione

Gli oggetti di grandi dimensioni hanno i loro problemi, proprio come altri tipi di dati (soprattutto quando si utilizzano i tipi per archiviare dati binari di grandi dimensioni). Spetta agli sviluppatori e agli amministratori di database sfruttare i vantaggi e mitigare i contro.

Abbiamo fornito una possibile query per eseguire la pulizia, ma c'è anche una bella estensione che ripulisce gli oggetti di grandi dimensioni orfani con i trigger:Large Object Manager

Alcune persone potrebbero preferire eseguire una query di eliminazione durante le ore di silenzio invece di eseguire un trigger ad ogni UPDATE e ELIMINA . Su sistemi con UPDATE molto, molto basso e/o ELIMINA rate, un trigger su ogni tabella che ha un oid campo, sembra una soluzione più elegante. E qualsiasi perdita di prestazioni per dover eseguire la funzione di trigger sarebbe superflua.

In ogni caso, gli oggetti di grandi dimensioni hanno ancora grandi fan, molto probabilmente a causa delle funzioni interne fornite per importare ed esportare i dati binari direttamente nel filesystem locale. Con bytea normalmente utilizzeresti più memoria a livello di applicazione. È una procedura molto comune leggere il campo binario completamente in una variabile e quindi elaborarlo.

Potrei scrivere qualcosa sull'utilizzo di bytea che ho usato in uno dei miei sviluppi passati in un futuro post sul blog.