Lock (Kilit/Blok)

Kilitler tüm tablonun ya da satırların değiştirilmesini engeller ve sadece okumaya
(select) izin verir.
update ve delete işlemleri satırı kilitli hale getirir ve başka bir oturumun (session) o
satırları değiştirmesine engel olur. rollback ya da commit edilince kilit açılır.
Satırlar kilitli iken okuma (select) işlemleri engellenmez (MVCC konusundan bahsi
geçmişti ).
Lock işlemleri otomatik olarak gerçekleşir ama gerek olursa manuel tetiklemek
mümkündür. Böylece istenen tablo ya da satır değişikliğe kapatılabilir.

Örnek olarak;

Adım 1

# Tablo oluşturulur.
testdb=# CREATE TABLE ekip(
TKN integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
--Kurumsal kimlik no
NO text UNIQUE,
isim text,
maas numeric
);

# Test için 3 satır veri girişi yapılır.
testdb=# INSERT INTO ekip VALUES
(1, '1001', 'Mehmed', 1000.00),
(2, '2001', 'Ahmed', 100.00),
(3, '2002', 'Ahmed', 900.00);

Adım 2

# Session A
testdb=# SELECT pg_backend_pid();
pg_backend_pid
----------------
4793
testdb=*# UPDATE ekip SET maas = maas + 200 WHERE TKN = 1;
# Backend prosess ID’si 4793 olan oturum yukarıdaki update
cümlesi ile TKN=1 olan satırı kitlemiş.

testdb=# SELECT locktype, mode, granted, pid, pg_blocking_
pids(pid) AS wait_for FROM pg_locks WHERE relation =
'ekip'::regclass;
locktype | mode | granted | pid | wait_for
----------+------------------+---------+------+----------
relation | RowExclusiveLock | t | 4793 | {}
# rollback ya da commit edilmedi.

Adım 3

testdb=# SELECT pg_backend_pid();
pg_backend_pid
----------------
4798
testdb=*# UPDATE ekip SET maas = maas + 125 WHERE TKN = 1;
# Aynı satır güncellenmeye çalışıldı. Session A’nın kilidi
henüz kalkmadığı için. Cursor askıda/beklemede kalır.

testdb=# SELECT locktype, mode, granted, pid, pg_blocking_
pids(pid) AS wait_for FROM pg_locks WHERE relation =
'ekip'::regclass;
locktype | mode             | granted | pid | wait_for
----------+------------------+---------+------+----------
relation | RowExclusiveLock | t       | 4793 | {}
relation | RowExclusiveLock | t       | 4798 | {4793}
tuple    | ExclusiveLock    | t       | 4798 | {4793}

# 4798 oturumu 4793 oturumunu beklemektedir.

Diğer izleme yöntemi;

testdb=# SELECT pid, wait_event_type, wait_event, pg_blocking_
pids(pid)
testdb-# FROM pg_stat_activity
testdb-# WHERE backend_type = ‘client backend’;
pid  | wait_event_type | wait_event    | pg_blocking_pids
------+-----------------+---------------+------------------
1586 | Client          | ClientRead    | {}
4793 | Client          | ClientRead    | {}
1587 | Client          | ClientRead    | {}
1588 | Client          | ClientRead    | {}
4798 | Lock            | transactionid | {4793}
4805 |                 |               | {}
5511 | Client          | ClientRead    | {}
(7 rows)

Daha anlaşılır bir çıktı için aşağıdaki sorgu kullanılabilir. Bu sorgu ile view oluşturarak
daha kullanışlı hale getirilebilir. Bloklayan bloklanan PID ve SQL bilgilerini görmek
mümkün.

testdb=# Create view as kilit
SELECT blocked_locks.pid AS blocked_pid,
blocked_activity.usename AS blocked_user,
blocking_locks.pid AS blocking_pid,
blocking_activity.usename AS blocking_user,
blocked_activity.query AS blocked_statement,
blocking_activity.query AS current_statement_in_blocking_
process
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_
activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks
ON blocking_locks.locktype = blocked_locks.locktype
AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.
database
AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.
relation
AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.
tuple
AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_
locks.virtualxid
AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_
locks.transactionid
AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.
classid
AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.
objid
AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.
objsubid
AND blocking_locks.pid != blocked_locks.pid
JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_
activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.granted;

testdb=# select * from kilit ;
-------------------------+---------------------------------------------
blocked_pid   | 1021
blocked_user  | postgres
blocking_pid  | 800
blocking_user | postgres
blocked_statement | UPDATE ekip SET maas = maas + 200 WHERE TKN = 2;
current_statement_in_blocking_process | UPDATE ekip SET maas = maas + 300 WHERE TKN = 2;

testdb=# SELECT blocked_locks.pid AS blocked_pid,
blocked_activity.usename AS blocked_user,
blocking_locks.pid AS blocking_pid,
blocking_activity.usename AS blocking_user,
blocked_activity.query AS blocked_statement,
blocking_activity.query AS current_statement_in_blocking_
process,
blocked_activity.application_name AS blocked_application,
blocking_activity.application_name AS blocking_application
FROM pg_catalog.pg_locks blocked_locks
JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_
activity.pid = blocked_locks.pid
JOIN pg_catalog.pg_locks blocking_locks
ON blocking_locks.locktype = blocked_locks.locktype
AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.
DATABASE
AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.
relation
AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.
tuple
AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_
locks.virtualxid
AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_
locks.transactionid
AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.
classid
AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.
objid
AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.
objsubid
AND blocking_locks.pid != blocked_locks.pid
JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_
activity.pid = blocking_locks.pid
WHERE NOT blocked_locks.GRANTED;
---------------+----------------------------------------------
blocked_pid | 1021
blocked_user | postgres
blocking_pid | 800
blocking_user | postgres
blocked_statement | UPDATE ekip SET maas = maas + 200 WHERE TKN = 2;
current_statement_in_blocking_process | UPDATE ekip SET maas =
maas + 300 WHERE TKN = 2;
blocked_application | psql
blocking_application | psql

Manuel kilit (block/lock) tetikleme

Kilit işlemi sadece update ve delete işlemleri ile gerçekleşmez. for update ve
lock table komutları ile manuel olarak kilit işlemi gerçekleştirilebilir

SELECT xmin,xmax, txid_current(), * FROM ekip FOR UPDATE;
xmin | xmax | txid_current | tkn | no | isim | maas
-------+-------+--------------+-----+------+--------+---------
46015 | 46035 | 46037 | 2 | 2001 | Ahmed | 100.00
46015 | 46036 | 46037 | 3 | 2002 | Ahmed | 900.00
46034 | 46035 | 46037 | 1 | 1001 | Mehmed | 1500.00

SELECT locktype, transactionid, virtualtransaction, pid,mode,
granted, fastpath FROM pg_locks WHERE transactionid=46037 ;
locktype|transactionid|virtualtransaction|pid|mode | granted | fastpath
---------------+---------------+--------------------+-----+----
transactionid | 46037 | 3/9 | 800 | ExclusiveLock | t | f
(1 row)
# where koşulu ile de kullanmak mümkün.
SELECT * FROM tablo_ismi WHERE kolon_ismi=abcd FOR UPDATE;

BEGIN; LOCK TABLE tablo_ismi; -- “VACUUM FULL” ile aynı modda kilitler.

Not: ACCESS SHARE, ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE
EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, ACCESS
EXCLUSIVE
diğer lock modları için detaylara postgresql.org/docs/current/index.html
sitesinden erişilebilir.

vacuum full, index create/rebuild işlemleri de lock’a neden olur.
Örneğin lock olan bir tabloda vacuum full başlatılırsa, devam edebilmesi için
lock’ın kalkması gerekir.
unlock table gibi sadece lock işlemini sonlandırmak için bir komut yoktur.
Ya commit edildiğinde ya da vacuum vb. işlem tamamlandığında/sonlandırıldığında lock
kalkar.

Deadlock

2 oturum birbirini kilitlediğinde deadlock oluşur.
Her iki otutumda da otomatik commit kapalı iken \set AUTOCOMMIT off

# Session A
testdb=# UPDATE ekip SET maas = maas + 200 WHERE TKN = 1;
# Session B
testdb=# UPDATE ekip SET maas = maas + 200 WHERE TKN = 2;
# Session A
testdb=# UPDATE ekip SET maas = maas + 300 WHERE TKN = 2;
# Session B
testdb=# UPDATE ekip SET maas = maas + 100 WHERE TKN = 1;
ERROR: deadlock detected
DETAIL: Process 1021 waits for ShareLock on transaction 46040;
blocked by process 800.
Process 800 waits for ShareLock on transaction 46041; blocked
by process 1021.
HINT: See server log for query details.
CONTEXT: while updating tuple (0,28) in relation "ekip"

$PGDATA/log/postgresql_%a.log dosyasına düşen loglar.
ERROR: deadlock detected
user=postgres db=testdb pid=1021 line=3 trx=46041|DETAIL: Process 1021 waits for ShareLock
on transaction 46040; blocked by process 800.
Process 800 waits for ShareLock on transaction 46041; blocked by process 1021.
Process 1021: UPDATE ekip SET maas = maas + 100 WHERE TKN = 1;
Process 800: UPDATE ekip SET maas = maas + 300 WHERE TKN = 2;
HINT: See server log for query details.
user=postgres db=testdb pid=1021 line=5 trx=46041|CONTEXT: while updating tuple (0,28) in
relation “ekip”
user=postgres db=testdb pid=1021 line=6 trx=46041|STATEMENT: UPDATE ekip SET maas =
maas + 100 WHERE TKN = 1;

deadlock_timeout : Bir lock gerçekleştiğinde burada belirtilen süre kadar
bekledikten sonra deadlock olup olmadığını kontrol eder. Deadlock tespit edildiğinde
otomatik olarak rollback edilir.
Deadlock uygulama kurgusundan kaynaklanmaktadır. Fazla sayıda deadlock
performansı ve belki de uygulamanın işleyişini etkileyebilir. Bu yüzden tespit edilen
deadock’lar yazılım geliştiricilere bildirilmelidir.

lock_timeout : Max time out süresini milisecond cinsinden belirler.
Default değeri 0’dır yani kapalıdır.
Örneğin 10.000 olarak ayarlanırsa,
lock’lanan transaction 10 saniye sonra sonlandırılır
ve satırı lock’layan commit ya da rollback yapana kadar tekrar çalıştırmasına izin
verilmez.

log_lock_waits : Aktif edilirse deadlock_timeout’ta belirtilen süreden uzun
süren lock’ları log dosyasına yazar.
Bir lock gerçekleştiğinde uygulama beklemeye geçer ve ekranda kum saati belirir.
Kullanıcı, uygulamanın geç cevap verdiğini dolayısı ile performans sorunu olduğunu
düşünecektir. Yapılması gerken, lock’ları tespit edip uygulamacılarara raporlayıp gerekli
yerlere commit veya belirli bir süreden sonra rollback ekletmektir.

Kategori seçin...