# [ORACLE] Demo: Locking Verfahren



## Exceptionfault (23. August 2005)

> Wie wärs denn mal mit einem "Tutorial" zum Thema locking in Oracle?
> Row-Level Locking
> Table Level Locking
> Select for update
> ...


Aber gerne doch ;-)
Locking ist eines der entscheidenden Verfahren um das ACID Prinzip von Transaktionen in Datenbanken zu wahren. ACID steht hierbei für Atomicity, Consistency, Isolation und Durability. Eine Transaktion muss also Isoliert von allen anderen Transaktionen laufen, sie dürfen sich nicht gegenseitig beeinflussen. Ausserdem muss der Zustand der Daten nach einer Transaktion Konsistent sein, es dürfen keine Regeln der Datenbank (Datenlogik) verletzt werden. 
Locking dient vor allem zur Isolation der Transaktion und verhindert, dass sich Benutzer durch den gleichzeitigen Zugriff auf die selben Resourcen stören.

Will ein Benutzer also einen Datensatz manipulieren, schliesst danach jedoch seine Transaktion nicht ab, so darf der Datensatz solange von keinem anderen Benutzer geändert werden. Es würde in die Transaktion vom 1. Benutzer eingreifen.

Demo:

```
User1:
SQL> conn bert/bert
Connected.
SQL> select * from lock_tes;

    FIELD1 FIELD2
---------- ----------
         1 Feld1
         2 Feld 2
         3 Feld  3

SQL> update lock_tes set field1=4 where field1=3;

1 row updated.
-----
User2:
SQL> conn erni/erni
Connected.
SQL> select * from bert.lock_tes for update wait 5;
select * from bert.lock_tes for update wait 5
                   *
ERROR at line 1:
ORA-30006: resource busy; acquire with WAIT timeout expired

SQL> select * from bert.lock_tes;

    FIELD1 FIELD2
---------- ----------
         1 Feld1
         2 Feld 2
         3 Feld  3

SQL> update bert.lock_tes set field2='Test' where field1=1;

1 row updated.
```
Bert hat also einen Satz upgedated und somit gelockt. Wenn Erni versucht ihn zu lesen mit dem Anhang "for Update" erbittet Erni von der Datenbank ebenfalls einen Lock um den Datensatz zu editieren, dieser ist jedoch schon gelockt und somit erscheint nach 5 Sekunden (wait 5) eine Fehlermeldung. Ein "normaler" Select funktioniert noch, zeigt Erni aber den ursprünglichen Datenbestand der Tabelle, und zwar so lange wie Bert seine Transaktion nicht commited hat.

Das ganze nennt man nun Row Level Locking, da der Lock nur auf dem einzelnen Datensatz liegt, man sieht am zweiten Update, dass es mit einem anderen Datensatz problemlos funktioniert.

Das schlimmste was ein Anwendungsentwickler produzieren kann sind sog. Full Table Locks. Das bedeutet jeder Satz der Tabelle ist gesperrt, was schonmal eine komplette Applikation lahm legen kann... Daher Updates möglichst in kleinen Transaktionen und möglichst rasch abschliessen!

Ein Table Level Locking (bzw, Object Level Locking) tritt meistens dann auf, wenn DDL Statements auf das Objekt wirken. Denkbar wäre z.B. das verschieben einer Tabelle in ein anderes Datenfile, der Neuaufbau einen Indizes etc. In diesem Fall ist das komplette Objekt für den Benutzerzugriff gesperrt. Zusätzlich gibt es noch DDL Locks. Diese verhindern z.B. das Droppen einer Tabelle falls gerade eine View erstellt (kompiliert wird) welche auf diese Tabelle zugreift.

Erfahrungsgemäss ist das Lockingverfahren von Oracle sehr sehr gut und alle mir bisher bekannten Probleme rühren von schlechter Entwicklung. Einer der gravierensten Fehler, nämlich ein "unsortierter" Zugriff auf Objekte führt bei stark genutzten Applikationen ständig zu Deadlocks:


```
Erni:
SQL> UPDATE bert.lock_tes set field2='Test2' where field1=1;

1 row updated.
Bert:
SQL> update lock_tes set field2='Test3' where field1=2;

1 row updated.
Erni:
SQL> update bert.lock_tes set field2='Testbla' where field1=2;
... Transaktion wartet, wegen Lock von Bert...
Bert:
SQL> update lock_tes set field2='djkfh' where field1=1;
... Transaktion wartet, wegen Lock von Erni...
Erni:
update bert.lock_tes set field2='Testbla' where field1=2
            *
ERROR at line 1:
ORA-00060: deadlock detected while waiting for resource
```
Oracle erkennt zum Glück den Deadlock und bricht eine der Transaktionen ab und rollt sie zurück.
Auf welchem Objekt gerade ein Lock sitzt, und ob und wer gerade darauf wartet, kann man aus mehreren Tabellen im Data Dictionary lesen.
Einen guten Einstieg gibt die Tabelle DBA_BLOCKERS. Sie liefert die SessionID der User, die andere User blockieren. Die SessionID kann man widerum in der v$Session Abfragen und somit den User, den Host und die Applikation ermitteln.
Auch V$SESSION_WAIT ist gut, sie zeigt wartende Benutzer.
DBA_DML_LOCKS zeigt alle DML Locks in der Datenbank. Noch etwas genauer, wenn man z.B. die gelockte Zeile ermitteln will, ist jedoch die View v$Lock, wenn auch etwas kompliziert zu lesen.


```
SQL> select * from v$lock where sid in ( select sid from v$session where username <> 'ORACLE');

ADDR     KADDR           SID TY        ID1        ID2      LMODE    REQUEST      CTIME      BLOCK
-------- -------- ---------- -- ---------- ---------- ---------- ---------- ---------- ----------
29E1957C 29E19590         39 TX     327698        298          0          6        852          0
292BC6F4 292BC70C         39 TM      52533          0          3          0        852          0
292BC7A0 292BC7B8         49 TM      52533          0          3          0       1143          0
292D7620 292D773C         49 TX     327698        298          6          0       1143          1


SQL> select sid, username from v$session where sid in (39, 49);

       SID USERNAME
---------- ------------------------------
        39 ERNI
        49 BERT

SQL> SELECT Owner, Object_type, object_name from dba_objects where object_id = 52533

OWNER                          OBJECT_TYPE         OBJECT_NAME
------------------------------ ------------------- --------------------
BERT                           TABLE               LOCK_TES
```

Eine hübsche Methode den Datensatz zu bekommen:

```
Wir nehmen die SID der geblockten Session (siehe v$lock):

SQL> SELECT SID, ROW_WAIT_OBJ#, ROW_WAIT_FILE#, ROW_WAIT_BLOCK#, ROW_WAIT_ROW# FROM V$SESSION WHERE SID = 49;

       SID ROW_WAIT_OBJ# ROW_WAIT_FILE# ROW_WAIT_BLOCK# ROW_WAIT_ROW#
---------- ------------- -------------- --------------- -------------
        49         52533              4             404             1

... ermitteln den Namen des Objekts (über DBA_OBJECTS) und fragen die Zeile mit der gesuchten ROWID ab:

SQL> SELECT * FROM BERT.LOCK_TES WHERE ROWID = DBMS_ROWID.ROWID_CREATE(1, 52533, 4, 404, 1);
    FIELD1 FIELD2
---------- ----------
         2 Test3

Tada ;-)
```

Allgemein kann man sagen, dass Locking ein sehr schwieriges Thema ist. Schon alleine die Theroie über Isolation Levels etc.. würde den Rahmen hier bei weitem sprengen. Dennoch sollte aber jeder Entwickler zumindest das ACID Prinzip verstanden haben und wissen worauf es in seinem verwendeten RDBMS ankommt um es erfolgreich umzusetzen. Ich muss zugeben, dass mir oft auch nie ganz klar wird wann Oracle welchen Lock setzt und was dann noch alles erlaubt wird, in so einem Fall hilft eben einfach nur ausprobieren.
Ums Testen kommt man sowieso nicht drum rum, denn spätestens bei einem Versionswechsel kann sich das Locking Verhalten wieder ändern. Seit Oracle 9 gibt es auch schöne Features wie "ALTER INDEX REBUILD ONLINE". Dies verhindert z.B. ein kompletten Lock des Indizes so dass dieser weiterhin benutzt werden kann, bis er an anderer Stelle komplett neu aufgebaut wurde. (Kann bei sehr großen Tabellen durchaus Sinn machen)

Soviel erstmal zum Thema Locking. Im Moment wüsste ich nicht was ich dazu noch erklären könnte ohne zu tief einzusteigen ?!


----------



## Thomas Darimont (23. August 2005)

Hallo!

   wiedermal wunderbar gemacht  
   Wie wär's mit noch nem tutorial zum Thema DB-Links in Oracle? *g*
  //Edit:
  Ich sehe es schon vor mir... bald brauchen wir wirklich sowas wie einen
askandreas.tutorials.de - Corner ;-)

   Gruß Tom


----------



## Nico Graichen (23. August 2005)

Auch von mir ein großes DANKE *lob*
Werd' bei einer Gelegenheit mal versuchen, das in der Praxis nachzuvollziehen, kann sein, dass ich dann noch Fragen hab 
Klingt so aber erstmal alles einleuchtend.

Nochmal ein großes DANKE


----------

