Təsəvvür elə, ofisdə işləyirsən və bütün qapılar bir nəfərin açarları otaqda unutmasına görə bağlanıb. PostgreSQL-də bloklanmalar da eynilə belə işləyir. Əgər bir sorğu və ya tranzaksiya resursu bloklayıbsa, həmin resursa çıxış almaq istəyən digər əməliyyatlar onların bitməsini gözləməli olur. Bu vəziyyət gecikmələrə, konfliktli ssenarilərə və ən pis halda sistemin dayanmasına səbəb ola bilər.
Bloklanmalar nə vaxt yaranır?
PostgreSQL-də bloklanmalar (Locks) verilənlərə eyni vaxtda çıxışı idarə etmək üçün istifadə olunur. Onlar aşağıdakı hallarda yaranır:
- Yazma əməliyyatları zamanı:
UPDATE,DELETE,INSERT. - Resursu lazım olandan artıq saxlayan tranzaksiyalardan istifadə edəndə.
- Eyni resursa iddia edən müxtəlif tranzaksiyalar arasında konfliktlər olanda.
Real verilənlər bazası — resurslar uğrunda "döyüş meydanı"dır və səncə sistemin ideal işlədiyini düşünsən belə, bir ehtiyatsız tranzaksiya hər şeyi "tormozlaya" bilər, elə bil Git-də uğursuz merge etmisən.
Bloklanmaların analizi üçün alətlər: pg_locks
pg_locks — PostgreSQL-in sistem view-sudur, hazırda tranzaksiyalar tərəfindən saxlanılan və gözlənilən bloklanmaları göstərir. O, belə bir suala cavab verir: "Kim bloklanmanı saxlayır və kim gözləyir?"
pg_locks-un əsas sahələri:
locktype: bloklanma tipi (məsələn,relation,transaction,page,tuple).database: verilənlər bazasının identifikatoru.relation: cədvəlin identifikatoru (əgər bloklanma cədvəllə bağlıdırsa).mode: bloklanma rejimi (məsələn,RowExclusiveLock,AccessShareLock).granted: bloklanmanın verilib-verilmədiyini göstərən flag (true) və ya tranzaksiya hələ də onu gözləyir (false).
Qeyd: PostgreSQL-də "hierarxik bloklanma rejimi" tətbiq olunur. Bu o deməkdir ki, müxtəlif əməliyyatlar daha zəif bloklanma (məsələn, AccessShareLock oxumaq üçün) və ya daha sərt bloklanma (ExclusiveLock cədvəl strukturunu dəyişmək üçün) qoyur.
Nümunə: bütün cari bloklanmalara baxış
SELECT *
FROM pg_locks;
Amma əgər sadəcə pg_locks-un bütün məzmununu çıxarsan, çoxlu "səs-küy" olacaq. Gəlin, daha mənalı bir şey yoxlayaq!
Nümunə: hələ verilməmiş bloklanmalar (yəni tranzaksiyalar gözləyir)
SELECT pid, locktype, relation::regclass AS table_name, mode, granted
FROM pg_locks
WHERE NOT granted;
Burada nə baş verir?
granted = falseolan qeydləri filtrləyirik, yəni bloklanma hələ verilməyib.relation::regclasscədvəl identifikatorunu oxunaqlı ad formasına çevirir.
Nəticə təxminən belə görünə bilər:
| pid | locktype | table_name | mode | granted |
|---|---|---|---|---|
| 1234 | relation | students | RowExclusiveLock | false |
| 4321 | relation | courses | RowShareLock | false |
Bu sorğular sənə hansı cədvəlin/resursun bloklandığını və hansı tranzaksiyanın səbəb olduğunu tapmağa kömək edəcək.
Konfliktlərin analizi: pg_blocking_pids()
Bloklanmalar — hələ işin yarısıdır, bəs bir tranzaksiya digərini bloklayanda nə etməli? PostgreSQL "günahkarı" tapmaq üçün rahat bir yol təklif edir — pg_blocking_pids() funksiyası.
pg_blocking_pids() funksiyası cari tranzaksiyanı bloklayan proseslərin (pid) siyahısını qaytarır.
Nümunə: digər tranzaksiyaları bloklayanları tapmaq
SELECT pid, pg_blocking_pids(pid) AS blocking_pids
FROM pg_stat_activity
WHERE cardinality(pg_blocking_pids(pid)) > 0;
Burada nə baş verir?
pg_stat_activityview-sundan istifadə edirik ki, sistemdə aktiv prosesləri çıxaraq.pg_blocking_pids(pid)hərpidüçün bloklayan proseslərin siyahısını qaytarır. Siyahı boş deyilsə (uzunluğu 0-dan böyükdürsə), deməli proses bloklanıb.
Nümunə nəticə:
| pid | blocking_pids |
|---|---|
| 4567 | {1234, 5678} |
| 6789 | {4321} |
pid = 4567 olan tranzaksiya 1234 və 5678 prosesləri tərəfindən bloklanıb. "Günahkarlarımızı" tapdıq.
Bloklayan proseslərin sonlandırılması
Bloklayan prosesləri tapdıqdan sonra onları pg_terminate_backend() funksiyası ilə dayandıra bilərsən:
SELECT pg_terminate_backend(1234); -- 1234 prosesini "öldürürük"
Amma ehtiyatlı ol! Prosesi zorla dayandırmaq cari tranzaksiyada məlumatların rollback olmasına səbəb ola bilər. Bu aləti "nüvə düyməsi" kimi yalnız ən son halda istifadə et.
Praktik tətbiq: bloklanmaların analizi ssenarisi
Təsəvvür elə, universitet üçün verilənlər bazan var və orada students və enrollments cədvəlləri var. Bir neçə tranzaksiya eyni anda enrollments cədvəlinə yazmağa çalışır və bloklanmalarla qarşılaşırsan.
- Bloklanmaların identifikasiyası:
SELECT pid, locktype, relation::regclass AS table_name, mode, granted
FROM pg_locks
WHERE NOT granted;
- Bloklayan proseslərin müəyyənləşdirilməsi:
SELECT pid, pg_blocking_pids(pid) AS blocking_pids
FROM pg_stat_activity
WHERE cardinality(pg_blocking_pids(pid)) > 0;
- Bloklanmaların aradan qaldırılması:
Konflikt yaradan proseslərdən birini məcburi sonlandırırıq:
SELECT pg_terminate_backend(1234); -- 1234 prosesini sonlandırırıq
Qeyd: Prosesi "öldürməzdən" əvvəl bloklanmanın səbəbini araşdırmağa çalış. Ola bilər ki, tranzaksiya məntiqini dəyişmək daha düzgün olar.
Tipik səhvlər və onların qarşısının alınması
Bloklanmalar çox vaxt tranzaksiyaların düzgün idarə olunmamasından yaranır. Məsələn:
Səhv: bir tranzaksiya çox uzun müddət bloklanmanı saxlayır, heç bir əməliyyat aparmır ("idle in transaction" vəziyyəti).
Həll: pg_stat_activity ilə tranzaksiyaların vəziyyətinə diqqət et və "asılı qalmış" tranzaksiyaları sonlandır.
SELECT pid, state, query
FROM pg_stat_activity
WHERE state = 'idle in transaction';
Səhv: sorğularda indekslərdən istifadə etməyi unutmusan və nəticədə bütün cədvəl səviyyəsində bloklanmalar yaranıb.
Həll: Sorğuları optimallaşdır, tez-tez istifadə olunan şərtlər üçün indekslər əlavə et.
"Kim kimi gözləyir" cədvəlinin çıxarılması
Diaqnostikanı rahatlaşdırmaq üçün, hansı tranzaksiyanın hansını blokladığını göstərən asılılıq ağacı qura bilərsən:
WITH RECURSIVE blocking_tree AS (
SELECT pid, pg_blocking_pids(pid) AS blocked_by
FROM pg_stat_activity
WHERE cardinality(pg_blocking_pids(pid)) > 0
UNION ALL
SELECT a.pid, pg_blocking_pids(a.pid)
FROM pg_stat_activity a
JOIN blocking_tree b ON a.pid = ANY(b.blocked_by)
)
SELECT pid, blocked_by FROM blocking_tree;
Nəticə:
| pid | blocked_by |
|---|---|
| 4567 | {1234} |
| 1234 | {5678} |
| 5678 | {} |
Burada görünür ki, 5678 prosesi 1234-ü bloklayır, o isə öz növbəsində 4567-ni bloklayır.
GO TO FULL VERSION