
MVCC eş zamanlı olarak read (okuma) write (yazma) işlemlerinin birbirlerini engellemeden ve tutarlı bir şekilde yapılabilmesi için geliştirilmiştir. MVCC postgresql in eş zamanlı okuma ve yazma yapabilmesi için kullandığı veritabanı modelidir. Bu model sayesinde read işlemleri write işlemlerini ,write işlemleri read işlemlerini bloklamadan (Lock) işlem yapabilmelerine olanak sağlayan bir mekanizmadır. Tamamen bloklama yok Lock hiç olmayacak) demek imkansızdır, zaten tutarlılık açısından bunun mutlaka olması gerekmektedir (ACID). Contention yaratabilecek tek kullanım durumu, iki eşzamanlı transactionın aynı kaydı değiştirmeye çalışmasıdır, çünkü bir kez değiştirildiğinde, bu kaydı değiştiren transaction commit edene veya rollback edene kadar ilgili satırlar her zaman lock alacaktır.
Eşzamanlı read ve write işlemlerinin birbirini engellememesi, ve yine iş zamanlı transactionların tutarlılığı sağlıklı bir şekilde lock mekanizmasının yönetilmesi MVCC ile sağlanmaktadır, PostgreSQL bunu ise Transaction ID bilgilerini kullanarak yapmaktadır.
Transaction ID nedir?
Transaction ID bilgisi veritabanı işlemi yapılırken, işlemleri kontrol etmek adına VTYS programı tarafından üretilen unique bir değerdi (txid)r. Bilginin 32-bit olarak tutulmasından dolayı 4,294,967,295 farklı değer üretilebilmektedir. Postgresql MVCC modeline göre her satır ekleme işlemi sırasında iki farklı bilgi daha kayıt altına alınmaktadır, bu versiyonlamalar tabloların veri yapısında depolanır ve bunlar iki sütun olarak sıralanmaktadır.
Xmin : Insert Transaction ID bilgisi
Xmax: Delete Transaction ID bilgisi
O anki geçerli Transaction ID
postgres=# select txid_current(); txid_current-------------- 100 |
MVCC kullanılan veritabanlarında, bir satır güncellendiğinde veya silindiğinde (update , delete), orijinal veri öğesinin üzerine yeni veriler yazmaz veya eski veri doğrudan silinmez, bunun yerine veri öğesinin daha yeni bir sürümünü oluşturur. Bu yaklaşım güncellenen yada silinen bir verinin birden fazla sürümünün oluşmasına yol açar. Her transactionın görebileceği sürüm, uygulanan izolasyon seviyesine bağlı olarak değişir. Okuma ve yazma işlemleri böylece kilitlemeye gerek kalmadan birbirinden izole edilir. MVCC, eskiyen ve asla okunmayacak sürümlerin nasıl kaldırılacağına dair bir zorluk getirmektedir, bu durum ise eski sürümleri periyodik olarak taranması ve silinmesi için bir işlem uygulanır. PostgreSQL, VACUUM süreciyle bu yaklaşımı benimser.
Konuyu bir Örnek Üzerinden anlamaya çalışalım;
mvcc_test
adında bir tablo oluşturalım
postgres=# create table mvcc_test (id int, name varchar(50),surname varchar(50)); CREATE TABLE postgres=# insert into mvcc_test values (1,'Faruk','Cevik'); INSERT 0 1 postgres=# insert into mvcc_test values (2,'Betul','Cevik'); INSERT 0 1 |
2 Adet session açalım ve psql üzerinden bağlanarak autocommit özelliğini kapatalım
Session A
Session B
Session A da yapılan işlemler; bir kayıt daha ekliyoruz ve mevcut bir kaydıda güncelliyoruz, ancak commit etmiyoruz, sonrasında current txid ye bakalım;
postgres=# \set AUTOCOMMIT OFF |
Session A üzerinde tabloda 3 Kayıt var,
Session B üzerinden bu tabloyu ve txid mizi sorgulayalım;
postgres=# select xmin, xmax, id, name,surname from mvcc_test ; xmin | xmax | id | name | surname ------+------+----+-------+--------- 1408 | 0 | 1 | Faruk | Cevik 1409 | 1410 | 2 | Betul | Cevik (2 rows) txid_current -------------- 1411 |
Session B üzerinden sorguladığımızda session A da yapılan hiçbir değişikliği görmüyoruz, yani MVCC ye göre eski versiyonları görüyoruz, çünkü Session A da transaction henüz commit olmadı. Değişiklikler Commit olmadı ancak halen select sorgularımız çalışıyor herhangi bir Lock da yaşamadık.
Session B de Insert, Update işlemleri yapmak isteyelim;
postgres=# insert into mvcc_test values (4,'Yusuf','Cevik'); INSERT 0 1 postgres=# update mvcc_test set name ='Betus' where id=2; ERROR: canceling statement due to user request CONTEXT: while updating tuple (0,2) in relation "mvcc_test |
Session B de yeni bir kayıt eklemek istediğimizde herhangi bir problem olmadı çünkü row bazlı olarak bir problem yok yeni kayıt eklendi, A session da henüz commit olmayan id=2 kaydı burada da bir değişiklik yapılmak istendiğinde Lock oluştu ve psql komut satırı diğer tarafın commit veya rollback olmasını bekleyecek, bu yüzden bu işlemi cancel yaptık.
Yukarıdaki Örnekte LOCK nasıl oluyor gördük, bu Lock ları nasıl tespit ederiz kim kimi Lock yapıyor ona bakalım
Session A da bir delete işlemi yapalım ve commit etmeyelim yine ve Session A için Prosess ID PID değerine bakalım;
postgres=*# delete from mvcc_test where id=4; |
Session B da id=4 olan kaydı Update etmek isteyelim;
postgres=# select pg_backend_pid(); pg_backend_pid ---------------- 2148180(1 row) postgres=# update mvcc_test set name='Betus' where id=4; |
Lock oluştu ve B session ı, Session A nın bu satırlar üzerindeki işlemini bitirmesini bekliyor. Başka bir session üzerinden Lock durumunu kontrol edelim;
postgres=# select locktype, mode, granted, pid, pg_blocking_pids(pid) as blocked from pg_locks where relation='mvcc_test'::regclass; locktype | mode | granted | pid | blocked ----------+------------------+---------+---------+----------- relation | AccessShareLock | t | 2149241 | {} relation | RowExclusiveLock | t | 2149241 | {} relation | RowExclusiveLock | t | 2148180 | {2149241} tuple | ExclusiveLock | t | 2148180 | {2149241} (4 rows) ##Diğer Bir Sql İle Bakalım
pid | wait_event_type | wait_event | pg_blocking_pids ---------+-----------------+---------------+------------------ 2148180 | Lock | transactionid | {2149241} (1 row) |
A session commit ederek tekrar sorgulayalım;
Session A postgres=*# commit; COMMIT ##Diğer Sessionlardan Lock kontrolü yapalım postgres=# select locktype, mode, granted, pid, pg_blocking_pids(pid) as blocked from pg_locks where relation='mvcc_test'::regclass; locktype | mode | granted | pid | blocked ----------+------+---------+-----+--------- (0 rows) #################
postgres=# select pid,wait_event_type,wait_event,pg_blocking_pids(pid) from pg_stat_activity where wait_event_type='Lock'; pid | wait_event_type | wait_event | pg_blocking_pids -----+-----------------+------------+------------------ (0 rows) |
Lock sorguları DBA ler açısından önemlidir ve daha anlaşılır bir Lock sorgusunu view haline getirerek Lock kontrolünü view üzerinden yapabiliriz. Aşağıda örnek bir Lock view sorgusu verilmiştir.
create view my_lock_query as |
View ı sorgulayalım;
postgres=# select * from my_lock_query ; |
Lock konusu sadece update ve delete ten ibaret değildir, bir çok Lock çeşidi mevcuttur,, ayrıca Vacuum full işlemi , index create , reindex işlemleri de Lock a sebep olur ayrıca memory üzerinde oluşan bazı lock çeşitleri de vardır. Lock olan bir tablo vacuum full işlemi tamamlanamaz taaki tablo üzerindeki Lock kaldırılana kadar.
Deadlock
2 session birbirini blokladığında (Lock) Deadlock durumu oluşacaktır, deadlock tespit edildiğinde session lardan biri deadlock_timeout kadar bekledikten sonra otomatik olarak rollback olacaktır
Örnek
Session A Session B Session A Session B Session A
|
deadlock_timeout; Lock tespit edildikten sonra bu süre kadar beklenir ve deadlock bu süre sonunda session lardan biri rollback yapılarak giderilir. deadlock hataları alınıyorsa genelde uygulama tasarımının gözden geçirilmesi önerilmektedir.
lock_timeout; Varsayılan 0 dır yani kapalıdır, milisaniye cinsinden lock ların ne kadar bekleyeceğini belirtir bu süre sonunda blocknan transaction sonlandırılır.
log_lock_waits; Aktif edilmesi durumunda deadlock_timeout süresinden daha uzun süren sorgular log dosyasına yazılacaktır.
Bir yanıt yazın