Ho una tabella partizionata per la registrazione di alcune applicazioni. Alcuni anni fa, ho partizionato la tabella con una partizione al mese. Mentre ci avviciniamo al 2016, è tempo per me di aggiungere partizioni per il nuovo anno. La tabella partizionata ha, come ultime due partizioni, la partizione di dicembre 2015 e una partizione che utilizza MAXVALUE. Non ho mai intenzione di avere dati nella partizione MAXVALUE. È lì solo per semplificare le operazioni di SPLIT PARTITION.
In passato aggiungevo partizioni con comandi simili ai seguenti:
ALTER TABLE usage_tracking
SPLIT PARTITION usage_tracking_pmax AT (TO_DATE('02/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS))
INTO (PARTITION usage_tracking_p201601, PARTITION usage_tracking_pmax);
ALTER TABLE usage_tracking
SPLIT PARTITION usage_tracking_pmax AT (TO_DATE('03/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS'))
INTO (PARTITION usage_tracking_p201602, PARTITION usage_tracking_pmax);
Le istruzioni SQL precedenti divideranno la partizione MAXVALUE in due partizioni. Esistono 12 comandi di questo tipo, uno per ogni mese.
Quest'anno, quando ho provato a eseguire lo script per il 2016 in un ambiente non di produzione, sono rimasto sorpreso di scoprire che questi comandi hanno impiegato circa 30 minuti per il completamento di ciascuno. Negli anni precedenti, hanno completato in pochi secondi. Ricorda che USAGE_TRACKING_PMAX è vuoto, quindi non è necessario spostare i dati in una partizione appropriata.
Analizzando l'attività della mia sessione eseguendo lo SPLIT, ho potuto vedere chiaramente gli eventi di attesa del file db che sono stati tracciati in questa tabella partizionata. Era ovvio che l'operazione SPLIT stava leggendo la partizione massima, anche se era vuota.
Gli anni precedenti hanno funzionato bene, ma questo database è stato recentemente aggiornato a Oracle 12c. Ho trovato informazioni su come eseguire un'operazione di partizione divisa veloce in MOS Note 1268714.1 che dice che questo si applica a Oracle 10.2.0.3 e versioni successive, ma non ho riscontrato problemi in 11.2.0.4. Probabilmente è stata solo una stupida fortuna e non ho un database 11g su cui verificarlo poiché tutti i miei sono stati aggiornati. In quanto tale, invece di concentrarmi su ciò che è cambiato, affronterò semplicemente il problema e andrò avanti con la mia giornata.
Secondo la nota MOS, per eseguire una partizione divisa veloce su questa partizione vuota, devo assicurarmi di avere statistiche sulla partizione vuota.
Ho confermato che NUM_ROWS era 0 per questa partizione vuota. Quindi non ho dovuto calcolare le statistiche sulla partizione. La mia prima operazione di SPLIT PARTITION è stata molto veloce, solo pochi secondi. La partizione era vuota e Oracle lo sapeva. Ciò che mi ha sorpreso è stato che la nuova partizione, USAGE_TRACKING_P201601 e USAGE_TRACKING_PMAX è passata ai valori NULL per le statistiche. Ciò significava che l'esecuzione dell'operazione SPLIT PARTITION per la seconda nuova partizione richiedeva molto tempo. Ecco un esempio di cosa intendo. Innanzitutto, possiamo vedere 0 righe nella partizione del valore massimo.
SQL> select num_rows from dba_tab_partitions
2 where partition_name='USAGE_TRACKING_PMAX';
NUM_ROWS
----------
0
Ora dividerò quella partizione.
SQL> ALTER TABLE usage_tracking
2 SPLIT PARTITION usage_tracking_pmax AT ( TO_DATE('02/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS') )
3 INTO (PARTITION usage_tracking_p201601, PARTITION usage_tracking_pmax);
Table altered.
Elapsed: 00:00:03.13
Nota ora che le ultime due partizioni ora non hanno statistiche.
SQL> select num_rows from dba_tab_partitions
2 where partition_name='USAGE_TRACKING_PMAX';
NUM_ROWS
----------
SQL> select num_rows from dba_tab_partitions
2 where partition_name='USAGE_TRACKING_P201601';
NUM_ROWS
----------
Senza statistiche, la successiva partizione divisa per creare la partizione di febbraio 2016 richiede molto tempo.
SQL> ALTER TABLE nau_system.usage_tracking
2 SPLIT PARTITION usage_tracking_pmax AT (TO_DATE('03/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS'))
3 INTO (PARTITION usage_tracking_p201602, PARTITION usage_tracking_pmax);
Table altered.
Elapsed: 00:27:41.09
Come dice la nota MOS, abbiamo bisogno delle statistiche sulla partizione per eseguire un'operazione di divisione veloce. La soluzione è calcolare le statistiche sulla partizione, quindi utilizzare un comando ALTER TABLE per creare tutte le partizioni contemporaneamente.
BEGIN
DBMS_STATS.gather_table_stats (tabname=>'USAGE_TRACKING',
partname => 'USAGE_TRACKING_PMAX',
granularity => 'PARTITION');
END;
/
ALTER TABLE usage_tracking
SPLIT PARTITION usage_tracking_pmax INTO
(PARTITION usage_tracking_p201601 VALUES LESS THAN (TO_DATE('02/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS')),
PARTITION usage_tracking_p201602 VALUES LESS THAN (TO_DATE('03/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS')),
PARTITION usage_tracking_p201603 VALUES LESS THAN (TO_DATE('04/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS')),
PARTITION usage_tracking_p201604 VALUES LESS THAN (TO_DATE('05/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS')),
PARTITION usage_tracking_p201605 VALUES LESS THAN (TO_DATE('06/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS')),
PARTITION usage_tracking_p201606 VALUES LESS THAN (TO_DATE('07/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS')),
PARTITION usage_tracking_p201607 VALUES LESS THAN (TO_DATE('08/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS')),
PARTITION usage_tracking_p201608 VALUES LESS THAN (TO_DATE('09/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS')),
PARTITION usage_tracking_p201609 VALUES LESS THAN (TO_DATE('10/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS') ),
PARTITION usage_tracking_p201610 VALUES LESS THAN (TO_DATE('11/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS') ),
PARTITION usage_tracking_p201611 VALUES LESS THAN (TO_DATE('12/01/2016 00:00:00','MM/DD/YYYY HH24:MI:SS') ),
PARTITION usage_tracking_p201612 VALUES LESS THAN (TO_DATE('01/01/2017 00:00:00','MM/DD/YYYY HH24:MI:SS') ),
PARTITION usage_tracking_pmax);
Se avessi lasciato lo script per eseguire 12 singole operazioni SPLIT PARTITION, avrei dovuto ricalcolare le statistiche sulla partizione massima tra ciascuna. Usare un comando era più efficiente.