PL-SQL производителност
(PL/SQL Performance, от Arup Nanda, от Arup Nanda)
Аруп Нанда Oracle ACE Директор
Разгледайте случаите, при които вграждането на код, правилно приложената собствена компилация и използването на прости цели числа могат да подобрят производителността на кода
Oracle Database 11g въвежда много страхотни нови функции за подобряване на производителността на PL/SQL кода, най-забележителните от които са нативната компилация и вграждането в модула.
Нативната компилация (вградена компилация) не е напълно нова функция, но сега няма пречки при използването й, например инсталирането на C компилатор е наистина ново. (Oracle нарече тази страхотна функция „Истинска собствена компилация“). В допълнение, новият тип данни simple_integer прави кода по-добър, когато се компилира в линия. Intra-unit inlining е техника за оптимизация, приложена към PL/SQL код по време на компилиране, за да се създаде ефективен код.
Тази статия ще разгледа някои случаи на използване на тези нови функции. Тяхната производителност също ще бъде тествана при различни сценарии: когато се използва нативна компилация, когато се използват числа от тип просто цяло число, когато се използва заместване (вграждане) и техните различни комбинации.
Истинска нативна компилация
Спомнете си вградената компилация в Oracle9i Database Release 2; кара PL/SQL програмите да работят много по-бързо от интерпретираните форми. От друга страна, приемането беше бавно, тъй като много системни администратори се противопоставиха на инсталирането на необходимия C компилатор.на производствения сървър на база данни. В допълнение, такива компилатори изискват настройка на параметъра plsql_native_library_dir с директория за междинни OS файлове.
В Oracle Database 11g можете да изпълнявате native-compile без C компилатор на сървъра и без да задавате опцията. Всичко, което трябва да направите, е да зададете параметъра на сесията, преди да създадете или прекомпилирате съхранения код:
Нативната (нативна) компилация отнема повече време от интерпретативната (интерпретирана), но тъй като този процес е много по-бърз в Oracle Database 11g, разликата може да не се забележи. Най-добре е да използвате интерпретативна компилация по време на нормалния цикъл на разработка и вградена компилация, когато разработката приключи.
Като част от моята миграция към 11g, експериментирах с реален код за жизненоважно приложение с много голяма партида от 5827 реда. Компилирах го първоначално на съществуваща база данни 10g и направих същото на 11g, след което повторих тези стъпки в режим на компилиране-интерпретиране. Всяка от тези компилации беше извършена с plsql_optimize_level, зададен на 2. Измерих времето за компилация за всеки случай, което е показано по-долу (в секунди).
10g
11g
Тълкувано
Родно
Резултатите говорят сами за себе си. В режим на интерпретация времето за компилация е почти същото. При вградена компилация обаче времето за компилиране в 11g е с около 60% по-бързо, отколкото в 10g, което е значително подобрение. Така че, въпреки че вграденото компилиране в 11g отнема допълнително време, то е много по-бързо от вграденото компилиране в 10g.
За да намерите обекти, компилирани с NATIVE, трябва да се обърнете към изгледа USER_PLSQL_OBJECT_SETTINGS:
Има и подобно представяне за всички обекти, DBA_PLSQL_OBJECT_SETTINGS.
Нов тип данни: Просто цяло число
Предимствата на вградената компилация са още по-очевидни при използване на новия тип данни, simple_integer. Всъщност това не е истински тип данни, а по-скоро подтип на типа данни pls_integer. Този подтип е предназначен да подобри машинното изчисление спрямо софтуерното изчисление. Когато се използва simple_integer едновременно с вградената компилация, производителността се подобрява значително. В експеримента, който ще бъде показан по-нататък, ще видите защо това е така.
Тъй като simple_integer е подтип на pls_integer, той наследява свойствата си като 32-битово цяло число със знак и може да бъде цяло число от отрицателни -2,147,483,648 до положителни 2,147,483,647 стойности. Въпреки това, той се различава от pls_integer по следните начини: този тип не е nullable, но overflowable, което означава, че когато стойността надвиши максимума, тя се нулира, но не възниква грешка.
Синтактично, този тип данни може да се използва във всички случаи, в които се използва pls_integer, но разликата трябва да се наблюдава внимателно; допълнителни свойства simple_integer може да са неподходящи в някои случаи.
Нека да разгледаме няколко възможни задачи, при които трябва да заменим pls_integer с simple_integer:
- Тази променлива не може да бъде нула, така че ако я напишете по различен начин, както е показано по-долу:
- но просто:
- тогава ще получите грешка при компилиране:
- Ако зададете стойността на променливата на NULL вътре в програмата, например, така:
- ще се получи грешка при компилиране:
- Избягвайте тези съобщения за грешка, които може да не са видими по отношение на точната същностгрешки. Ако програмата очаква дадена променлива да е нула, тогава променливата не трябва да се декларира като simple_integer.
- Друг важен момент при използването на simple_integer е, че той нулира стойностите, когато се достигнат максималните и минималните граници. Не забравяйте, че максималната положителна стойност на pls_integer е 2147483647. Какво се случва, ако се опитате да съхраните стойност, която е по-голяма? Вижте демонстрацията:
- Ще бъде получена грешка:
- Грешката е очевидна и съвсем разбираема; опитахте да надхвърлите максимално допустимата стойност за тип данни. Ако използвате simple_integer вместо pls_integer:
- Резултатът ще бъде следният:
- Имайте предвид, че стойността (-2147483648) е минималната стойност на simple_integer. Когато добавите 1 към максималната стойност (2147483647), стойността просто се нулира до минимума - това е характеристика на simple_integer. Пазете се от подобни ситуации.
Използване на Real Native компилация с прости цели числа
Както можете да видите, simple_integer не може да се използва никъде; трябва да се внимава да се вземат предвид допълнителните условия (особено относно възможното нулиране на стойностите), преди да се приложат. Следователно, simple_integer е създаден за вградена компилация. В режим на интерпретирана компилация може да няма ефект на подобряване на производителността (но няма и вреда, както ще се види по-късно). В естествен режим на компилация производителността на simple_integer е много по-значима.
Повечето PL/SQL бизнес приложения са твърдо кодирани с SQL. Следователно тези приложения няма да видят значителна промяна в производителността при вградено компилиране. В "минал живот" разработвах инструмент за планиране на капацитет на база данни, използвайки PL/SQL,включващи много числени и статистически изчисления в няколко хиляди реда код. Използването на вградена компилация води до значително подобрение на производителността. По това време simple_integers не бяха налични, но ако бяха, това би имало още по-голямо влияние върху производителността.
Вътрешна подплата
Вътрешното вграждане е заместването на извикване на подпрограма с копие на кода на подпрограмата. В резултат на това модифицираният код работи по-бързо. В Oracle Database 11g PL/SQL компилаторът е в състояние да идентифицира извиквания към подпрограма, която трябва да бъде копирана (с други думи, заменена от нея) и прави промени, които подобряват производителността.
Това е най-добре обяснено с пример. Кодът, показан по-долу, променя таблицата BALANCES чрез изчисляване на стойности въз основа на салдото по сметката. Кодът преминава през всички записи на таблицата, изчислява резултата и променя колоната на таблицата с баланса.
Всъщност изчислението на резултата е еднакво за всички типове публикации и аз поставих логиката в отделна процедура calc_int() в основната процедура. Това подобрява четливостта и поддръжката на кода, но за съжаление е неефективно.
Въпреки това, ако замените извикването на calc_int() с кода calc_int(), получавате по-бърза програма, както е показано по-долу:
Този преработен код се различава от оригинала само в частта от кода за изчисляване на баланса, която сега е вътре в цикъла, а не в процедурата calc_int ().
Имайте предвид, че по-новата версия може да е по-бърза, но не е много добра практика за кодиране. Частта от кода, която изчислява баланса, се изпълнява веднъж за всяка итерация на цикъла за месеци и след това за всеки номер на сметка. Тъй като тази част от кода се повтаря, е по-удобно да се постави отделно, както е показано впредишна версия на upd_int, в процедура (calc_int). Този подход прави кода модулен, лесен за поддръжка и действително четим - но и по-малко ефективен.
И така, как можем да постигнем помирение между противоречивите начини за писане на код, правейки кода модулен и бърз в същото време? И така, възможно ли е да напишете код, като използвате модулен подход (както в първата версия на upd_int), и след това да оставите PL/SQL компилатора да го "оптимизира", така че да изглежда като във втората версия на кода?
Това може да се направи в Oracle Database 11g. Всичко, което трябва да се направи, е да се прекомпилира процедурата с по-високо ниво на PL/SQL оптимизация. Това може да се постигне по два начина:
-
Задайте опцията за ниво на сесия и прекомпилирайте процедурата: Командата, показана по-горе, инструктира PL/SQL компилатора да пренапише кода във вграден код.
Компилирайте процедурата директно с инсталацията на plsql.
Всяка друга процедура, компилирана в същата сесия, няма да бъде засегната. Този метод се използва най-добре за обработка на вграждане, ако има много процедури, които трябва да бъдат компилирани в една сесия.
Можете да използвате секцията pragma, която е директива на компилатор.
Добавих реда прагма inline (calc_int, 'YES'); за да каже на компилатора да замени тази процедура в кода. По подобен начин можете да посочите „НЕ“ на същото място, за да кажете на компилатора да не отменя тази процедура, дори ако plsql_optimizer_level е зададено на 3.
Вграждането кара кода да работи по-бързо. Точната степен на подобрение ще зависи, разбира се, от броя на заместванията, които ще бъдат направени от компилатора. В края на тази статия ще разгледаме пример с използване на вграждане и ще видим подобрението на производителността в резултат на това.приложения.
Време за компилиране
Разбира се, този процес на оптимизация затруднява работата на компилатора. Но колко?
За да отговоря на този въпрос, взех реалния код на приложението, показан по-рано, и го компилирах в различни комбинации от вграждане/без вграждане и интерпретиран/роден. Ето времето за компилация: