Hesabatınız verilənlər bazası hostunda təxminən bir saniyə, VPN üzərindən isə dörd saniyə bitir və psql-in çap etdiyi rəqəm birbaşa verilənlər bazasına işarə edir. Bu rəqəm müştəri tərəfindəki saniyəölçəndir, server ölçməsi deyil. O, ifadənin çölə çıxış yolunu, cavab gözləməsini və sətirlərin geri qayıtmasını əhatə edir. Bu üçünü ayırmaq hansı tərəfi tənzimləmək lazım olduğunu bilməyin yeganə yoludur.

psql taymeri əslində nəyi ölçür

\timing on müştəri ifadəni soketə yazmazdan az əvvəl saniyəölçən işə salır və həmin nəticənin son sətri oxunanda dayandırır. Serverin öz icrası bu pəncərənin haradasa içindədir və paketlərin tuneldə keçirdiyi hər millisaniyə də elə. Çıxışda heç nə onları ayırmır. EXPLAIN da rəqəm çap edir, lakin o rəqəmi server istehsal edir və müştərinin gözləməsini istisna edir. İki rəqəm, iki fərqli sual.

Bir sessiya başladın, vaxt ölçməni açıq saxlayın və maraqlandığınız ifadəni onun içindən icra edin. Sessiyanı təkrar istifadə etmək qoşulma xərcini ilk ölçmədən kənarda saxlayır.

psql "host=db.internal port=5432 dbname=appdb user=app" \
  -c "\timing on" \
  -c "select count(*) from orders where created_at > now() - interval '1 day';"

Rəqəm hələ də nəticənin ötürülməsini əhatə edir, burada istədiyiniz də elə budur. O, artıq yeni çağırışın əlavə edəcəyi qoşulma və autentifikasiya mübadiləsini əhatə etmir.

Bir ifadəni üç yol üzrə müqayisə edin

Eyni ifadəni Unix soketi, LAN ünvanı və VPN host adı üzərindən icra edin və üçünü müqayisə edin. Yalnız sonuncu yavaşdırsa, sorğu dəyişən deyil.

Hər çağırış əvvəlcə qoşulur və yalnız sonra ifadəni ölçür, buna görə qoşulma xərci rəqəmdən kənarda qalır, ötürmə isə içində qalır. WHERE bəndini, sətir sayını və müştəri maşınını eyni saxlayın, əks halda yolları deyil, iş yüklərini müqayisə edirsiniz.

psql -h /var/run/postgresql -U app -d appdb -c "\timing on" -c "select count(*) from orders;"
psql -h 10.40.0.12 -U app -d appdb -c "\timing on" -c "select count(*) from orders;"
psql -h db.vpn.internal -U app -d appdb -c "\timing on" -c "select count(*) from orders;"

LAN-da fərq adətən nəzərə alınmayacaq qədər kiçikdir; VPN üzərində isə çox vaxt belə deyil. Üç nəticəni sıralamaq sonra hansı gözləməyə hücum edəcəyinizi deyir.

Yavaş yerli soket server işi və ya planlaşdırma deməkdir. Yavaş LAN nəticəsi sorğu və ya onun daşıdığı həcm deməkdir. Yalnız VPN nəticəsinin yavaş olması yol üzrə gecikmə, MTU və ya mövcud bant genişliyi deməkdir və heç bir indeks dəyişikliyi bunu düzəltmir.

Serverin öz icra vaxtını çıxarın

Server nə qədər vaxt sərf etdiyini bildirə bilər və müştərinin rəqəmi ilə tutuşdurulacaq rəqəm elə budur. ANALYZE ilə EXPLAIN ifadəni icra edir və planın içində planlaşdırma və icra vaxtını çap edir, BUFFERS isə tez-tez isti işləməni soyuq işləmədən ayıran keş davranışını əlavə edir. Yazma ifadələrinə yönəltməyin, yazmaq niyyətiniz yoxdursa. Yalnız oxuna bilən replikada başlamaq üçün ən təhlükəsiz yerdir və psql istinadı taymerin müştəri tərəfli əhatəsini sənədləşdirir.

Tətbiqin artıq icra etdiyi ifadələr üçün pg_stat_statements heç nəyi yenidən işə salmadan server tərəfli icranı toplayır. Sətirlər normallaşdırılmış sorğu üzrədir, buna görə bir giriş bir neçə çağırış yerini əhatə edə bilər.

select calls, round(mean_exec_time::numeric, 2) as mean_ms, rows
from pg_stat_statements
order by mean_exec_time desc
limit 5;

Sütun adları buraxılışa görə dəyişir: PostgreSQL 13 və sonrası mean_exec_time göstərir, köhnə versiyalar isə onu mean_time adlandırır, ona görə \d pg_stat_statements ilə yoxlayın, əgər həmin sorğu xəta verirsə. Bu dəyərlər planlaşdırmanı və müştəriyə səfəri istisna edir. Məhz bu istisna onları müqayisə üçün faydalı edir.

Gediş-dönüş sayını sayın, təkcə millisaniyələri deyil

İki yüz kiçik asılı ifadədən qurulmuş hesabat yolun əlavə etdiyi gecikməni iki yüzə vurur. Bir milyon sətir qaytaran tək ifadə həmin gecikməni bir dəfə ödəyir və sonra uzun müddət bant genişliyi ödəyir. İkisi də müştəri taymerində eyni görünür və onlara əks düzəlişlər lazımdır.

İfadə sayını təxmin etmək əvəzinə ölçün. Cəmi oxuyun, hesabatı bir dəfə işə salın, sonra yenidən oxuyun və çıxın.

select sum(calls) as calls from pg_stat_statements;
-- run the report here
select sum(calls) as calls from pg_stat_statements;

Fərq böyükdürsə və hər ifadə serverdə ucuzdursa, toplama ödəyən düzəlişdir. Asılı sorğuları birləşdirin, çoxluq əsaslı tək ifadə istifadə edin və ya döngəni kursorla idarə edin. İnteraktiv psql-də, \set FETCH_COUNT 1000 böyük nəticənin partiyalarla gəlməsini təmin edir və ötürmə ilə müştəri yaddaşının davranışını dəyişir. Server işini azaltmır, buna görə yavaş sorğunu xilas etməyəcək.

Tuneli yenidən ötürmələr və MTU dayanmaları üçün yoxlayın

Kiçik ifadələr sürətli, böyük nəticələr isə yarı yolda dayanırsa, sorğu adətən qaydasındadır, yol isə deyil. MTU-su əsas kanaldan aşağı olan tunel, bloklanmış ICMP ilə birlikdə məhz bu nümunəni yaradır: ilk paketlər gəlir, böyüklər yox olur və ötürmə yenidən cəhd edənə qədər asılı qalır. Müştəri tərəfli yenidən ötürmə sayğacları bunu göstərir.

Qəsdən yavaş ötürmə işləyərkən soketi və nüvə sayğaclarını izləyin. Yenidən ötürmə cəmləri kumulyativdir, buna görə bir nümunə oxumaq əvəzinə əvvəl və sonra qeyd edin.

ss -tin dst 10.40.0.12
netstat -s | grep -iE "retransmit|segments retransmitted"
ping -M do -s 1400 -c 3 10.40.0.12

Ötürmə zamanı artan yenidən ötürmələr, 1400 baytda uğurlu olub 1472-də uğursuz olan ping ilə birlikdə verilənlər bazası problemi deyil, MTU problemi göstərir. Tunel interfeysinin MTU-nu aşağı salmaq və ya şlüzda MSS-i sıxmaq adətən düzəlişdir və eyni üç əmr bunu sonra təsdiqləyir. Ötürmə hələ də yavaş ikən yenidən ötürmələr sabit qalırsa, itkiyə deyil, tunelə icazə verilən bant genişliyinə baxın.

Qısa ardıcıllıq növbəti yavaş hesabatı əhatə edir: bir ifadəni yerli, LAN və VPN üzərindən müqayisə edin; server icrasını EXPLAIN və ya pg_stat_statements-dən oxuyun; tək işə salmadan əvvəl və sonra çağırışları sayın; böyük nəticə hərəkət edərkən yenidən ötürmələri izləyin. Hər addım yolun bir tərəfini istisna edir. Heç biri hansı komponenti yenidən başlatmağı təxmin etməyi tələb etmir.

Əlaqəli məqalələr