27 Kasım 2008 Perşembe

Data Concurrency and Consistency - Veri Uyumluluğu ve Tutarlılığı

Merhaba,



Oracle veritabanı bir ilişkisel veritabanı olduğundan tablolar arasındaki verilerin ilişkisi üzerine kurulmuştur. Bu ilişki bildiğimiz gibi primary ve foreign key başta olmak üzere index, synonym, pl/sql gibi destek üniteleri ile desteklenir. İlişkisel veritabanları birden çok kullanıcının aynı anda bağlı olacağı şekilde tasarlanmıştır. Bu da, yine birden çok kullanıcının aynı anda veritabanı içinde verilerin tutulduğu tablolara erişebileceği anlamına gelmektedir. Bugün anlatacağım konuda, birden çok kullanıcının aynı tablo üzerinde aynı anda aynı kolonu aynı değerlerle değiştirmesine kadar açıklamaya çalışacağım.

Veri uyumluluğu ve tutarlılığı birden çok kullanıcısı olmayan, yani sadece tekil kullanıcının bağlandığı -çoğu zaman test veya kişisel, bireysel sunucu diyebiliriz- durumlarda geçerliliğini yitirir. Çünkü tek kullanıcı istediği tabloyu değiştirebildiği için ve o anda bir başkasının da o tablodaki değişikliklerin üzerine yazamayacağı için veri tutarlılığından bahsedilemez. O veri, zaten tutarlı ve uyumlu olacaktır. Elektriklerin gitmesi, long operation dediğimiz işlemlerler sürerken veya ciddi bir pl/sql sorgusu çalıştırılırken ya da shutdown abort yazılmışken, veri uyumluluğu ve tutarlılığını sağlayabilen background processlerimiz de vardır. Bu tarz bir durumda bile Oracle, verilerin sağlığı için herşeyi yapmaktadır. SMON(System monitor) ve PMON(Process monitor) diye adlandırılan ve arkaplanda devamlı çalışan işlemler, Oracle'ın düzgün kapatılamadığı durumlarda devreye girer ve gerektiğinde "rollback" gerektiğinde "rollforward" yaparlar.


Tam olarak "Data Concurrency, Data Consistency" ne demek, şu şekilde açıklayabiliriz:

DATA CONCURRENCY: Birden çok kullanıcının aynı anda verilere erişmesine denir.

DATA CONSISTENCY: Her kullanıcı verilerin tutarlı halini görürler. Buna kullanıcının kendi yaptığı değişiklikler ve diğer kullanıcıların yaptığı değişiklikler de dahildir.

Oracle veritabanında, bir kullanıcı bağlı olduğu schemada, bir tablodaki veriye eriştiği zaman ve o veriyi "update" ederken, diğer hiçbir kullanıcı o veriyi update veya delete edemez. Çünkü ilk kullanıcı tablonun update etmeye çalıştığı kolonu üzerine kilit almıştır. Onun işlemi bittikten sonrada yani kolonu update ettikten sonrada kilit devam eder. Bu esnada buffer cache'de veri, dirty buffer olarak bekler. İlk kullanıcı henüz commit etmediği için aynı sessiondaki bütün kullanıcılar o verinin eski halini, yani update edilmemiş halini görürler. Update edilmiş halini tek gören ilk kullanıcımızdır. Bunun Oracle'da bu şekilde olmasının sebebi ise, kullanıcı yaptığı değişiklik veya değişikliklerden vazgeçebilir, rollback yapabilir. Ne zaman ki ilk kullanıcı commit eder, o zaman bütün kilitleri kaldırır ve diğer kullanıcılar da update edilmiş hallerini görmeye başlarlar. Yalnız, bu demek değildir ki, buffer cache'deki dirty buffer'lar commit ile yazılırlar. HAYIR, update, delete veya insert yani bir DML komutundan sonra gelen commit ile dirty buffer'ları dbf'lere (database file) yazmıyoruz. Bunun sebebi ise, redo logların dbf'lere yazılma işleminin son derece zor olduğudur. Veritabanının performansını çökermemek için verilerin dbf'lere yazıldığı belirli araklıklar vardır.

1) Buffer cache'deki dirty buffer miktarı 1/3'e ulaşırsa,
2) alter system switch logfile; yazılırsa,
3) Redo log'ların boyutu dolar ve diğer gruba geçerse,


VERILER REDO LOG'LARDAN DATABASE FILE'LARA YAZILIRLAR...


ANSI'nin belirlediği SQL standartlarına göre 3 çeşit "Preventable Phenomena" vardır. Yani 3 çeşit Önlenebilir fenomen vardır. Peki ne demek bu? Hemen açıklayalım.

Dirty Read: Bir başkası tarafından (bir başkasının transaction'ı tarafından) henüz commit edilmemiş verinin, diğer kullanıcının transaction'ı tarafından okunmasına denir.

Fuzzy Read: Bir transaction eğer daha önceden okuduğu veriyi tekrar okuduğunda, bir başka transaction'ın, ilk okuduğu veriyi commit etmesiyle, okuyacağı veri değişir ve buna da fuzzy read denir.

Phantom Read: Belirli bir arama koşulunu sağlayan ve belirli bir satır döndüren bir transaction yeniden aynı sorguyu sorguladığı zaman, bir diğer transaction'ın, aynı arama koşullarını sağlayan veriyi commit etmesiyle ortaya çıkan ortak sonuç kümesinden okuma işlemine denir.


Commit edilmemiş veri; dirty, fuzzy ve phantom olarak okunabilir, Commit edilmiş veri; fuzzy ve phantom olarak okunabilir, Tekrar tekrar okunan veri; sadece phantom olarak okunabilir. Bu şekilde sınırlanan okuma durumlarına ve oluşan katmana, Isolation Level (İzolasyon seviyesi) denir.

Oracle'ın garanti ettiği 2 tane uyumluluk kontrolü vardır. Bunlar, Statement level read consistency ve Transaction level read consistency dir. Bunlar, Oracle'ın her zaman bir sorgunun veri ile döneceğini ve her sorgunun veriye ulaşacağını garanti eden koşullardır.

Az önce bahsettiğim row locking yani satır bazında kilitleme duruma geri dönersek, satır kilitleme işlemi; bir kullanıcı o satırdaki veriyi update delete veya insert ederken satır bazında kilitleme gerçekleşir ve kilidi bulunduğu transaction ve kendisinin üzerine alır. Üzerineki kilit, commit veya rollback diyene kadar ya da ikisini de demeden veritabanından çıkana kadar (rollback yapar fakat autocommit on ise commit yaparak çıkar) geçerlidir. Bu kilit olduğu sürece aynı satıra erişmeye çalışan ve update etmeye kalkışan bütün transactionlar askıda kalırlar.

Bunu bir örnek ile göstermem gerekise;

İlk session'ımızı açtık ve ogan kullanıcısı ile bağlandık ...


C:\> sqlplus
ogan/deneme@orcl

SQL*Plus: Release 10.2.0.3.0 - Production on Thu Nov 27 19:13:04 2008

Copyright (c) 1982, 2006, Oracle. All Rights Reserved.

Connected to:Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - Production With the Partitioning, OLAP and Data Mining options

SQL> set timing on

SQL> drop table ogan_deneme purge;

Table dropped.

Elapsed: 00:00:04.38
SQL> create table ogan_deneme
2 (
3 kullanici_adi varchar2(40),
4 kullanici_soyadi varchar2(50)
5 );

Table created.

Elapsed: 00:00:00.09
SQL> insert into ogan_deneme

2 values ('ogan','ozdogan');

1 row created.

Elapsed: 00:00:00.01
SQL> / 1 row created.

Elapsed: 00:00:00.00
SQL> / 1 row created.

Elapsed: 00:00:00.00
SQL> select * from ogan_deneme;

KULLANICI_ADI KULLANICI_SOYADI

----------------------------------------

ogan ozdogan
ogan ozdogan
ogan ozdogan

Elapsed: 00:00:00.04

ogan_deneme tablomuza 3 tane duplicate veri aktardık ve şimdi bunları update ediyoruz ve row lock'ı üzerimize alıyoruz ...

SQL> update ogan_deneme
2 set kullanici_adi = 'ahmet';

3 rows updated.


Elapsed: 00:00:00.01
SQL> select * from ogan_deneme;

KULLANICI_ADI KULLANICI_SOYADI
-------------------------------------------

ahmet ozdogan
ahmet ozdogan
ahmet ozdogan

Elapsed: 00:00:00.03

Kullanici_adi satırı üzerinde henüz bir DDL çalıştırmadığımız veya commit ya da rollback yazmadığımız için row lock duruyor. Şimdi bunu aynı user ile bağlanan fakat farklı transaction'dan update etmeye çalışan kullanıcı ile nasıl olacağına bakalım ...

C:\> sqlplus
ogan/deneme@orcl

SQL*Plus: Release 10.2.0.3.0 - Production on Thu Nov 27 19:23:19 2008

Copyright (c) 1982, 2006, Oracle. All Rights Reserved.

Connected to:Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - Production With the Partitioning, OLAP and Data Mining options

SQL> select * from ogan_deneme;

no rows selected

Peki şimdi ne oldu? Hazır yeri gelmişken, veriler, diğer transaction tarafından commit edilmediği için görülemiyor ... Diğer transaction'a geçelim ve commit edelim ...

SQL> commit;

İlk ve ikinci transactionların ogan_deneme tablosunu nasıl gördüklerini görelim ...

SQL> select * from ogan_deneme;

KULLANICI_ADI KULLANICI_SOYADI

--------------------------

ahmet ozdogan
ahmet ozdogan
ahmet ozdogan

Elapsed: 00:00:00.04

Şimdi en kritik noktaya geldik. İlk transaction kullanici_adi'nı update etmeye çalışacak ve commit etmeyecek. Bu sırada ikinci transaction da aynı satırı update etmeye çalışacak ...

İlk oturum


SQL> update ogan_deneme
2 set kullanici_adi=
3 'ogan'
4 where kullanici_adi='ahmet';

3 rows updated.

Elapsed: 00:00:00.01

İkinci oturum

SQL> update ogan_deneme
2 set kullanici_adi='mehmet'
3 where kullanici_adi='ahmet';
...
...
...
...

* * * İkinci oturum aynı satırı update etmeye çalışırken askıda kaldı. Çünkü o satırın şu anki sahibi, kilidi koyan transaction ilk transactiondır. Kilitlenen transaction ve kilitleyen transaction'a bakalım ...

C:\> sqlplus / as sysdba

SQL*Plus: Release 10.2.0.3.0 - Production on Thu Nov 27 19:44:10 2008

Copyright (c) 1982, 2006, Oracle. All Rights Reserved.

Connected to:Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - Production With the Partitioning, OLAP and Data Mining options


SQL> select * from dba_waiters;

WAITING_SESSION HOLDING_SESSION LOCK_TYPE
--------------- --------------- --------------------------
MODE_HELD

----------------------------------------
MODE_REQUESTED LOCK_ID1 LOCK_ID2
---------------------------------------- ---------- ----------
159 153 Transaction Exclusive Exclusive 589831 1127


159 numaralı session (ikinci) bekliyor, 153 numaralı session (ilk) onu
tutuyor.

SQL> select * from dba_blockers;

HOLDING_SESSION
---------------
153

SQL> select * from v$lock order by SID;

ADDR KADDR SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK
-------- -------- ---------- -- ---------- ---------- ---------- ---------- ---------- ---------
331C4324 331C433C 153 TM 54936 0 3 0 611 0
3323CD88 3323CDAC 153 TX 589831 1127 6 0 611 1
33C34398 33C343AC 159 TX 589831 1127 0 6 506 0
331C43E8 331C4400 159 TM 54936 0 3 0 1092 0

TX: Satır tipi kilitlemelere verilen isim TX'tir. Genelde DML omutları kullanılırken satırlara TX kiliti konulur

TM: Tablo tipi kilitlemelere verilen isim TM'dir. Tablolar DDL komutlarına karşı kilitlenirler.

SQL> select * from dba_locks order by session_id;

SESSION_ID LOCK_TYPE MODE_HELD MODE_REQUESTED

---------- -------------------------- ---------------------------------------- ------

153 DML Row-X (SX) None
153 Transaction Exclusive None
159 Transaction None Exclusive
159 DML Row-X (SX) None
164 Temp Segment Row-X (SX) None
165 RS Row-S (SS) None
165 XR Null None
165 Control File Row-S (SS) None
166 Redo Thread Exclusive None
167 Media Recovery Share None
167 Media Recovery Share None
167 Media Recovery Share None
167 Media Recovery Share None
167 PW Row-X (SX) None
167 Media Recovery Share None

15 rows selected.

Satır bazında exclusive kilitleri 153 ve 159 için görüyoruz ...

Bu aşamadayken bahsetmek istediğim bir durum var. "Deadlock" dediğimiz kilit çeşidir vardır. Eğer ilk transaction A tablosunu update ediyorsa, aynı anda diğer bir transaction B'yi update ediyorsa ve ikisi de commit etmediyse, tam bu esnada ilk transaction B'yi, ikinci transaction'da A'yı update etmeye çalışırsa (update edilmeye çalışılan satırların aynı olduğunu düşünelim) o zaman ikisi birden kilitlenir ve deadlock oluşur.


Bir başka deadlock oluşma yolunu aşağıdaki (tahiti.oracle.com'dan alınan bir örnektir) tabloda görebiliriz. Kurtulmanın yolu, dba olarak bağlanıp ikisinden birinin session'ını kill etmemiz gerekir. Kill edilen session, kendisini rollback edeceğinden, diğeri update'ine devam edebilecektir.

DEADLOCK





İkinci transaction kilitli durumda bekliyor. Onu kurtaralım :) İlk session'da commit ya da rollback diyelim. Fakat bunu derken diğer komut satırıda ekranın yanında dursun. İlk transaction commit edildiğinde ikinci transaction'ın nasıl davrandığına ve ilk ve ikinci session'ın ogan_deneme tablosunu nasıl gördüğüne bakın ...

Şimdi de "flashback query" özelliğinden bahsetmek istiyordum ancak ya vista'dan dolayı ya da blogger'dan dolayı explorer çok yavaşlamış durumda :) Bundan sonraki yazı için flashback query diyelim ve oradan devam edelim. Konu ile ilgili daha detaylı yazmak isterdim fakat yazabilmek için ter döküyorum olmuyor :) Okuduğunuz zaman aklınıza takılan birşey olursa lütfen mail atmaktan çekinmeyin.


İyi akşamlar,


Ogan

Hiç yorum yok:

Takip et: @oganozdogan