Kilit beklemesi yavaş sorgudan farklıdır
Bir sorgu kendi başına yavaş olabilir; çok satır okur, kötü indeks kullanır veya diskten veri çeker. Kilit beklemesi ise başka bir işlemin kapıyı tutmasıdır. Sorgunuz aslında hızlı çalışacakken sırada bekler.
Bu ayrımı yapmadan indeks eklemek, makine büyütmek veya bağlantı sayısını artırmak çoğu zaman işe yaramaz. Önce sorgu CPU mu yakıyor, yoksa başka bir transaction yüzünden mi bekliyor bunu anlamak gerekir.
- Uzun transaction satır veya tablo kilidini gereğinden uzun tutabilir.
- idle in transaction, uygulamanın transaction açıp işi bitirmeden beklediğini gösterir.
- Deadlock, iki işlemin birbirini beklemesi ve PostgreSQL’in birini iptal etmesidir.
- Migration veya toplu veri düzeltme işleri kilit etkisini büyütebilir.
Bekleyen ve bekleten oturumu bulun
İlk bakılacak yer pg_stat_activity ve pg_locks çıktısıdır. Ama amaç sadece uzun süren sorguyu görmek değildir; kimin beklediğini ve kimin kilidi tuttuğunu aynı ekranda yakalamaktır.
Aşağıdaki sorgu canlı ortamda dikkatle kullanılabilecek bir başlangıçtır. Size bekleyen oturumun sorgusunu, ne kadar süredir beklediğini ve onu engelleyen oturum kimliğini gösterir.
PostgreSQL kilit bekleme teşhisi
SELECT blocked.pid AS blocked_pid, blocked.usename AS blocked_user, now() - blocked.query_start AS blocked_for, blocked.query AS blocked_query, blocking.pid AS blocking_pid, blocking.usename AS blocking_user, now() - blocking.query_start AS blocking_for, blocking.query AS blocking_query FROM pg_stat_activity blocked JOIN pg_locks blocked_locks ON blocked_locks.pid = blocked.pid JOIN 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.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid AND blocking_locks.pid <> blocked_locks.pid JOIN pg_stat_activity blocking ON blocking.pid = blocking_locks.pid WHERE NOT blocked_locks.granted ORDER BY blocked_for DESC;
idle in transaction genellikle uygulama davranışıdır
idle in transaction gördüğünüzde veritabanını suçlamadan önce uygulama koduna bakın. Transaction açılmış, bir sorgu çalışmış, sonra kod dış servis çağrısı, dosya işlemi veya kullanıcıdan cevap bekleme gibi veritabanı dışı bir işte kalmış olabilir.
Bu sırada PostgreSQL transactionın tuttuğu kilitleri bırakamaz. En kötü durumda küçük bir kod ihmali, bütün ödeme veya stok güncelleme akışını bekletebilir.
Uzun süren açık transactionları bulma
SELECT pid, usename, application_name, client_addr, state, now() - xact_start AS transaction_age, query FROM pg_stat_activity WHERE xact_start IS NOT NULL AND now() - xact_start > INTERVAL '2 minutes' ORDER BY transaction_age DESC;
Ne zaman oturum sonlandırılır?
Kilit tutan oturumu hemen öldürmek cazip gelir ama bu karar kör verilmemeli. Oturum bir migration, ödeme yazımı veya stok azaltma işlemi olabilir. Önce kullanıcı, uygulama adı, sorgu metni ve transaction süresi okunmalıdır.
Sonlandırma kararı gerekiyorsa bunu olay notuna yazın: hangi pid, hangi sorgu, neden sonlandırıldı, kullanıcı etkisi neydi? Böylece aynı sorun tekrarlandığında yalnızca kahramanlık hikâyesi değil, öğrenilmiş bir karar kalır.
Son çare olarak oturum sonlandırma
-- Önce blocking_pid değerini ve sorgu bağlamını doğrulayın. SELECT pg_terminate_backend(12345);
Bu komut veri tutarlılığı ve uygulama davranışı açısından riskli olabilir. Önce sorgu bağlamını ve geri dönüş etkisini anlayın.