Efektīva (vairāk vai mazāk) SQL un PL/SQL vadlīnijas

1. Iespēja kaut ko izdarīt visātrāk ir to nedarīt vispār, tāpēc par katru SQL teikumu būtu jāpārliecinās:

  • vai tas vispār ir vajadzīgs, varbūt to var nemaz nepildīt,
  • vai to tiešām ir nepieciešams pildīt tik daudz reižu (ciklā), varbūt to var pildīt tikai vienreiz (piemēram iegūt kodu no db, aizpildīt mainīgo ar sistēmas datumu utml),
  • varbūt to var pildīt tikai kādos speciālos gadījumos (if, case).

2. Pēc iespējas jādomā kopas operācijās. Jāmēģina izvairīties no procedurālas SQL teikumu sasaistes. Ja problēmu ir iespējams atrisināt tikai ar SQL līdzekļiem, tad vispārējā gadījumā tā arī jādara. Jo sevišķi jāmēģina izvairīties no SQL konstrukcijām, kas ir ievietotas PL/SQL ciklos.

3. Dinamisko SQLu ir atļauts lietot tad un tikai tad, ja ar statisko SQLu uzdevumu nav iespējams izpildīt.

4. Ja tomēr nepieciešams izmantot dinamisko SQLu un jo sevišķi, ja ir paredzams, ka tas notiks bieži, tad ir jāizmanto EXECUTE IMMEDIATE kopā ar USING klauzu, lai tiktu lietoti bind variables.

5. Dinamiski ģenerējot SELECT teikumus ar iepriekš nezināmu skaitu parametru (tipiski izvērstām meklēšanas iespējām) ir jāizmanto konteksta iespējas (CREATE OR REPLACE CONTEXT), lai sistēma netiktu pārpludināta ar tikai vienreizlietojamiem unikāliem SQL teikumiem, kas nelieto BIND VARIABLES.

6. Ir jāmēģina izvairīties no lietotāja definētu funkciju izsaukšanas SQLā, jo sevišķi, ja attiecīgais SQLs tiek izpildīts bieži vai arī lietotāja definētā funkcija tiek izsaukta daudz reižu viena SQL teikuma ietvaros, jo tiek apstradāts liels datu apjoms. Tā vietā lietotāja definētā funkcija jāmēģina iekļaut pamata SQL teikumā.

7. Ja PL/SQL kodā no datubāzes jāiegūst mainīgie, kas pēc tam nemainās un tiek vairākkārt izmantoti, tad tos vēlams inicializēt vai nu pakotnes ķermenī (bet tikai tad, ja vērtība datubāzē NEVAR mainīties starp vairākiem pakotnes izsaukumiem) vai arī procedūras sākumā (ja vērtība datubāzē var mainīties starp vairākiem pakotnes izsaukumiem).

8. UPDATE operācijām pēc iespējas jāveic koriģēšana tikai tām kolonām, kas patiesi ir mainījušās.

9. Visiem SELECT, UPDATE, INSERT INTO..SELECT FROM, MERGE un DELETE, kas nav triviāli (nenotiek uz vienu tabulu pēc primārās atslēgas) vajadzētu noskaidrot potenciālo izpildes plānu lietojot tādus rīkus kā EXPLAIN PLAN vai AUTOTRACE SQL*Plusā vai izmantojot atbilstošu funkcionalitāti PL/SQL Developer vai SQL Navigator vai līdzīgos vizuālajos rīkos.

Vērtējot izpildes plānu jāņem vērā šādi faktori:

  • Ja darbība tiks izpildīta ļoti bieži un atgriezīs mazu rezultātu kopumu, tad iesaistītajām operācijām būtu jānotiek pēc indeksa(-iem) un ir jāizvairās no tabulu full scan.
  • Ja darbības rezultātam ir nepieciešams pārskanēt visu tabulu, tad ir jāveic full scan pa visu tabulu un jāizvairās no tabulu kombionēšanas (join) izmantojot nested loops uz šo tabulu.
  • Izpildes plānam darbu būtu jāsāk no tabulas, kura ir visselektīvākā, t.i., kur attiecība <Paredzamais atgriezto ierakstu skaits>/<Viss tabulas ierakstu skaits> ir vismazākais, lai kopsummā veicot pieprasījumu tiktu parbaudīta iespējami mazākā ierakstu kombinācija datubāzē.

10. Hintu (ne)lietošana. Izpildes plāni pēc iespējas optimāli būtu jāpanāk nelietojot hintus, bet izmantojot datu bāzes statistikas. Tāpēc arī par izpildes plāna pareizību var ar zināmu drošību pārliecināties tikai līdzīga satura un apjoma (testa, produkcijas) datu bāzē, kurai ir veikta atbilstoša statistiku rēķināšana un ir līdzīga konfigurācija.

10.1. Ja tas nav iespējams, tad pēc iespējas jālieto hinti, kas dod optimizatoram (optimizer) papildus informāciju, bet tieši nenosaka plāna izpildes veidu:

  • ALL_ROWS – mērķis ir iegūt visu rezultātu ar iespējami mazāko resursu patēriņu;
  • FIRST_ROWS (n) – mērķis ir iegūt pirmās (n) rindiņas pēc iespējas ātrāk;
  • DYNAMIC_SAMPLING (n) – dinamiski rēķina statistikas, atkarībā no dotā līmeņa n. Informācija par līmeņiem. Vērtīgs, ja statistikas netiek rēķinātas, kā arī priekš pagaidu tabulām (temporal tables).
  • DRIVING_SITE (table) – ja vaicājuma izpilde notiek vairākās datubāzēs izmantojot datubāzes linku, tad norāda kurā datubāzē vaicājums tiks izpildīts.
  • CARDINALITY (n) – norāda sagaidāmo ierakstu apjomu vaicājumam. Sevišķi noderīgs, ja nepieciešams izmantot tipus.

10.2. Hinti, kurus ieteicams izmantot tikai tad, ja ir 100% pārliecība par savu taisnību un kā pēdējo līdzekli:

  • FULL (table) – ja ir pilnīga pārliecība, ka ir nepieciešama visas tabulas pārskanēšana, lai iegūtu rezultātu, tipiski varētu noderēt atskaitēm, pilnīgi nepieņemams tipiskai datu ievadei/attēlošanai.
  • LEADING (table) – ja ir pilnīga pārliecība, ka vaicājuma izpilde jāsāk ar konkrētu tabulu un statistiku rēķinašana tabulām un indeksiem nedod vēlamo rezultātu.
  • Speciālos gadījumos, ja ir jāveic liela datu pievienošana kādā tabulā, tad var izmantot APPEND – paturot prātā, ka šīs operācijas rezultātā izveidotos datus nav iespējams atjaunot bez pilnas rezerves kopijas, kā arī, ka pēc šis operācijas ir jābeidz transakcija (COMMIT, ROLLBACK), ja vēlas veikt kādas darbības ar izmainīto tabulu.

10.3. Jebkuru citu hintu vajadzētu lietot tad un tikai tad, ja tā lietotājs pilnībā apzinās šī hinta sekas, piemēram, INDEX, INDEX_ASC vai INDEX_FFS – ir pilnīga pārliecība, ka indekss vienmēr būs tāds pats kā sākumā (saturēs tās pašas kolonas), indekss netiks nomests (drop), indekss netiks pārsaukts, šis indekss jebkuros apstākļos būs patiesi īstais un vienīgais, kas derēs vislabāk, lai iegūtu nepieciešamo rezultātu.

10.4. RULE hintu vajadzētu lietot tad un tikai tad, ja cilvēks zin, saprot un spēj izskaidrot visus RBO likumus, kas to nosaka (http://download-west.oracle.com/docs/cd/B10501_01/server.920/a96533/rbo.htm#38864 un http://www.oreilly.de/catalog/orsqltunpr/chapter/).

Turpmākā lasāmviela:

6 Responses to Efektīva (vairāk vai mazāk) SQL un PL/SQL vadlīnijas

  1. Edgars saka:

    Sveiks,

    Tu mani uzaicināji apskatīties bloga saturu, tagad nevaru noturēties un nepakomentēt.

    Punks 4. – ir jāizmanto EXECUTE IMMEDIATE

    DBMS_SQL arī nav ne vainas, dažreiz pat ir labāks, jo var izparsēt pieprasījumu vienu reizi pie inicializēšanas un izpildīt vairākas reizes ar dažādiem mainīgiem.

    Punkts 10. Hintu (ne)lietošana.

    Diemžēl, dzīvē, tas ir vieglāk sākams nekā izdarāms, jo CBO ir ļoti dinamisks un visi CBO rezultāti balstās uz statistikas par datu sadalījumu.
    It sevišķi tas ir grūti, ja programmatūra tiek izstrādāta priekš vairākiem pasūtītājiem (boxed application), jo datu sadalījums var būt pavisam atšķirīgs nekā izstrādes/testa vidē.
    Man liekas, ka labāk ieteikt izmantot Hintus pēc iespējas mazāk un tikai vissvarīgākajiem pieprasījumiem, utt.
    Pirms hintu izmantošanas, ir jāpamēģina panākt adekvātu plānu bez hintiem, ja ir iespējams šo plānu panākt, tad ir steidzīgi jāpieraksta dokumentācijā, kādas papildus operācijas nācās veikt, lai panāktu rezultātu.
    Izmantojot hintus, jāatceras tikai vienu, ka ja mainīsies datu sadalījums (ja būs pieejams cits, efektīvāks, plāns), tad vienīga iespēja iegūt efektīvāku plānu, būs programmatūras jaunas versijas izlaidums (testēšana, etc), kas var būt neadekvāti dārgi.
    Principā hintu izmantošana, ir viena no tām lietam, kurai ir ļoti sarežģīti noteikt kaut kādas vadlīnijas un pēc kuras var gandrīz viennozīmīgi pateikt, cik dziļi cilvēks saprot to ko viņš dara un cik liels profesionāls viņš ir. 🙂

    10.2 var izmantot APPEND

    Nav 100% korekti. Oracle atšķir divus Direct-Path insert veidus: with Logging un without Logging. Šeit ir aprakstīts, kā Oracle izlemj kādu logging režīmu izmantot: http://download.oracle.com/docs/cd/B19306_01/server.102/b14231/tables.htm#i1009882 Ir vērts arī piezīmēt, ka ir jāatcerās par citām lietām, kad izvertē vai lietot vai nelietot direct-path mode. Direct-path mode automātiski tiek aktivizēts, ja inserts notiek paralēli.
    Man liekas pats svarīgākais par Direct-Path vs Conventional Path ir aprakstīts, tālāk tajā linkā.

    10.3. INDEX, INDEX_ASC vai INDEX_FFS – ir pilniga parlieciba, ka indekss vienmer bus tads pats ka sakuma

    Ar INDEX hintu, sākot ar 10g viss ir nedaudz labāk. Izmantojot hintu var noradīt, tikai tabulas nosaukumu (aliasu), kurai ir jāmēģina izmantot indeksu. Noteiktu indeksu Oracle centīsies izvēlēties pats izmantojot pieejamu statistiku.

    http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/sql_elements006.htm#BABEFDFC

    10.4. RULE hintu vajadzetu lietot

    Nav jāaizmirst, ka RBO ir jau oficiāli miris un dažreiz izmantojot RULE hintu, Jūs varat izdarīt daudz vairāk slikta, nekā laba.
    Esmu redzējis simtiem dažādu pakotņu, kur izstrādātājiem bija slinkums testēt, kā viņu modulis strādās ar CBO un kur nedomājot, visur tika iebāzti RULE hinti. Nekas labs priekš DBA tas nav, jo vienkārši nav veida kaut ko ietekmēt, bez pilna izlaiduma cikla (bug report, izstrāde, testēšana, izlaidums, implementēšana). Es vispār nevienam neieteiktu izmantot RULE hintu 2007. gada beigās 🙂

    Par hintiem var runāt ļoti, ļoti ilgi 🙂

    Veiksmi,
    Edgars

  2. Gints Plivna saka:

    @Edgars
    >DBMS_SQL arī nav ne vainas, dažreiz pat ir labāks, jo var izparsēt >pieprasījumu vienu reizi pie inicializēšanas un izpildīt vairākas reizes ar >dažādiem mainīgiem
    Pilnīgi piekrītu, acīmredzot nebija pareizi salikti uzsvari Uzsvars bija uz to, ka nedrīkst rupji ģenerēt miljonu EXECUTE IMMEDIATE teikumus, kas savā starpā atšķiras tikai ar mainīgo vērtībām, jo tas rada daudzas nepatīkamas un nekā citādi neatrisināmas problēmas, tai skaitā pārpludina SHARED POOL ar līdzīgiem SQL teikumiem un rada iespējas SQL injekcijām.
    >Principā hintu izmantošana, ir viena no tām lietam, kurai ir ļoti sarežģīti
    >noteikt kaut kādas vadlīnijas un pēc kuras var gandrīz viennozīmīgi
    >pateikt, cik dziļi cilvēks saprot to ko viņš dara un cik liels profesionāls
    >viņš ir.
    Šeit varētu piekrist un man vēlreiz gribētos uzsvērt – ka galvenais tik tiešām lietojot hintus ir – apzināties tas sekas, kas var rasties, ja mainās dati, pazūd/tiek mainīti/rodas kļat objekti un hints vēl joprojām tiek izmantots, kaut gan situācijas iespējams ir pavisam citāda. Ja cilvēks apzinās šī hinta sekas, tad jau viss ir kārtībā.
    >Par Append
    Hmmm, nekad neesmu nodarbojies ar direct path insert tāda vai citādā force logging modē. Tad, ja es pareizi saprotu, galvenās atšķirības starp conventional insert un direct path insert with logging būtu, ka indeksi pēdējam veidam tiek koriģēti tikai pašās beigās un rakstīts tiek virs HWM (high water mark)?
    >Rule
    Njā oficiāli Rule jau bija ļoti neieteikts arī 9i, tas gan Oraclei pašai neliedza to lietot pat vēl 10g 🙂 Mums pirms pāris gadiem viens īrētais resurss pamanījās to lietot, tad vēl 9i. Bet nu uzsvars bija uz to, ka, ja cilvēks iemācīsies visus rule likumus, tad jādomā, ka pa starpām palasīs arī kaut ko citu un sapratīs, ka tā nevajag darīt 🙂

  3. Edgars saka:

    Par APPEND hintu

    Man dažas produkcijas datubāzes speciāli ir/bija ieliktas FORCE LOGGING mode, tikai tāpēc, lai izvairītos no izstrādātāja, kam patīk nologging direct path load.
    Bija gadījumi, kad vajadzēja bāzi atjaunot un pēkšņi viena no pamat-tabulām ir pilna ar logical corrupt blokiem un neviens nezin, kā lai ātri un efektīvi atjaunot tabulas saturu (nekur tas arī nav nodokumentēts).
    Tāpēc, ja tas ir manos spēkos/ja tas “īpaši neietekmēs” sistēmas darbu, es cenšos OLTP bāzēm ieslēgt FORCE LOGGING mode, lai varētu būt pārliecināts, ka atjaunošana no ARCHIVLOGIEM būs veiksmīga. DWH datubāzu gadījumā, datu atkārtota ielāde (ETL) ir vienkāršāka/dokumentētāka.

  4. Vetal saka:

    Varbut kads gudrs cilveks varetu man pateikt, kapec, lietotojot contekstus dynamic SQLaa, es nevaru to kontekstu izveidot no pakotnes?
    Meginu izveidot tadaa veidaa:
    execute IMMEDIATE ‘CREATE OR REPLACE CONTEXT :ppp USING ktrp_utility’ USING IN ‘ktpr_prts’
    dabuju kludu ORA-01805

    Ja nosaukumu rakstit iekshaa, un neizmantot parametru, tad viss notiek.

  5. Gints Plivna saka:

    njā intereasnti, ka lec ārā kļūda ORA-01805 kura nekur nav definēta.
    Bet problēma tātad ir, ka tu mēģini lietot bind variable nevis priekš mainīgā, bet objekta nosaukuma, un tur nekādus bind variables lietot nevar. Faktiski vispār execute immediate ar DDL teikumu nekādi mainīgie būt nevar.
    Skat šeit:
    “You cannot use bind arguments to pass the names of schema objects to a dynamic SQL statement.”

    Bet nopietnākais jautājums protams paliek – kāpēc vispār pakotnē ir jāveic šādas darbības? DDL pakotnēs ir visnotaļ slikts stils un vienmēr ir jābūt ļoti nopietnam attaisnojumam kāpēc tā darīt.

  6. Gints Plivna saka:

    Ja nu kāds šeit ir ieklīdis no MySQL puses, tad izrādās, ka ir laba vietne MySQL Performance Blog un attiecīgi MySQL Performance Presentations

Komentēt