VPN üzerinden psql zamanlaması: ağ beklemesini sorgu işinden ayırmak
psql her ifade için tek bir geçen süre bildirir, bu yüzden yavaş bir VPN bu değeri şişirir. Herhangi bir tarafı ayarlamadan önce bağlanma, yürütme, aktarma ve gidiş-dönüş sürelerini ayırın.
Raporunuz veritabanı ana bilgisayarında yaklaşık bir saniyede, VPN üzerinden ise dört saniyede bitiyor ve psql'in yazdırdığı sayı doğrudan veritabanını gösteriyor. Bu sayı istemci tarafındaki bir kronometredir, sunucu ölçümü değildir. İfadenin dışarı çıkış yolunu, yanıt beklemesini ve geri dönen satırları kapsar. Bu üçünü ayırmak, hangi tarafı ayarlayacağınızı bilmenin tek yoludur.
psql zamanlayıcısı gerçekte neyi ölçer
\timing on istemci ifadeyi sokete yazmadan hemen önce bir kronometre başlatır ve o sonucun son satırı okunduğunda durdurur. Sunucunun kendi yürütmesi o pencerenin içinde bir yerdedir; paketlerin tünelde geçirdiği her milisaniye de öyle. Çıktıda hiçbir şey ikisini ayırmaz. EXPLAIN de bir sayı yazdırır, ama onu sunucu üretir ve istemcinin beklemesini dışarıda bırakır. İki sayı, iki farklı soru.
Bir oturum başlatın, zamanlamayı açık bırakın ve ilgilendiğiniz ifadeyi onun içinden çalıştırın. Oturumu yeniden kullanmak bağlanma maliyetini ilk ölçümün dışında tutar.
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';"Sayı hâlâ sonucun aktarımını içeriyor, burada istenen de bu. Artık yeni bir çağrının ekleyeceği bağlanma ve kimlik doğrulama alışverişini içermiyor.
Bir ifadeyi üç yolda karşılaştırın
Aynı ifadeyi Unix soketi, LAN adresi ve VPN ana bilgisayar adı üzerinden çalıştırın ve üçünü karşılaştırın. Yalnızca sonuncusu yavaşsa, sorgu değişken değildir.
Her çağrı önce bağlanır ve ancak ondan sonra ifadeyi ölçer, bu yüzden bağlanma maliyeti sayının dışında kalırken aktarım içinde kalır. WHERE yan tümcesini, satır sayısını ve istemci makinesini aynı tutun, yoksa yolları değil iş yüklerini karşılaştırıyorsunuz.
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 fark genellikle göz ardı edilecek kadar küçüktür; VPN üzerinden çoğu zaman değildir. Üç sonucu sıralamak hangi beklemenin hedefleneceğini söyler.
Yavaş yerel soket, sunucu işi veya planlama demektir. Yavaş LAN sonucu, sorgu veya taşıdığı hacim demektir. Yalnızca VPN sonucunun yavaş olması gecikme, MTU veya yolda kullanılabilir bant genişliği demektir ve hiçbir dizin değişikliği bunu düzeltmez.
Sunucunun kendi yürütme süresini çıkarın
Sunucu ne kadar zaman harcadığını bildirebilir ve istemcinin sayısıyla karşılaştırılacak sayı odur. EXPLAIN ANALYZE ile ifadeyi çalıştırır ve plan içinde planlama ile yürütme süresini yazdırır; BUFFERS ise sıcak bir koşuyu soğuk olana karşı sıkça açıklayan önbellek davranışını ekler. Yazma niyetiniz yoksa onu yazma ifadelerine yöneltmeyin. Salt okunur bir replikada başlamak için en güvenli yer burasıdır ve psql başvurusu zamanlayıcının istemci tarafındaki kapsamını belgeler.
Uygulamanın zaten çalıştırdığı ifadeler için pg_stat_statements hiçbir şeyi yeniden çalıştırmadan sunucu tarafındaki yürütmeyi toplar. Satırlar normalleştirilmiş sorgu başınadır, bu yüzden bir girdi birkaç çağrı yerini kapsayabilir.
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ı sürüme göre değişir: PostgreSQL 13 ve sonrası mean_exec_time sunar, eski sürümler ise onu mean_time olarak adlandırır, bu yüzden şununla kontrol edin: \d pg_stat_statements, eğer o sorgu hata verirse. Bu değerler planlamayı ve istemcinize kadar olan yolculuğu dışarıda bırakır. Onları karşılaştırma için kullanışlı kılan tam da bu dışarıda bırakma.
Gidiş-dönüşleri sayın, yalnızca milisaniyeleri değil
İki yüz küçük bağımlı ifadeden oluşan bir rapor, yolun eklediği her gecikmeyi iki yüzle çarpar. Bir milyon satır döndüren tek bir ifade o gecikmeyi bir kez öder ve sonra uzun süre bant genişliği öder. İkisi de istemci zamanlayıcısında aynı görünür ve zıt düzeltmeler gerektirir.
İfade sayısını tahmin etmek yerine ölçün. Toplamı okuyun, raporu bir kez çalıştırın, sonra yeniden okuyun ve çıkarın.
select sum(calls) as calls from pg_stat_statements;
-- run the report here
select sum(calls) as calls from pg_stat_statements;Delta büyükse ve her ifade sunucuda ucuzsa, toplu işleme karşılığını veren düzeltmedir. Bağımlı sorguları birleştirin, küme tabanlı tek bir ifade kullanın veya döngüyü bir imleçten sürün. Etkileşimli psql'de, \set FETCH_COUNT 1000 büyük bir sonuç parçalar hâlinde gelir ve aktarımın ve istemci belleğinin davranışını değiştirir. Sunucu işini azaltmaz, bu yüzden yavaş bir sorguyu kurtarmaz.
Tüneli yeniden iletimler ve MTU duraklamaları için denetleyin
Küçük ifadeler hızlıyken büyük sonuçlar yolun ortasında duruyorsa, sorgu genellikle iyidir ve yol değildir. MTU'su alttaki bağlantıdan düşük bir tünel, engellenmiş ICMP ile birleştiğinde tam olarak o deseni üretir: ilk paketler gelir, büyükler kaybolur ve aktarım yeniden deneyene kadar asılı kalır. İstemci tarafındaki yeniden iletim sayaçları bunu gösterir.
Kasıtlı olarak yavaş bir aktarım sürerken soketi ve çekirdek sayaçlarını izleyin. Yeniden iletim toplamları birikimlidir, bu yüzden tek bir örnek okumak yerine önce ve sonra not alın.
ss -tin dst 10.40.0.12
netstat -s | grep -iE "retransmit|segments retransmitted"
ping -M do -s 1400 -c 3 10.40.0.12Aktarım sırasında artan yeniden iletimler, 1400 baytta başarılı olup 1472'de başarısız olan bir ping ile birlikte, veritabanı sorununa değil MTU sorununa işaret eder. Tünel arayüzünün MTU'sunu düşürmek veya ağ geçidinde MSS'yi sıkmak olağan düzeltmedir ve aynı üç komut bunu sonradan doğrular. Aktarım hâlâ yavaşken yeniden iletimler düz kalıyorsa, kayıp yerine tünele izin verilen bant genişliğine bakın.
Kısa bir dizi bir sonraki yavaş raporu kapsar: bir ifadeyi yerel olarak, LAN'da ve VPN üzerinden karşılaştırın; sunucu yürütmesini EXPLAIN veya pg_stat_statements'ten okuyun; bir çalıştırmadan önce ve sonra çağrıları sayın; ve büyük sonuç hareket ederken yeniden iletimleri izleyin. Her adım yolun bir tarafını dışarıda bırakır. Hiçbiri hangi bileşenin yeniden başlatılacağını tahmin etmeyi gerektirmez.