İçeriğe geç
Muhammet Şafak
en

Kuyruk tablosunda index'i canlı kümeye göre kurmak

Postgres 17 kuyruk tablosunda partial index: 41 kat küçük, generic planda uçurum, 15 dakikalık churn altında bloat ve hiç gelmeyen autovacuum.

Tan, elinde bir planla

PostgreSQL · Ölçüm

Kuyruk tablosunda index'i canlı kümeye göre kurmak

Tek makine, Postgres 17, sentetik tezgâh: 30 saniyelik ölçüm, planner ve 15 dakikalık churn.

Muhammet Şafak

41×

10 milyon ölü satırda

daha küçük index

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.

10M ölü satır, 5.000 canlı satır

Index'siz kuyruk tablosu yok oluyor

tps

  • index yok7
  • (status)6.426
  • (status, created_at)10.795
  • partial, pending11.537
partial: (created_at) WHERE status = 'pending' · 8 istemci, 30 saniye, üç tekrarın medyanı · bench/run.sh

Aynı tablo, aynı index, aynı sorgu

Partial index generic planda

  • 11.752tps, custom planIndex Scan, 0,68 ms
  • 7tps, generic planSort → Seq Scan; index hiç taranmadı
  • 1.113ms, generic gecikmeYaklaşık 1.635 kat; force_generic_plan ile zorlandı
  • 11.416Composite, genericKanıtlanacak predicate'i yok, etkilenmedi

Varsayılan auto mod, tek oturum

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.

15 dk · 2.000 iş/sn · pg_relation_size, 15 sn'de bir

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

pg_relation_size, 15 saniyede bir örnek, bench/endurance.sh. Kuyruk derinliği beş bin satır civarında sabit; büyüyen ölü index girdileri.
Sürepartialcomposite
0 sn0,125 MB301 MB
286 sn12,4 MB344 MB
587 sn25,3 MB388 MB
888 sn38,2 MB427 MB
Artış~305 kat%42, +126 MB

Hedef 2.000 iş/sn · 900. saniye

Otuz saniyede neredeyse eşit olan iki index, on beş dakikada ayrıldı

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ı; 427 MB belleğe sığmıyorHedefi tuttu

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
Tan, başparmağını kaldırmış onaylarken

Ne yapmalı

Kuyruk tablosunu canlı kümeye göre kurun

  • Index'i kuyruğa kurun: partial (created_at) WHERE status = 'pending' (tamamlandı)
  • Predicate'li index'e parametreyle ulaşmayın (tamamlandı)
  • Autovacuum eşiğini tablodan koparın; etkisi ölçülmedi, 50.000 bir başlangıç (tamamlandı)
  • Önce pending/done oranına, index boyutuna, n_dead_tup'a ve eşiğe bakın (tamamlandı)
  • Kalıbı bırakın: tamamlananı hemen siliyor ya da arşive taşıyorsanız, ORM status'u parametre bağlıyor ve generic plan zorlanıyorsa, tabloyu zamana göre partition'lıyorsanız (bekliyor)
Tan, gülümserken

Tablo boyutu bir gürültüdür

github.com/muhammetsafak/pg-queue-bench

Index'i ve vacuum'u, işi yapan kümeye göre kurun. Sayılar tek makineden, Postgres 17'den ve tek erişim deseninden; gerçek bir kuyruğun aylarını temsil etmez.

Paylaş ve indir

Sitede Ara

Yazı, proje ve sayfalarda arama yapmak için yazmaya başlayın.

Escile kapatPagefind ile güçlendirildi