Skip to content

Latest commit

 

History

History

22_Transactions

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

Transaktionen

Folien zur Theorie dazu sind auf https://www.griesmayer.com/?menu=Oracle&semester=DatabaseArchitecture&topic=01_Transaction abrufbar.

In SQL Developer muss unter Preferences - Advanced der Punkt Autocommit für diese Übung deaktiviert. werden. In DBeaver muss in der Symbolleiste statt Auto der Punkt Manual Commit aktiviert werden.

Anlegen von 2 Usern

Wir verbinden uns mit dem User System zur Datenbank und legen 2 Benutzer an.

-- DROP USER User1 CASCADE;
CREATE USER User1 IDENTIFIED BY oracle;
GRANT CONNECT, RESOURCE, CREATE VIEW TO User1;
GRANT UNLIMITED TABLESPACE TO User1;

-- DROP USER User2 CASCADE;
CREATE USER User2 IDENTIFIED BY oracle;
GRANT CONNECT, RESOURCE, CREATE VIEW TO User2;
GRANT UNLIMITED TABLESPACE TO User2;

Nach dem Anlegen der User werden in SQL Developer oder DBeaver 2 Oracle Verindungen erstellt: Eine unter dem Benutzer User1 und eine unter dem Benutzer User2. Öffnen Sie dann für diese User sowie dem System User ein SQL Fenster.

COMMIT mit mehreren Usern

Operationen unter User1

Nun erstellen wir die Tabelle GRIESMAYER_ACCOUNTS

CREATE TABLE GRIESMAYER_ACCOUNTS
(
  ACCOUNT_ID  INTEGER      NOT NULL PRIMARY KEY,
  FIRST_NAME  VARCHAR(15)  NOT NULL,
  BIRTH_DATE  DATE         NOT NULL,
  AMOUNT      DECIMAL(8,2) NOT NULL
);

Damit User2 auf diese Tabelle zugreifen kann, gewähren wir das Recht für User2:

GRANT SELECT ON User1.GRIESMAYER_ACCOUNTS TO User2;
GRANT DELETE ON User1.GRIESMAYER_ACCOUNTS TO User2;
GRANT INSERT ON User1.GRIESMAYER_ACCOUNTS TO User2;
GRANT UPDATE ON User1.GRIESMAYER_ACCOUNTS TO User2;

Nun werden einige Datensätze eingefügt. Sie sind unter User1 sichtbar:

DELETE FROM   GRIESMAYER_ACCOUNTS;
INSERT INTO GRIESMAYER_ACCOUNTS VALUES (1, 'Thomas', TO_DATE('1973-07-14', 'yyyy-mm-dd'),  500.50);
INSERT INTO GRIESMAYER_ACCOUNTS VALUES (2, 'Andera', TO_DATE('1975-08-20', 'yyyy-mm-dd'),  100.00);
INSERT INTO GRIESMAYER_ACCOUNTS VALUES (3, 'Marion', TO_DATE('1981-12-12', 'yyyy-mm-dd'), -200.00);
INSERT INTO GRIESMAYER_ACCOUNTS VALUES (4, 'Verena', TO_DATE('1977-01-27', 'yyyy-mm-dd'),  900.00);
INSERT INTO GRIESMAYER_ACCOUNTS VALUES (5, 'Kurt',   TO_DATE('1975-02-28', 'yyyy-mm-dd'),  800.40);

SELECT * FROM   GRIESMAYER_ACCOUNTS;

-- Ausführen als User System
SELECT * FROM  V$TRANSACTION;

Operationen unter User2

Das SELECT zeigt keine Datensätze:

SELECT * FROM  User1.GRIESMAYER_ACCOUNTS;

Operationen unter User1

Wir setzen ein COMMIT ab, damit die Transaktion geschrieben wird.

COMMIT;

Operationen unter User2

Das SELECT zeigt jetzt die Änderung der Datensätze:

SELECT * FROM  User1.GRIESMAYER_ACCOUNTS;

Operationen unter User1

Wir aktualisieren den Wert von AMOUNT bei User1 ohne COMMIT.

UPDATE GRIESMAYER_ACCOUNTS SET AMOUNT = 100 WHERE ACCOUNT_ID = 1;

Operationen unter User2

Die Änderung ist noch nicht sichtbar. Wir starten ebenfalls eine Änderung:

SELECT * FROM  User1.GRIESMAYER_ACCOUNTS;
UPDATE User1.GRIESMAYER_ACCOUNTS SET AMOUNT = 200 WHERE ACCOUNT_ID = 2;

Erst beim COMMIT des jeweiligen Users wird die Änderung sichtbar.

Gleichzeitiges Ändern von Datensätzen

Wird der gleiche Datensatz von beiden Usern veränedrt, so wartet das 2. COMMIT bis das erste COMMIT durchgeführt wurde.

Busy wait

Fügen Sie in die Tabelle GRIESMAYER_ACCOUNTS wieder als Grundzustand die Musterdaten ein:

DELETE FROM   GRIESMAYER_ACCOUNTS;
COMMIT;
INSERT INTO GRIESMAYER_ACCOUNTS VALUES (1, 'Thomas', TO_DATE('1973-07-14', 'yyyy-mm-dd'),  500.50);
INSERT INTO GRIESMAYER_ACCOUNTS VALUES (2, 'Andera', TO_DATE('1975-08-20', 'yyyy-mm-dd'),  100.00);
INSERT INTO GRIESMAYER_ACCOUNTS VALUES (3, 'Marion', TO_DATE('1981-12-12', 'yyyy-mm-dd'), -200.00);
INSERT INTO GRIESMAYER_ACCOUNTS VALUES (4, 'Verena', TO_DATE('1977-01-27', 'yyyy-mm-dd'),  900.00);
INSERT INTO GRIESMAYER_ACCOUNTS VALUES (5, 'Kurt',   TO_DATE('1975-02-28', 'yyyy-mm-dd'),  800.40);
COMMIT;

Führen Sie nun die folgenden SQL Anweisungen in SQL Developer unter dem entsprechenden User aus:

User1 User2
UPDATE GRIESMAYER_ACCOUNTS SET FIRST_NAME = 'Klaus' WHERE ACCOUNT_ID = 1;
UPDATE User1.GRIESMAYER_ACCOUNTS SET FIRST_NAME = 'Michael' WHERE ACCOUNT_ID = 1;

Es entsteht ein Busy wait. Sie müssen nun unter User1 ein COMMIT absetzen, um die Daten schreiben zu können. Um den Lock zu beobachten, führen Sie vor dem COMMIT folgendes SQL Statement unter User1 aus:

SELECT SID,
       DECODE ( block,
                    0, 'Not Blocking',
                    1, 'Blocking',
                    2, 'Global'
               ) status,
        DECODE (lmode,
                    0, 'None',
                    1, 'Null',
                    2, 'Row-S (SS)',
                    3, 'Row-X (SX)',
                    4, 'Share',
                    5, 'S/Row-X (SSX)',
                    6, 'Exclusive', TO_CHAR(lmode)
                ) mode_held,
        DECODE (REQUEST,
                    0, 'None',
                    1, 'Null',
                    2, 'Row-S (SS)',
                    3, 'Row-X (SX)',
                    4, 'Share',
                    5, 'S/Row-X (SSX)',
                    6, 'Exclusive', TO_CHAR(lmode)
                ) mode_request
FROM   v$lock
WHERE  TYPE = 'TM';

Sie bekommen folgende Ausgabe:

SID STATUS MODE_HELD MODE_REQUEST
261 Not Blocking Row-X (SX) None
261 Not Blocking Row-X (SX) None

Nach dem COMMIT unter User1 verschwindet zwar der Busy Wait, der Datensatz mit der Account ID 1 ist jedoch durch User2 weiterhin gesperrt:

SID STATUS MODE_HELD MODE_REQUEST
261 Not Blocking Row-X (SX) None

Erst nach einem COMMIT unter User2 verschwindet der Lock.

Weitere Beispiele

User1 User2
UPDATE GRIESMAYER_ACCOUNTS SET AMOUNT = AMOUNT + 100 WHERE ACCOUNT_ID = 1; Was sieht User2?
Was sieht User1? UPDATE User1.GRIESMAYER_ACCOUNTS SET AMOUNT = AMOUNT - 500 WHERE ACCOUNT_ID = 4;
COMMIT; Was sieht User2
Was sieht User1? COMMIT;

Angenommen User1 verringert den Betrag (Amount) eines Kunden, User2 verringert ebenfalls den Betrag des selben Kunden. Kann es sein, dass der Saldo negativ wird?

Deadlock

Führen Sie in SQLDeveloper folgende Anweisungen unter den entsprechenden Usern aus:

User1 User2
UPDATE GRIESMAYER_ACCOUNTS SET FIRST_NAME = 'Klaus' WHERE ACCOUNT_ID = 1;
UPDATE User1.GRIESMAYER_ACCOUNTS SET FIRST_NAME = 'Michael' WHERE ACCOUNT_ID = 2;
UPDATE User1.GRIESMAYER_ACCOUNTS SET FIRST_NAME = 'Michael' WHERE ACCOUNT_ID = 1;
UPDATE GRIESMAYER_ACCOUNTS SET FIRST_NAME = 'Klaus' WHERE ACCOUNT_ID = 2;

Nachdem Sie die Anweisungen unter User2 ausgeführt haben, ist dieser im Zustand Busy wait. Führen Sie nun unter User1 das UPDATE Statement aus, bekommt einer der beiden User eine Fehlermeldung:

Fehler beim Start in Zeile: 2 in Befehl -
UPDATE User1.GRIESMAYER_ACCOUNTS SET FIRST_NAME = 'Michael' WHERE ACCOUNT_ID = 1
Fehlerbericht -
ORA-00060: Deadlock beim Warten auf Ressource festgestellt