Jag har en partitionerad tabell för viss applikationsloggning. För några år sedan partitionerade jag bordet med en partition per månad. När vi närmar oss 2016 är det dags för mig att lägga till partitioner för det nya året. Den partitionerade tabellen har, som sina två sista partitioner, partitionen för december 2015 och en partition som använder MAXVALUE. Jag planerar aldrig att ha några data i MAXVALUE-partitionen. Det är bara till för att göra SPLIT PARTITION-operationer enklare.
Tidigare skulle jag lägga till partitioner med kommandon som liknar följande:
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);
SQL-satserna ovan kommer att dela upp MAXVALUE-partitionen i två partitioner. Det finns 12 sådana kommandon, ett för varje månad.
I år, när jag försökte köra skriptet för 2016 i en icke-produktionsmiljö, blev jag förvånad över att finna att dessa kommandon tog cirka 30 minuter för varje att slutföra. Tidigare år klarade de på några sekunder. Kom ihåg att USAGE_TRACKING_PMAX är tom så ingen data behöver flyttas till en lämplig partition.
När jag analyserade aktiviteten i min session när jag utförde SPLIT, kunde jag tydligt se db-fil väntande händelser som spårades till denna partitionerade tabell. Det var uppenbart att SPLIT-operationen läste maxpartitionen, även om den var tom.
Tidigare år fungerade bra, men denna databas uppgraderades nyligen till Oracle 12c. Jag hittade information om hur man utför en snabb delad partitionsoperation i MOS Note 1268714.1 som säger att detta gäller Oracle 10.2.0.3 och högre, men jag hade inga problem i 11.2.0.4. Det var förmodligen bara en dum tur och jag har inte en 11g-databas att kolla upp detta på eftersom alla mina har uppgraderats. Som sådan, istället för att fokusera på det som har förändrats, kommer jag bara att ta itu med problemet och fortsätta med min dag.
Enligt MOS-anteckningen, för att utföra en snabb delad partition på den här tomma partitionen, måste jag se till att jag har statistik på den tomma partitionen.
Jag bekräftade att NUM_ROWS var 0 för denna tomma partition. Så jag behövde inte beräkna statistik på partitionen. Min första SPLIT PARTITION operation var väldigt snabb, bara några sekunder. Partitionen var tom och Oracle visste det. Det som förvånade mig var att den nya partitionen, USAGE_TRACKING_P201601 och USAGE_TRACKING_PMAX gick till NULL-värden för statistik. Detta innebar att det skulle ta lång tid att utföra SPLIT PARTITION-operationen för den andra nya partitionen. Här är ett exempel på vad jag menar. Först kan vi se 0 rader i maxvärdespartitionen.
SQL> select num_rows from dba_tab_partitions
2 where partition_name='USAGE_TRACKING_PMAX';
NUM_ROWS
----------
0
Nu ska jag dela upp den partitionen.
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
Lägg märke till att de två sista partitionerna nu inte har någon statistik.
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
----------
Utan statistik tar nästa delade partition för att skapa februari 2016-partitionen lång tid.
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
Som MOS-anteckningen säger behöver vi statistiken på partitionen för att utföra en snabb delad operation. Lösningen är att beräkna statistik på partitionen och sedan använda ett ALTER TABLE-kommando för att skapa alla partitioner på en gång.
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);
Om jag skulle ha lämnat skriptet till att utföra 12 individuella SPLIT PARTITION-operationer, så skulle jag ha behövt räkna om statistiken för maxpartitionen mellan var och en. Att använda ett kommando var mer effektivt.