Имам разделена таблица за записване на някои приложения. Преди няколко години разделих таблицата с един дял на месец. Тъй като наближаваме 2016 г., е време да добавя дялове за новата година. Разделената таблица има като последните си два дяла, дяла за декември 2015 г. и дял, използващ MAXVALUE. Никога не планирам да имам никакви данни в дяла MAXVALUE. Той е там само за улесняване на операциите на SPLIT PARTITION.
В миналото бих добавял дялове с команди, подобни на следните:
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 изразите по-горе ще разделят дяла MAXVALUE на два дяла. Има 12 такива команди, по една за всеки месец.
Тази година, когато се опитах да стартирам скрипта за 2016 г. в непроизводствена среда, бях изненадан да открия, че тези команди отнемат около 30 минути, за да бъдат изпълнени. В предишни години те завършваха за секунди. Не забравяйте, че USAGE_TRACKING_PMAX е празен, така че не е необходимо да се преместват данни в подходящ дял.
При анализиране на активността на моята сесия, изпълняваща SPLIT, можех ясно да видя събитията на изчакване на db файл, които бяха проследени към тази разделена таблица. Беше очевидно, че операцията SPLIT чете максималния дял, въпреки че беше празен.
Предишните години работеха добре, но тази база данни наскоро беше надстроена до Oracle 12c. Намерих информация как да изпълня операция за бързо разделяне в MOS Note 1268714.1, в която се казва, че това се отнася за Oracle 10.2.0.3 и по-нова версия, но нямах проблеми в 11.2.0.4. Вероятно беше просто глупав късмет и нямам база данни от 11g, за да проверя това, тъй като всичките ми бяха надстроени. Като такъв, вместо да се съсредоточавам върху това, което се промени, просто ще се справя с проблема и ще продължа деня си.
Според бележката за MOS, за да извърша бързо разделяне на този празен дял, трябва да се уверя, че имам статистика за празния дял.
Потвърдих, че NUM_ROWS е 0 за този празен дял. Така че не трябваше да изчислявам статистика за дяла. Първата ми операция SPLIT PARTITION беше много бърза, само за няколко секунди. Разделът беше празен и Oracle го знаеше. Това, което ме изненада, беше, че новият дял, USAGE_TRACKING_P201601 и USAGE_TRACKING_PMAX отиде до NULL стойности за статистика. Това означаваше, че извършването на операцията SPLIT PARTITION за втория нов дял ще отнеме много време. Ето един пример за това, което имам предвид. Първо, можем да видим 0 реда в дяла с максимална стойност.
SQL> select num_rows from dba_tab_partitions
2 where partition_name='USAGE_TRACKING_PMAX';
NUM_ROWS
----------
0
Сега ще разделя този дял.
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
Забележете сега, че последните два дяла вече нямат статистика.
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
----------
Без статистически данни следващият разделен дял за създаване на дяла от февруари 2016 г. отнема много време.
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
Както се казва в бележката за MOS, имаме нужда от статистиката на дяла, за да извършим бърза операция за разделяне. Решението е да изчислите статистиката за дяла и след това да използвате една команда ALTER TABLE, за да създадете всички дялове наведнъж.
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);
Ако бих оставил скрипта да изпълнява 12 отделни операции SPLIT PARTITION, тогава щеше да ми се наложи да преизчисля статистиката за максималния дял между всеки от тях. Използването на една команда беше по-ефективно.