mercoledì 22 settembre 2010

Divagazioni sul table partitioning in PostgreSQL - parte 2

Una prima soluzione per nulla insoddisfacente l'ho trovata definendo i nomi delle tabelle in modo opportuno. "Codificando" cioè nei loro nomi la condizione che fa sì che una riga vi appartenga. Prima che iniziate a storcere il naso tengo a precisare che neanche a me piace molto questa cosa, ma bisogna ammettere che è funzionale e ragionevolmente efficiente.
In questo modo il trigger che intrappola, ad esempio, le INSERT può:
  1. "calcolare" il nome della tabella figlia candidata all'operazione
  2. verificare se questa esiste
  3. crearla in caso negativo
  4. realizzare l'operazione di INSERT vera e propria
In questo modo la "tabella di supporto" di cui ho parlato non sarebbe altro che il catalogo di sistema. Non serve alcuna tabella extra e relative operazioni di DML. Le CREATE TABLE fanno già tutto.
Quanto poi ad inserire informazioni nel nome di una tabella, a guardare bene, corrisponde ad inserire informazioni in una colonna di una tabella del catalogo. Il che non è poi così inelegante: La condizione da testare corrisponde ad una parte del testo (il nome della tabella) da ricercare nel catalogo.
Semplice come bere un bicchiere d'acqua ... stando a testa ingiù.
Ad esempio, le partizioni della tabella

CREATE TABLE movimenti_magazzino (
  maga int8 not null,
  prod int8 not null,
  qnta numeric not null,
  data timestamp not null
);

suddivisa in base al magazzino e alle settimane di movimentazione potrebbero chiamarsi:
"moma maga=42,week=19,year=2010","moma maga=42,week=20,year=2010","moma maga=42,week=21,year=2010" ...

usando il quoting dei nomi come da manuale.
Questa soluzione, inoltre, è chiaramente migliore di quest'altra:
  1. "calcolare" il nome della tabella figlia candidata all'operazione
  2. provare a realizzare la INSERT
  3. intrappolare l'eventuale errore con BEGIN...EXCEPTION...WHEN per creare la tabella figlia e rieseguire la INSERT
I  questo caso, come noto, il costrutto di intrappolamento degli errori nasconde una penalità di performance che impatterebbe su ogni singola INSERT. Questa penalità è certamente superiore a quella di un "IF NOT FOUND THEN". Si tratta più o meno, dello stesso problema che si trova, ad esempio, in C++ con le eccezioni e i blocchi "try { ... }".

Abbiamo poi trovato un'altra possibilità ancora, che si discosta un po' dallo schema ufficiale e che fa completamente a meno dei TRIGGER. Ecco la sostanza della tecnica.
Tutte le operazioni avvengono direttamente (e solamente) sulla tabella madre, definita con tutti gli indici e le constraint del caso e senza alcun TRIGGER.
Quando, e se possibile, questa viene scorsa da una procedura che realizza lo smistamento delle righe nelle relative tabelle figlie, creandole "on demand". Questa procedura può essere scritta in modo da limitare il numero di linee da smistare per volta. In questo modo la si può eseguire in modo "schedulato" e incrementale, al limite anche durante le normale operazioni sulla tabella madre senza creare LOCK troppo estesi.
In questo modo le penalizzazioni delle normali operazioni sono nulle ed il costo di "smistamento" può essere frazionato e diluito nel tempo o, se l'applicazione lo consente, effettuato tutto in una volta nella fase di manutenzione del DB, poco prima cioè di eseguire la VACUUM [ANALYZE].

L'ultima, almeno per il momento, soluzione "alternativa" diverge in modo totale da tutte quelle vista in precedenza. Nessun trigger, come per la soluzione di prima, e nemmeno ereditarietà.
Alla prossima.

Nessun commento:

Posta un commento