Warning: Declaration of Suffusion_MM_Walker::start_el(&$output, $item, $depth, $args) should be compatible with Walker_Nav_Menu::start_el(&$output, $item, $depth = 0, $args = Array, $id = 0) in /DISK2/WWW/plsql.cz/www/wp-content/themes/suffusion/library/suffusion-walkers.php on line 39
Nov 062011
 

Je to necelý týden co se na plsqlchallenge.com objevil kvíz, kde bylo podstatné vědět, že Oracle nabízí dva způsoby serializování přístupu k datum: Defalutní je transakční – jedna transakce nevidí změny druhé, dokud ji druhá neukončí. Druhá a méně známá možnost je serializaci na úrovni session – jedna session nevidí změny, která provedla druhá (včetně commitnutých transakcí) dokud druhá session neskončí. Serializace na úrovni session se dá nastavit například pomocí ALTER SESSION:

ALTER SESSION SET ISOLATION_LEVEL = SERIALIZABLE;

Nicméně standard SQL92 nabízí nabízí další dvě možnosti serializace přístupu k datům – nezajímavé “Repeatable read” a mnohem zajímavější “Read Uncommited”, tedy čtení dat ostatních transakcí, které ještě nejsou commitnuté. SQL92 to definuje jako možnost, nicméně Oraclem to není právem podporováno (a všichni víme proč – porušuje to ACID) i když některé databáze tuto podporu implementovaly.

Chápu důvody, proč to Oracle nepodporuje, nicméně umím si představit scénáře, kdy bych se rád na necommitnuté data podíval i s rizikem, že nemám konzistetní pohled na data. Kde? V programu určitě NIKDE, při analýze problému za jistých okolností vcelku snadno.

Opravdu není možné číst necommitnutá data v Oracle? Není – Nicméně za určitých speciálních okolností to lze ;) V Oracle od verze 11.2g existuje nový balík DBMS_XA, tento balík má primárně sloužit k dvojfázovému commitu (především distribuované transakce na odlišných databázích), nicméně z toho vychází i jedna krásná vlastnost – lze si mezi sessions předávat necommitnuté transakce, pokračovat v nich a tedy i číst necommitnutá data. Jak to funguje:

1) SESSION1: Pomocí DMBS_XA svážu svou transakci s XID
2) SESSION1: Provedu nějaké transakční změny (necommitnu ani nerolbacknu)
3) SESSION1: Pomocí DBMS_XA suspendnu session
4) — SESSION2: Pomocí DBMS_XA si vyzvednu rozdělanou transakci od SESSION1
5) — SESSION2: Pokračuji v transakci
6) — SESSION2:  Pomocí DBMS_XA vrátím zpět transakci (nezbytně nutně nemusím)
7) SESSION1: Commitnu;

Hurá na příklad:

1) Nejprve si vytvoříme testovací tabulku

CREATE TABLE T_TABLE (ID NUMBER, X_TEXT VARCHAR2(20));
table T_TABLE created.

2) SESSION1 – (body 1,2,3)

DECLARE
-- svázání transakce s cislem
XIDT   DBMS_XA_XID := DBMS_XA_XID(666);
ret    BINARY_INTEGER;
BEGIN
-- SESSION1: Pomocí DMBS_XA svážu svou transakci s XID
ret:=DBMS_XA.XA_START(XID=>XIDT,flag => DBMS_XA.TMNOFLAGS);
-- SESSION1: Provedu nějaké transakční změny (necommitnu ani nerolbacknu)
INSERT INTO T_TABLE SELECT ROWNUM,'TEST_'||ROWNUM  FROM DUAL CONNECT BY LEVEL<1000;
-- SESSION1: Pomocí DBMS_XA suspendnu session
ret:=DBMS_XA.XA_END(XID=>XIDT,flag => DBMS_XA.TMSUSPEND);
-- čekání, aby transakce byla vidět online
dbms_lock.sleep(300);
END;

3) SESSION2 – (body 4,5,6)

DECLARE
XIDT   DBMS_XA_XID := DBMS_XA_XID(666);
n_count BINARY_INTEGER;
ret BINARY_INTEGER;
BEGIN
-- SESSION2: Pomocí DBMS_XA si vyzvednu rozdělanou transakci od SESSION1
ret:=DBMS_XA.xa_start(xid => XIDT,flag => DBMS_XA.TMRESUME);
-- SESSION2: Pokračuji v transakci
SELECT COUNT(*) INTO n_count FROM T_TABLE;
dbms_output.put_line(n_count);
-- uz COMMITNU
ret:=DBMS_XA.xa_commit(xid=> l_xid,onePhase => TRUE);
END;

Na výstupu se opravdu objeví 999 – počet záznamů vložených (ale necommitnutých)

4) Test z jiné session, zda se data opravdu commitnula:

SELECT * FROM T_TABLE;

5) Předchozí script však obsahuje bug – transakci je nutné regulerně ukončit v DBMS_XA, jinak nad tabulkou T_TABLE zůstanou locky a další pokusy o úpravu dat končí s chybou: ORA-00054: prostředek je zaneprázdněn a pro získání byla zadána volba NOWAIT nebo vypršel časový limit, tedy jsme to měli regulerně ukončit commitem, náprava:

DECLARE
XIDT DBMS_XA_XID := DBMS_XA_XID(666);
ret number;
BEGIN
ret:=SYS.DBMS_XA.xa_start(xid => XIDT,flag => DBMS_XA.TMRESUME);
commit;
ret:=SYS.DBMS_XA.xa_end(xid => XIDT, flag => DBMS_XA.TMSUCCESS);
END;

Nyní již je vše vpořádku a tabulku T_TABLE lze dropnout a uklidit tak po sobě.

Nutno podotknout, že DBMS_XA pouze umožní předání rozběhlé transakci JEDNÉ jiné. A tedy se nejedná ve skutečnosti o “dirty reads” a tedy DBMS_XA jako všechno jiného v Oracle splňuje ACID test.

 Posted by at 04:04

  One Response to “Oracle 11.2g – DBMS_XA – předávání transakcí”

  1. Umím si představit uncommited read – když mi na čistotě dat nezáleží. Zdovna nedávno jsem do manuálu dělal skrínšot selectnutých dat a dotaz trval desítky sekund. No… je to píčovina, ale dokázal jsem si to představit :)

 Leave a Reply

(required)

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>