ceturtdiena, 2011. gada 15. septembris

Ligzdotās (nested) transakcijas

Vārdu salikums "ligzdota transakcija" (nested transaction) man sākotnēji pamatīgi nepatika, bet, pāris dienas padomājot, sāk likties, ka nemaz nav tik traki. Rakstā par to, kāpēc uzskatu, ka ligzdotās transakcijas nav vēlamas un raksta beigās- īsumā paskaidrots risinājums, kā apstrādāt situācijas, kurām ligzdotās transakcijas šķietami (bet tikai šķietami) varētu noderēt.

Tātad, parasta transakcija izskatās aptuveni šādi:
Begin Transaction
-- Viss, kas notiek transakcijā
Commit Transaction
Vai šādi (ja transakcija tiek atcelta):
Begin Transaction
-- Viss, būtu jānotiek transakcijā, bet tiks atcelts
Rollback Transaction
Ligzdota transakcija izskatītos apmēram šādi ("Begin Transaction" var saīsināt uz "Begin Tran", savukārt "Commit Transaction" var saīsināt uz "Commit"):
Begin Tran
Begin Tran
Commit -- attiecas uz iekšējo transakciju
Commit
Lai labāk saprastu domu, var izmantot SQL Server iebūvēto funkciju @@trancount, kas parāda, cik transakcijas šobrīd ir atvērtas:
select @@trancount -- = 0
Begin Tran
select @@trancount -- = 1
Begin Tran
select @@trancount -- = 2
Commit
select @@trancount -- = 1
Commit
select @@trancount -- = 0
Tātad ideja īsumā ir tāda, ka vienas "Galvenās" transakcijas ietvaros var būt vairākas apakš-transakcijas (ja nepieciešams, vairākos līmeņos). Svarīgākais kas no tā visa ir jāzina:
  • ja "Galvenā" transakcija tiks atcelta, tad arī visas apakš-transakcijas arī tiks atceltas.
  • nav iespējams atcelt (rollback) tikai apakšējās transakcijas. tātad, nav iespējams:
    • sākt galveno transakciju
      • sākt apakš-transakciju
      • mēģinājums atcelt tikai apakš-transakciju (šis neiet cauri!!! Tiks atcelta visa transakcija)
    • veiksmīgi pabeigt galveno transakciju
  • ligzdotās transakcijas nedod nekādu ātrdarbības ieguvumu
    No kā izriet loģisks jautājums- kāpēc vispār vajadzīgas ligzdotās transakcijas? Principā vienīgais iemesls ir atļaut transakcijā palaist procedūras, kuru iekšienē ir sākta jauna transakcija.. MSDN raksts par ligzdotām transakcijām.

    Procedūru iekšienē transakciju sāk ar mērķi- lai viss procedūras paveiktais darbs vai nu notiktu, vai nu pilnīgi nekas nenotiktu. Ja šī pati procedūra tiks izsaukta citā transakcijā un kļūdas dēļ šajā procedūrā tiks izsaukta transakcijas atcelšana (rollback), tātad arī visa ārējā transakcija tiks atcelta!

    Tik tālu viss šķietami loģiski- galu galā- kļūdas ir izņēmumu situācijas un ja tādas rodas, tas ir ļoti slikti un vispār neko tad nevajag darīt. Tātad, ja mēs veidojam savu skriptu un veidojam transakciju, kuras ietvaros izsauc citu procedūru, kurā, savukārt, tiek sākta cita transakcija, tad process izpildīsies tikai gadījumā, ja nebūs kļūdas (iekšējā procedūrā korekti sākta un pabeigta trancakcija- ar commit).

    No tā izriet, ka ligzdotās transakcijas ir visai nepatīkama padarīšana. Jo- tiklīdz rodas izņēmuma situācija, mēs uzduramies uz kļūdas paziņojumiem. Kāpēc? jo rakstot procedūru, kurā tiek sākta transakcija- visdrīzāk mēs būsim paredzējuši kļūdas gadījumu un tādā situācijā izsaucam rollback (ar mērķi atcelt procedūrā sākto transakciju), bet, kā minēts iepriekš, rollback attieksies uz ārējo transakciju, kas radīs kļūdu- apmēram tādu, kā aprakstīju rakstā "Vienmēr beidzam transakciju". Te var apskatīties kā tas izskatās ļoti vienkāršotā gadījumā:
    create proc test
    As
    begin tran
    rollback
    Go
    begin tran
    select @@trancount
    exec test
    Go
    drop proc test
    Tādēļ iesaku neizmantot ligzdotās transakcijas!

    Kāds tad būtu "smukais" variants korektai transakcijas sākšanai un kļūdas gadījumā atcelšanai tā, lai tas strādātu gan gadījumā, kad procedūra tiek izsaukta citā transakcijā, gan tad, kad procedūra tiek izsaukta bez ārējās transakcijas?
    Izmantot transakciju saglabāšanu (Save Transaction) un izmantot iespēju pārbaudīt procedūras iekšienē- vai tā tiek vai netiek izsaukta ārējā transakcijā (jau pieminētā funkcija- @@trancount).
    Šis glītais risinājums ir aprakstīts gan MSDN (Save Transaction), gan arī, piemēram, šeit.

    Tas, kas nav skaidrs- kādam nolūkam vispār vajadzīgi transakciju vārdi? Šo iespēju rakstā nepieminēju, bet var izlasīt MSDN rakstos (Begin Transaction, Rollback Transaction, Commit Transaction).

    Nav komentāru:

    Ierakstīt komentāru