KoderKoder.ai
FiyatlandırmaKurumsalEğitimYatırımcılar için
Giriş YapBaşla

Ürün

FiyatlandırmaKurumsalYatırımcılar için

Kaynaklar

Bize UlaşınDestekEğitimBlog

Yasal

Gizlilik PolitikasıKullanım KoşullarıGüvenlikKabul Edilebilir Kullanım PolitikasıKötüye Kullanımı Bildir

Sosyal

LinkedInTwitter
Koder.ai
Dil

© 2026 Koder.ai. Tüm hakları saklıdır.

Ana Sayfa›Blog›Go + Postgres performans ayarları: odaklı bir API oyun planı
14 Ara 2025·7 dk

Go + Postgres performans ayarları: odaklı bir API oyun planı

AI tarafından üretilen API'ler için Go + Postgres performans kılavuzu: bağlantı havuzları, EXPLAIN ile sorgu planları, doğru indeksler, güvenli sayfalama ve hızlı JSON şekillendirme.

Go + Postgres performans ayarları: odaklı bir API oyun planı

Go API'lerinde Postgres ile "yavaş" nasıl görünür

AI tarafından üretilen API'ler başlangıç testlerinde hızlı gelebilir. Bir endpoint'e birkaç kez istek yaparsınız, veri seti küçüktür ve istekler tek tek gelir. Gerçek trafik gelince: karışık endpoint'ler, ani yük artışları, daha soğuk önbellekler ve beklenenden fazla satır olur. Aynı kod rastgele yavaşlamaya başlar, sanki bir şey kırılmış gibi değil ama deneyim bozulur.

Yavaşlık genellikle birkaç şekilde kendini gösterir: gecikme zirveleri (çoğu istek iyi, bazıları 5x–50x daha uzun sürer), zaman aşımı (küçük bir yüzdelik hata verir) veya CPU yüksek çalışması (Postgres sorgu CPU'su veya Go CPU'su JSON, gorutinler, logging ve yeniden denemelerden dolayı).

Yaygın bir senaryo, esnek filtreli bir liste endpoint'idir ve büyük bir JSON yanıtı döner. Test veritabanında birkaç bin satır taranır ve hızlı biter. Prodüksiyonda ise birkaç milyon satır taranır, bunlar sıralanır ve ancak sonra LIMIT uygulanır. API hâlâ "çalışır", ama p95 gecikmesi patlar ve bazı istekler ani artışlarda zaman aşımına uğrar.

Veritabanı yavaşlığını uygulama yavaşlığından ayırmak için zihniyeti basit tutun.

Veritabanı yavaşsa, Go handler'ınız zamanının çoğunu sorgunun bitmesini bekleyerek geçirir. Birçok isteğin "uçuşta" sıkıştığını ve Go CPU'nun normal göründüğünü görebilirsiniz.

Uygulama yavaşsa, sorgu hızlı biter ama sorgudan sonra zaman harcanır: büyük cevap nesneleri oluşturma, JSON marshal etme, satır başına ekstra sorgular veya istekte çok fazla işlem yapma. Go CPU ve bellek artar, ve gecikme yanıt boyutuyla birlikte büyür.

Lansman öncesi için "yeterince iyi" performans mükemmel olmak zorunda değil. Birçok CRUD endpoint'i için hedef: stabil p95 gecikmesi (ortalama değil), ani yüklerde öngörülebilir davranış ve beklenen zirvede zaman aşımı olmaması. Amaç basit: veri ve trafik arttığında sürpriz yavaş istekler olmasın ve bir şey saptığında net sinyaller olsun.

Önce temel: önemli birkaç sayı

Herhangi bir ayar yapmadan önce "iyi"nin ne olduğunu kararlaştırın. Bir temel olmadan, ayar değiştirip iyileşme olup olmadığını veya darboğazı sadece başka yana taşıyıp taşımadığınızı bilmeden saatler harcayabilirsiniz.

Genellikle hikâyeyi anlatan üç sayı vardır:

  • p95 istek gecikmesi (ortalama değil)
  • hata oranı (HTTP 5xx, zaman aşımı, iptal edilen istekler)
  • isteğe düşen DB zamanı (her isteğin Postgres'te ne kadar beklediği)

p95 "kötü gün" metriğidir. Eğer p95 yüksek ama ortalama iyiyse, küçük bir istek grubu çok fazla iş yapıyor, kilitleniyor veya yavaş planlar tetikliyor demektir.

Yavaş sorguları erken görünür kılın. Postgres'te, lansman öncesi test için düşük bir eşik ile slow query logging'i açın (ör. 100–200 ms) ve tam ifadeyi loglayın ki SQL istemcisine kopyalayabilesiniz. Bu geçici olsun. Üretimde her yavaş sorguyu loglamak hızla gürültü oluşturur.

Sonra, tek bir "hello world" rotası yerine gerçeğe yakın isteklerle test edin. Küçük bir set yeterlidir, yeter ki kullanıcıların yapacağı şeylere uysun: filtreli ve sıralamalı bir liste çağrısı, birkaç JOIN içeren detay sayfası, doğrulama ile create/update ve kısmi eşleşmeli arama tarzı sorgu.

Eğer endpoint'leri bir şemadan üretiyorsanız (ör. Koder.ai gibi bir araçla), aynı tutarlı girdilerle aynı birkaç isteği tekrar çalıştırın. Bu, indeksler, sayfalama ayarları ve sorgu yeniden yazımlarının ölçülmesini kolaylaştırır.

Son olarak, yüksek sesle söyleyebileceğiniz bir hedef seçin. Örnek: "Çoğu istek 50 eşzamanlı kullanıcıda p95 altında 200 ms kalsın ve hatalar %0.5'in altında olsun." Kesin rakam ürününüze bağlıdır, ama net bir hedef sonsuz uğraşmayı engeller.

Postgres'i stabil tutan bağlantı havuzu

Bir bağlantı havuzu, açık veritabanı bağlantılarının sayısını sınırlar ve bunları yeniden kullanır. Havuz yoksa, her istek yeni bir bağlantı açabilir ve Postgres oturum yönetimi yerine sorgu çalıştırmak yerine zaman ve bellek harcar.

Amaç, Postgres'i yararlı iş yaparken meşgul tutmaktır; çok fazla bağlantı arasında bağlam değiştirmek değil. Bu genellikle ilk anlamlı kazançtır, özellikle chatty endpoint'lere kolayca dönüşebilen AI-üretimli API'ler için.

Basit başlangıç ayarları

Go'da genellikle max open connections, max idle connections ve connection lifetime ayarlanır. Birçok küçük API için güvenli başlangıç: CPU çekirdeklerinizin küçük bir katı (çoğunlukla toplam 5–20 bağlantı), benzer sayıda idle bağlantı ve bağlantıları periyodik olarak yenilemek (ör. her 30–60 dakika).

Birden fazla API örneği çalıştırıyorsanız havuzun çarpıldığına dikkat edin. 10 örnekte her biri 20 bağlantılık bir havuz 200 bağlantı yapar ve bu takımların beklenmedik şekilde connection limitlerine takılmasına yol açar.

Havuzun sorun olup olmadığını nasıl anlarsınız

Havuz problemleri SQL yavaşlığından farklı hissedilir.

Havuz çok küçükse, istekler Postgres'e ulaşmadan önce bekler. Gecikme zirveleri olur, fakat veritabanı CPU ve sorgu zamanları normal görünebilir.

Havuz çok büyükse, Postgres aşırı yüklü görünür: çok sayıda aktif oturum, bellek baskısı ve endpoint'ler arasında düzensiz gecikme.

İkiye ayrılmış zamanlama yapmak hızlı bir ayırıcıdır: bağlantı alma için geçen süre vs sorguyu yürütme süresi. Eğer zamanın çoğu "beklemede" ise havuz sorundur. Eğer zamanın çoğu "sorguda" ise SQL ve indekslere odaklanın.

Hızlı kontroller:

  • Havuz istatistiklerini (open, in-use, idle) loglayın ve in-use'in maksimumda takılıp takılmadığına bakın.
  • Bağlantı edinme üzerinde timeout ekleyin ki staging'de beklemeler hızlıca başarısız olsun.
  • Postgres'te aktif bağlantıları ve max_connections'a ne kadar yaklaştığınızı izleyin.
  • Her isteğin rows'ları kapattığından ve bağlantıları çabuk serbest bıraktığından emin olun.
  • Çalıştırmayı planladığınız uygulama örnek sayısıyla load testi yapın.

pgx pool vs database/sql

pgxpool kullanıyorsanız Postgres-odaklı bir havuz, net istatistikler ve Postgres davranışı için iyi varsayılanlar alırsınız. database/sql kullanıyorsanız veritabanları arası ortak bir arayüz elde edersiniz, ancak havuz ayarlarını ve sürücü davranışını açıkça ayarlamanız gerekir.

Pratik bir kural: Tamamen Postgres'e bağlıysanız ve doğrudan kontrol istiyorsanız pgxpool genellikle daha basittir. Eğer database/sql bekleyen kütüphanelere bağımlıysanız, onunla devam edin, havuzu açıkça ayarlayın ve beklemeleri ölçün.

Örnek: bir sipariş listeleyen endpoint 20 ms çalışırken 100 eşzamanlı kullanıcı altında 2 s'ye fırlıyorsa ve loglar 1.9 s'in bağlantı beklemede geçtiğini gösteriyorsa, sorgu iyileştirme havuz ve toplam Postgres bağlantıları doğru boyuta gelene kadar yardımcı olmayacaktır.

Sorgu planlaması: EXPLAIN çıktısını hızlı okumak

Bir endpoint yavaş hissettiriyorsa, Postgres'in gerçekten ne yaptığını kontrol edin. EXPLAIN'ın hızlı bir okunması çoğu zaman dakikalar içinde çözümü gösterir.

API'nizin gönderdiği tam SQL üzerinde bunu çalıştırın:

EXPLAIN (ANALYZE, BUFFERS)
SELECT id, status, created_at
FROM orders
WHERE user_id = $1 AND status = $2
ORDER BY created_at DESC
LIMIT 50;

Birkaç satır en fazla önem taşır. Postgres'in seçtiği üst düğüme bakın ve alttaki toplam süreleri kontrol edin. Tahmin edilen satır ile gerçek satır arasındaki büyük farklar genellikle planlayıcının yanlış tahmin ettiğini gösterir.

Önemli satırlar genelde ne anlama gelir

Eğer Index Scan veya Index Only Scan görürseniz, Postgres bir indeksi kullanıyor demektir; genelde iyidir. Bitmap Heap Scan orta büyüklükteki eşleşmeler için uygun olabilir. Seq Scan tüm tabloyu okuduğunu gösterir; tablo küçükse veya neredeyse her satır eşleşiyorsa kabul edilebilir.

Yaygın kırmızı bayraklar:

  • Büyük bir tabloda Seq Scan
  • Tahmini satır sayısı ile gerçek arasında büyük fark (ör. 10 tahmin vs 10.000 gerçek)
  • Sıralama zamanın çoğunu alıyorsa (ORDER BY ile birlikte)
  • Tarama sonrası çok sayıda satırın "Filter" ile atılması
  • BUFFERS içinde yüksek shared read blokları (çok veri okunuyor)

Planların neden yanlış gittiği (ve kolay düzeltmeler)

Yavaş planlar genellikle birkaç kalıp nedeniyle olur:

  • WHERE + ORDER BY deseninize uyan eksik indeks (ör. (user_id, status, created_at))
  • Uyuşmayan tipler (ör. UUID sütunu ile text parametre karşılaştırılması), bu indeks kullanımını engelleyebilir
  • WHERE içinde fonksiyon kullanımı (ör. WHERE lower(email) = $1), uygun bir expression index eklenmediyse taramalara zorlayabilir

Plan garip görünüyorsa ve tahminler çok yanlışsa, istatistikler genelde güncel değildir. ANALYZE çalıştırın (veya autovacuum'un yakalamasına izin verin) ki Postgres güncel satır sayısını ve değer dağılımını öğrensin. Bu, büyük importlardan sonra veya yeni endpoint'ler hızlı veri yazmaya başlayınca önemlidir.

Gerçek kullanılan sorgulara göre indeksleme

Get Rewards for Sharing
Create content or refer others and earn credits to keep building on Koder.ai.
Earn Credits

İndeksler yalnızca API'nizin veriyi sorguladığı şekilde çalıştığında yardımcı olur. Tahmin ederek indeks inşa ederseniz, yazma maliyeti artar, depolama büyür ve hızlanma çok az olur.

Pratik düşünce biçimi: bir indeks belirli bir soru için kestirmedir. API'niz farklı bir soru soruyorsa Postgres kestirmeyi görmezden gelir.

Filtreler + sıralama etrafında indeks kurun

Bir endpoint account_id ile filtreliyor ve created_at DESC ile sıralıyorsa, tek bir bileşik indeks genelde iki ayrı indeksten daha iyidir. Postgres'e doğru satırları bulup doğru sırada döndürmek için daha az iş bırakır.

Genel kurallar:

  • En çok filtrelediğiniz sütunları indeksleyin, sonra sıralama sütununu ekleyin.
  • Bileşik indeksleri küçük tutun. İki sütun yaygın; üç bazen kabul edilebilir; daha fazlası genelde kötü bir işarettir.
  • En seçici filtreyi (sonuçları en çok daraltanı) önce koyun.
  • Tamamen örtülü ayrı indekslerden kaçının; daha iyi bir bileşik indeks onları kapsıyorsa gereksizdir.
  • Birkaç "belki işe yarar" indeks yerine iyi seçilmiş tek bir indeksi tercih edin.

Örnek: API'nizde GET /orders?status=paid varsa ve her zaman en yeniler gösteriliyorsa, (status, created_at DESC) gibi bir indeks iyi uyum sağlar. Eğer çoğu sorgu müşteri ile de filtreliyorsa (customer_id, status, created_at) daha iyi olabilir; ama sadece endpoint prod ortamında gerçekten böyle çalışıyorsa.

Yaygın filtreler için kısmi indeksler

Trafiğin çoğu dar bir satır dilimine vuruyorsa, kısmi indeks daha ucuz ve hızlı olabilir. Örneğin uygulama çoğunlukla aktif kayıtları okuyorsa, sadece WHERE active = true ile indekslemek indeksi daha küçük tutar ve bellekte kalma ihtimalini artırır.

Bir indeksin işe yaradığını doğrulamak için hızlı kontroller:

  • EXPLAIN çalıştırın ve sorgunuza uygun bir index scan arayın.
  • İndeksle ve indeks olmadan zamanlama ve okunan satırları karşılaştırın.
  • "Rows Removed by Filter"'ın yüksek kalıp kalmadığına bakın. Genelde bu indeksinizin filtreye uymadığını gösterir.

Kullanılmayan indeksleri dikkatle kaldırın. Kullanım istatistiklerine bakın (ör. bir indeks tarandı mı?). Düşürürken düşük riskli zamanları seçin ve geri alma planı hazırlayın. Kullanılmayan indeksler zararsız değildir; her yazmada insert/update yavaşlatırlar.

Zaman içinde yavaşlamayan sayfalama desenleri

Sayfalama genellikle hızlı bir API'nin yavaş hissettirdiği noktadır, veritabanı sağlıklı olsa bile. Sayfalamayı UI detayı değil, sorgu tasarımı olarak ele alın.

Neden LIMIT/OFFSET yavaşlar

LIMIT/OFFSET basit görünür, ama derin sayfalar daha pahalıdır. Postgres atlanan satırların üzerinden yine geçmek zorunda kalır (ve çoğu zaman sıralama yapar). 1. sayfa birkaç düzine satır okuyabilir. 500. sayfa ise 20 sonuç döndürmek için on binlerce satırı taramak ve atmak zorunda kalabilir.

Ayrıca istekler arasında satırlar eklenip silindiğinde sonuçları kararsız yapabilir. Kullanıcılar yinelemeler görebilir veya öğeleri kaçırabilir çünkü "10000. satır"ın anlamı değişmiştir.

"Son görülen" ile keyset (cursor) sayfalama örneği

Keyset sayfalama farklı bir soru sorar: "Bana son gördüğüm satırdan sonraki 20 satırı ver." Bu, veritabanının küçük, tutarlı bir dilim üzerinde çalışmasını sağlar.

Basit bir sürüm artan id kullanır:

SELECT id, created_at, title
FROM posts
WHERE id > $1
ORDER BY id
LIMIT 20;

API next_cursor olarak sayfadaki son id'yi döner. Sonraki istek bu değeri $1 olarak kullanır.

Zamana dayalı sıralama için kararlı bir sıra ve bağ çözücü kullanın. created_at tek başına yeterli değildir eğer iki satır aynı zaman damgasını paylaşıyorsa. Bileşik cursor kullanın:

WHERE (created_at, id) < ($1, $2)
ORDER BY created_at DESC, id DESC
LIMIT 20;

Tekrarlamalar ve eksik satırlar olmasını önleyecek birkaç kural:

  • ORDER BY'de her zaman benzersiz bir bağ çözücü ekleyin (genellikle id).
  • Sıralama düzenini istekler arasında aynı tutun.
  • Cursor'ı istemcilere opak tutun (örn. created_at ve id'yi birlikte encode edin).
  • Kullanıcılar filtreleyebiliyorsa, her sayfada aynı filtreleri uygulayın.
  • Mümkünse değiştirilebilir alanlar (status, score) yerine değişmez sıralama alanlarını (created time) tercih edin.

JSON şekillendirme: daha küçük ve daha hızlı yanıtlar

Ship With Rollback Ready
Deploy your Go service and keep performance tweaks safer with snapshots and rollback.
Deploy App

Şaşırtıcı derecede yaygın bir neden API'nin yavaş hissetmesi veritabanı değildir. Yanıttır. Büyük JSON oluşturmak daha uzun sürer, göndermek daha uzun sürer ve istemcilerin parse etmesi daha uzun sürer. En hızlı kazanım genelde daha az döndürmektir.

SELECT ile başlayın. Bir endpoint sadece id, name ve status gerekiyorsa, sadece bu sütunları isteyin. SELECT * tabloya uzun metin, JSON blobları ve audit sütunları eklendikçe sessizce ağırlaşır.

Diğer sık yavaşlama sebebi N+1 yanıt oluşturma: 50 öğelik bir liste alırsınız, sonra ilişkili verileri eklemek için 50 ek sorgu çalıştırırsınız. Testleri geçebilir, gerçek trafik altında çökebilir. Tek sorguyla gerekeni döndürmeyi tercih edin (dikkatli join'ler), ya da ikinci sorguyu ID'lere göre batchleyin.

Yanıtları müşteriyi bozmadan küçük tutmanın birkaç yolu:

  • Bir include= bayrağı (veya fields= maskesi) kullanın ki liste yanıtları hafif kalsın ve detay yanıtlar ekstra alanları isteyebilsin.
  • İç içe dizileri sınırlayın (ör. sadece son 10 event) ve tam geçmiş için ayrı endpoint sağlayın.
  • İstemciler sadece birkaç anahtara ihtiyaç duyuyorsa ham internal JSON sütunlarını geri vermeyin.
  • Uzun etiketleri tekrarlamak yerine kısa kodlar kullanın.

JSON'u Postgres'te mi yoksa Go'da mı oluşturmalı?

Her ikisi de hızlı olabilir. Ne optimize ettiğinize bağlı olarak seçin.

Postgres JSON fonksiyonları (jsonb_build_object, json_agg) az tur ile ve tek sorguda öngörülebilir şekiller üretmek istediğinizde faydalıdır. Go'da şekillendirme ise koşullu mantık, struct yeniden kullanımı veya SQL'i daha okunabilir tutmak istediğinizde avantajlıdır. JSON oluşturma SQL'iniz okunması zorlaşırsa, onu optimize etmek de zorlaşır.

İyi bir kural: Postgres'e filtreleme, sıralama ve agregasyonu yaptırın. Sonrasında Go son sunumu halletsin.

Eğer API'leri hızlı üretiyorsanız (ör. Koder.ai ile), include bayraklarını erken eklemek endpoint'lerin zaman içinde şişmesini önlemeye yardımcı olur. Ayrıca her yanıtı ağırlaştırmadan alan eklemenin güvenli bir yolunu verir.

İlk kullanıcılar gelmeden önce adım adım tuning süreci

Büyük bir test laboratuvarına ihtiyacınız yok; kısa, tekrar edilebilir bir geçiş çoğu performans sorununu ortaya çıkarır—özellikle başlangıç noktası üretim için hazırlanmış ve jenerik kod olduğunda.

Herhangi bir şeyi değiştirmeden önce küçük bir temel yazın:

  • En yoğun endpoint'ler için p95 ve p99 gecikme
  • hata oranı ve zaman aşımı
  • veritabanı CPU ve aktif bağlantılar
  • toplam sürede en yavaş 5 sorgu (sadece en kötüsü değil)

Tuning geçişi

Küçük başlayın, aynı anda bir şey değiştirin ve her değişiklikten sonra tekrar test edin.

  1. Gerçeğe yakın bir 10–15 dakikalık yük testi çalıştırın. İlk kullanıcıların vuracağı aynı endpoint'lere (login, listeleme, arama, oluşturma) istek atın. Sonra route'ları p95 gecikmeye ve toplam geçire zamanına göre sırala.

  2. SQL'i tune etmeden önce bağlantı basıncı olup olmadığına bakın. Çok büyük bir havuz Postgres'i boğar. Çok küçük bir havuz uzun beklemeler yaratır. Bekleme süresinde artış ve bağlantı sayılarında ani sıçramalara bakın. İlk önce havuz ve idle limitlerini ayarlayın, sonra aynı yük testi tekrar çalıştırın.

  3. En yavaş sorgulara EXPLAIN uygulayın ve en büyük kırmızı bayrağı düzeltin. Yaygın suçlular: büyük tablolarda tam tablo taramaları, büyük sonuç kümelerinde sıralamalar ve patlayan join'ler. Tek en kötü sorguyu seçin ve onu sıkıcı hale getirin.

  4. Bir indeks ekleyin veya ayarlayın, sonra tekrar test edin. İndeksler WHERE ve ORDER BY'ınıza uyduğunda yardımcı olur. Bir seferde beş tane eklemeyin. Eğer yavaş endpoint "kullanıcıya göre siparişleri created_at'a göre listele" ise (user_id, created_at) bileşik indeksi anlık ile acı arasında fark yaratabilir.

  5. Yanıtları ve sayfalamayı sıkıştırın, sonra tekrar test edin. Eğer endpoint büyük JSON bloblarıyla 50 satır döndürüyorsa veritabanı, ağ ve istemci hepsi bedel öder. UI'nin ihtiyaç duyduğu alanları döndürün ve tablolar büyüdükçe yavaşlamayan sayfalama tercih edin.

Basit bir değişiklik günlüğü tutun: ne değişti, neden ve p95'te ne hareket etti. Bir değişiklik temelinizi iyileştirmiyorsa geri alın ve devam edin.

Kaçınılması gereken yaygın hatalar ve tuzaklar

Generate a Go API Fast
Describe your endpoints and get a Go and Postgres service you can tune with real traffic patterns.
Start Free

Çoğu performans problemi Go API'lerde Postgres ile kendiliğinden yapılır. İyi haber: birkaç kontrol gerçek trafik gelmeden çoğunu yakalar.

Klasik bir tuzak havuz boyutunu "olabildiğince büyük" yapmak. Bu genelde her şeyi yavaşlatır. Postgres oturumları, bellek ve kilitlerle daha fazla vakit geçirir ve uygulamanız dalga dalga zaman aşımına girer. Küçük, stabil bir havuz genelde kazanır.

Bir diğer hata "her şeyi indeksle"mektir. Fazla indeksler okuma performansına yardımcı olabilir ama yazmaları yavaşlatır ve sorgu planlarını şaşırtıcı şekilde değiştirebilir. Ölçün, sonra ekleyin; indeks ekledikten sonra planları tekrar kontrol edin.

Sayfalama borcu sessizce girer. Offset sayfalama başta iyi görünür, sonra p95 zamanla yükselir çünkü veritabanı daha fazla satırın üzerinden geçmek zorunda kalır.

JSON yükü gizli bir vergi gibidir. Sıkıştırma bant genişliğini azaltır ama büyük nesneleri oluşturma, tahsis etme ve parse etme maliyetini ortadan kaldırmaz. Alanları kırpın, derin iç içe yapıları azaltın ve ekranda gerçekten gerekli olmayanları döndürmeyin.

Sadece ortalama yanıt süresine bakıyorsanız, gerçek kullanıcı acısını kaçırırsınız. p95 (ve bazen p99) havuz dolumu, kilit beklemeleri ve yavaş planların ilk göründüğü yerdir.

Kısa bir lansman öncesi kontrolü:

  • Küçük bir yük testinde havuz bekleme süresini ve Postgres bağlantı sayılarını izleyin.
  • Aynı endpoint için ortalama ile p95'i karşılaştırın.
  • Tablo 10x daha büyük olduğunda sayfalamanın bozulmadığını doğrulayın.
  • Liste endpoint'leri için yanıt boyutlarını kontrol edin (bayt önemlidir).
  • İndeks ekledikten veya filtreleri değiştirdikten sonra EXPLAIN'ı yeniden çalıştırın.

Lansmandan önce hızlı kontrol listesi ve sonraki adımlar

Gerçek kullanıcılar gelmeden önce API'nizin stres altında öngörülebilir kaldığına dair kanıt istiyorsunuz. Amaç mükemmel sayılar değil—zaman aşımına, zirvelere veya veritabanının yeni iş kabul etmeyi durdurmasına yol açacak birkaç sorunu yakalamaktır.

Staging benzeri bir ortamda (yakın DB boyutu, aynı indeksler, aynı havuz ayarları) kontrolleri çalıştırın: önemli endpoint'ler için yük altında p95 ölçün, toplam sürede en yavaş sorguları yakalayın, havuz bekleme süresini izleyin, en kötü sorguya EXPLAIN (ANALYZE, BUFFERS) uygulayıp beklediğiniz indeksi kullandığını doğrulayın ve en yoğun rotalarınızdaki payload boyutlarını mantık kontrolünden geçirin.

Sonra ürünlerin kırılma biçimini taklit eden bir en kötü durum çalıştırması yapın: derin bir sayfa isteği, en geniş filtreyi uygulama ve soğuk başlangıçla deneme (API'yi yeniden başlatıp aynı isteği ilk olarak atma). Eğer derin sayfalama sayfa sayısı arttıkça yavaşlıyorsa, lansmandan önce cursor tabanlı sayfalamaya geçin.

Varsayılanlarınızı yazın ki ekip daha sonra tutarlı seçimler yapsın: havuz limitleri ve time-out'lar, sayfalama kuralları (maks sayfa boyutu, offset izinli mi, cursor formatı), sorgu kuralları (sadece gerekli sütunları seç, SELECT *'ten kaçın, pahalı filtreleri sınırla) ve logging kuralları (slow query eşik değeri, örnekleri ne kadar saklayacağınız, endpoint etiketleme).

Eğer Go + Postgres servislerini Koder.ai ile oluşturup dışa aktarıyorsanız, dağıtımdan önce kısa bir planlama aşaması filtreleri, sayfalamayı ve yanıt şekillerini kasıtlı kılmada yardımcı olur. İndeksleri ve sorgu şekillerini iyileştirmeye başlayınca, anlık görüntüler (snapshots) ve rollback bir değişikliğin bir endpoint'e iyi gelip diğerlerine zarar veriyorsa geri almayı kolaylaştırır. Tek bir yerde bu iş akışını yinelemek isterseniz, Koder.ai koder.ai üzerinde sohbet etrafında bu servisleri üretip rafine etmek ve hazır olduğunuzda kaynak kodu dışa aktarmak üzere tasarlanmıştır.

SSS

How do I quickly tell if my Go API is slow because of Postgres or because of my code?

Start by separating DB wait time from app work time.

  • If the database is slow, the handler mostly waits on the query. Go CPU often stays normal while requests pile up “in flight.”
  • If the app is slow, queries return fast but time is spent building objects, doing extra per-row queries, marshaling large JSON, or logging. Go CPU and memory usually rise with response size.

Add simple timing around “wait for connection” and “query execution” to see which side dominates.

What metrics should I track first before tuning anything?

Use a small baseline you can repeat:

  • p95 latency per key endpoint (not average)
  • error rate (5xx, timeouts, cancellations)
  • DB time per request (time spent waiting on Postgres)

Pick a clear target like “p95 under 200 ms at 50 concurrent users, errors under 0.5%.” Then only change one thing at a time and re-test the same request mix.

Should I turn on Postgres slow query logging, and what threshold is practical?

Enable slow query logging with a low threshold in pre-launch testing (for example 100–200 ms) and log the full statement so you can copy it into a SQL client.

Keep it temporary:

  • It gets noisy fast in production.
  • It can add overhead if you log too much.

Once you’ve found the worst offenders, switch to sampling or raise the threshold.

What are good starting connection pool settings for a Go API on Postgres?

A practical default is a small multiple of CPU cores per API instance, often 5–20 max open connections, with similar max idle connections, and recycle connections every 30–60 minutes.

Two common failure modes:

  • Pool too small: requests wait to get a connection even though Postgres query time looks fine.
  • Pool too big: Postgres gets overloaded with many active sessions and latency becomes uneven.

Remember pools multiply across instances (20 connections × 10 instances = 200 connections).

How can I confirm the connection pool is the bottleneck (not SQL)?

Time DB calls in two parts:

  • Time waiting for a connection (pool wait)
  • Time executing the query (Postgres work)

If most time is pool wait, adjust pool sizing, timeouts, and instance counts. If most time is query execution, focus on EXPLAIN and indexes.

Also confirm you always close rows promptly so connections return to the pool.

What should I look for first in EXPLAIN when an endpoint is slow?

Run EXPLAIN (ANALYZE, BUFFERS) on the exact SQL your API sends and look for:

  • Seq Scan on a large table
  • Huge gap between estimated rows vs actual rows
  • Sort dominating time (often with ORDER BY)
  • “Rows Removed by Filter” being very high
  • Lots of shared read blocks in BUFFERS (heavy reads)

Fix the biggest red flag first; don’t tune everything at once.

How do I choose the right index for a list endpoint with filters and sorting?

Indexes should match what the endpoint actually does: filters + sort order.

Good default approach:

  • Build a composite index for your common WHERE + ORDER BY pattern.
  • Keep it small (2 columns often, 3 sometimes).
  • Put the most selective filter first, then the sort column.

Example: if you filter by user_id and sort by newest, an index like (user_id, created_at DESC) is often the difference between stable p95 and spikes.

When is a partial index worth it in Postgres?

Use a partial index when most traffic hits a predictable subset of rows.

Example pattern:

  • Many reads only for active = true
  • Few queries for inactive rows

A partial index like ... WHERE active = true stays smaller, is more likely to fit in memory, and reduces write overhead versus indexing everything.

Confirm with EXPLAIN that Postgres actually uses it for your high-traffic queries.

Why does LIMIT/OFFSET pagination get slower over time, and what should I use instead?

LIMIT/OFFSET gets slower on deep pages because Postgres still has to walk past (and often sort) the skipped rows. Page 500 can be dramatically more expensive than page 1.

Prefer keyset (cursor) pagination:

  • Use a stable sort plus a unique tie-breaker (often id).
  • Keep ORDER BY identical across requests.
  • Encode (created_at, id) or similar into a cursor.

This keeps each page cost roughly constant as tables grow.

My DB queries are fast, but responses are still slow—should I trim JSON payloads?

Usually yes for list endpoints. The fastest response is the one you don’t send.

Practical wins:

  • Select only needed columns (avoid SELECT *).
  • Add include= or fields= so clients opt into heavy fields.
  • Cap nested arrays (e.g., latest 10 items) and fetch the rest via a dedicated endpoint.
  • Avoid N+1 patterns (50 rows + 50 extra queries). Use joins or batch queries.

You’ll often reduce Go CPU, memory pressure, and tail latency just by shrinking payloads.

İçindekiler
Go API'lerinde Postgres ile "yavaş" nasıl görünürÖnce temel: önemli birkaç sayıPostgres'i stabil tutan bağlantı havuzuSorgu planlaması: EXPLAIN çıktısını hızlı okumakGerçek kullanılan sorgulara göre indekslemeZaman içinde yavaşlamayan sayfalama desenleriJSON şekillendirme: daha küçük ve daha hızlı yanıtlarİlk kullanıcılar gelmeden önce adım adım tuning süreciKaçınılması gereken yaygın hatalar ve tuzaklarLansmandan önce hızlı kontrol listesi ve sonraki adımlarSSS
Paylaş