CodeGym /Kurslar /SQL SELF /Bloklanmalar və konfliktlərin monitorinqi

Bloklanmalar və konfliktlərin monitorinqi

SQL SELF
Səviyyə , Dərs
Mövcuddur

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:

  1. Yazma əməliyyatları zamanı: UPDATE, DELETE, INSERT.
  2. Resursu lazım olandan artıq saxlayan tranzaksiyalardan istifadə edəndə.
  3. 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 = false olan qeydləri filtrləyirik, yəni bloklanma hələ verilməyib.
  • relation::regclass cə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_activity view-sundan istifadə edirik ki, sistemdə aktiv prosesləri çıxaraq.
  • pg_blocking_pids(pid) hər pid üçü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 12345678 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 studentsenrollments cədvəlləri var. Bir neçə tranzaksiya eyni anda enrollments cədvəlinə yazmağa çalışır və bloklanmalarla qarşılaşırsan.

  1. Bloklanmaların identifikasiyası:
SELECT pid, locktype, relation::regclass AS table_name, mode, granted
FROM pg_locks
WHERE NOT granted;
  1. 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;
  1. 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.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION