İçeriğe geç

Kuyruk tablosu: 30 saniyede kazanan, 15 dakikada kaybeden index

Klavye: ← → ile gezinin, F tam ekran, O genel bakış.

Tan, dizüstü bilgisayarıyla

PostgreSQL · Kuyruk tablosu

30 saniyede kazanan, 15 dakikada kaybeden index

Aynı tablo, Postgres 17, üç ölçüm.

Muhammet Şafak

Tek makine, Postgres 17, sentetik tezgâh

30 saniyede neredeyse eşit. 15 dakikada ayrıldılar.

Partial ve composite index otuz saniyede 11.537'ye 10.795 tps. On beş dakikalık sürekli yükte composite hedefi tutamadı.

Postgres bu ayrımı bilmez

Bir tabloda iki küme

Arşiv · doneCanlı küme · pending
Kim bakarKimse sorgulamazWorker'lar arar, claim'ler kilitler
BoyutSilinmezse sonsuza büyürKabaca sabit durur
Ölçülenn_live_tup 10.005.7265.000 satır
PostgresCanlı tuple: index, istatistik ve vacuum eşiği onu sayarAynı muamele; ikisi ayırt edilmez

Bölüm 01

Otuz saniye

Dört strateji, üç ölü satır kademesi. Canlı küme her kademede 5.000 satır.

SKIP LOCKED · 8 istemci · 30 sn · üç tekrarın medyanı

Üç kademe, dört strateji

FOR UPDATE SKIP LOCKED ile claim; 8 istemci, 30 saniye, üç tekrarın medyanı. Canlı küme her kademede 5.000 satır.
Strateji100k ölü satır1M10MIndex (10M)
index yok2.001 tps247 tps7 tps—
(status)6.499 tps6.417 tps6.426 tps66,1 MB
(status, created_at)12.414 tps11.202 tps10.795 tps310,4 MB
partial (created_at) WHERE pending13.041 tps11.707 tps11.537 tps7,6 MB

Index yok

Index'siz kuyruk yok oluyor.

Ölü satırla birlikte çökmüyor: 2.001'den 7 tps'ye. 10 milyon satırın arasından 5.000 satırı sequential scan saniyede yedi kez bulabiliyor.

41×

Asıl fark boyutta

daha küçük index

10 milyon ölü satırda partial 7,6 MB, composite 310,4 MB. Throughput farkı yalnız %6,9: seçme sebebi hız değil, index'in tabloyla birlikte büyümemesi.

Ölü satır arttıkça

Partial index tabloyla büyümüyor

index boyutu, MB

  • composite · 100k11,2
  • composite · 1M40,3
  • composite · 10M310,4
  • partial · 100k6,5
  • partial · 1M7,6
  • partial · 10M7,6
pg_relation_size, bench/run.sh

Bölüm 02

Planner

Aynı tablo, aynı index, aynı sorgu. Değişen tek şey planın türü.

10M ölü satır · aynı sorgu, parametreyle

Generic plan uçurumu

Aynı tablo, aynı index, aynı sorgu. Partial index generic planda hiç taranmadı: Limit, LockRows, Sort, Seq Scan.
Index · plantpsGecikmePlan
partial · custom11.7520,68 msIndex Scan
partial · generic71.113 msSort → Seq Scan
composite · custom11.1050,72 msIndex Scan
composite · generic11.4160,70 msIndex Scan

Neden

Yaklaşık 1.635 kat

  • NotGeneric plan $1'in ne olduğunu bilmeden kurulur. status = $1'in 'pending' olduğunu kanıtlayamaz, partial index'i eler.
  • İpucuComposite etkilenmez: kanıtlanacak bir predicate'i yok, status index'in içindeki bir kolon.
  • UyarıUçuruma düşmek için force_generic_plan gerekti. Bu bir üretim ayarı değil.

Tek oturum, varsayılan auto mod

Kırk çalıştırma, kırk custom plan

  1. İlk beş çalıştırma: üçü de custom plantamam
  2. Altıncı: composite generic'e geçertamam
  3. Partial: 40'ın 40'ı customtamam
  4. (status): 40'ın 40'ı customtamam

Düzeltme

Bu koşullarda geçmedi

  • DüzeltmeÖnceki kaydımda 'ısındıkça bozulur' demiştim. Ölçüldüğünde yanlış çıktı; kırk çalıştırmanın kırkı custom plan kaldı.
  • NedenEn olası açıklama: partial index'i kullanamayan generic planın tahmini maliyeti yüksek çıkıyor. Plan maliyetleri kaydedilmedi.
  • BedelPartial her çalıştırmada yeniden planlanır. Bu ölçekte fark ölçülemedi: 0,20 ms'ye 0,21 ms.

Bölüm 03

Şimdi 15 dakika

Sürekli churn: saniyede 2.000 iş, varsayılan autovacuum ayarları.

Partial · ~305 kat

Canlı küme sabit, index büyüyor

index boyutu, MB

Canlı küme sabit, index büyüyor (index boyutu, MB): 0 sn: 0,125, 135 sn: 5,9, 286 sn: 12,4, 436 sn: 18,8, 587 sn: 25,3, 738 sn: 31,7, 888 sn: 38,2.

pg_relation_size, 15 saniyede bir örnek, bench/endurance.sh. pending→done geçişi vacuum'a kadar ölü girdi bırakır.

Composite · %42, +126 MB

Composite daha az oranla, daha çok MB şişti

index boyutu, MB

Composite daha az oranla, daha çok MB şişti (index boyutu, MB): 0 sn: 301, 135 sn: 321, 286 sn: 344, 436 sn: 366, 587 sn: 388, 738 sn: 409, 888 sn: 427.

pg_relation_size, 15 saniyede bir örnek, bench/endurance.sh

Hedef 2.000 iş/sn · 900. saniye

Şişen index sürdürülebilir yükü taşımıyor

compositepartial
Başlangıç2.000 tps · 0,52 ms2.024 tps · 1,7 ms
900. saniye1.204 tps · 61.533 ms2.016 tps · 4.906 ms
Bekleyen iş5.000 → 126.0245.003 → 14.364
SonuçHedefi tutamadıHedefi tuttu

Okurken

İki not

  • NotGecikme pgbench'in zamanlama gecikmesini içeriyor: hedef hıza yetişemeyen istemcinin borcu buraya yazılıyor.
  • NotFark sürdürülebilir yükte açılıyor: 427 MB'a şişen index artık belleğe sığmıyor, her tarama diske gidiyor.

Eşik tabloyla büyür

Autovacuum hiç gelmedi

  • 1,75Milyon ölü satır15 dakikada 1.753.949
  • 0AutovacuumBir kez bile koşmadı
  • ≈2Eşik, milyon satır50 + 0,2 × 10.005.000 ≈ 2.001.050
  • 17Dakika, tahmini aralıkBu iş yüküne özgü; vacuum maliyeti ölçülmedi

Öneri

Eşiği tablodan koparın

Bu ayarı ölçmedim. 50.000 bir başlangıç noktası; kendi throughput'unuza göre ayarlayın.

jobs.sql
ALTER TABLE jobs SET (  autovacuum_vacuum_scale_factor = 0,  autovacuum_vacuum_threshold    = 50000);CREATE INDEX CONCURRENTLY  jobs_pending_created_at  ON jobs (created_at)WHERE status = 'pending';
Tan, başparmağını kaldırmış onaylarken

Sonuç

Sor Bakalım cevabındaki iddialar, ölçülünce

  • Partial index küçük kalır: 10M'de 7,6 MB (tamamlandı)
  • (status) index'inden üstün: 11.537'ye 6.426 tps (tamamlandı)
  • ORDER BY kolonu index'e girmeli (tamamlandı)
  • Parametreyle planner partial'ı seçemeyebilir: doğru ama dar (tamamlandı)
  • Churn ve bloat: ilk kayıtta ölçülmedi, sonradan ölçüldü (tamamlandı)
Tan, gülümserken

Koşulu bulun

github.com/muhammetsafak/pg-queue-bench

Bir tavsiyeyi ölçmenin değeri onu doğrulamak değil. Değeri, doğru bir cümlenin hangi koşulda tersine döndüğünü bulmakta.

Paylaş, göm, indir