# PostgreSQL tablo partitioning'e sıfır kesinti ile nasıl geçerim?

> `created_at` üzerinde aylık RANGE declarative partitioning kurun, geçmişi batch'lerle backfill edip isimleri tek transaction'da takaslayın.

- Soruldu: 2026-06-03
- Yanıtlandı: 2026-06-06
- Soran: Furkan
- Etiketler: veritabani, postgresql, olcekleme
- Kaynak: https://muhammetsafak.com/tr/sor-bakalim/postgresql-tablo-partitioning-sifir-kesinti-ile-gecis/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
**Soru:** SaaS uygulamamızda kullanıcı hareket loglarını tuttuğumuz `audit_logs` tablosu aylık 50M satır büyüyor; tablo büyüdükçe basit `select`'ler bile yavaşladı. PostgreSQL'in declarative partitioning'iyle bu tabloyu `created_at`'e göre aylık partition'lara ayırmak istiyorum.

Canlı veriyi sıfır kesinti ile bu yeni yapıya nasıl taşırım? İndeksler ve foreign key bağımlılıkları bundan nasıl etkilenir?


Kısa cevap: Aylık 50M satır gerçekten büyük — partitioning burada doğru hamle. `created_at` üzerinden **aylık RANGE declarative partitioning** kullanın; geçişi sıfır kesinti için aşamalı yapın.

## Kısa cevap

Önce şu sağlamayı yapmakta fayda var: 50M/ay gerçek bir hacim, "verin gerçekten büyük mü yoksa kötü mü modellenmiş" sorusunun cevabı burada net — gerçekten büyük. Partition mantıklı. Partition başına oluşan indekslerde kolon sırasını nasıl seçeceğinizi [composite index kaydında](/tr/sor-bakalim/postgresql-composite-index-kolon-sirasi-nasil-secilir/) ayrıca anlatmıştım; oradaki kural burada da aynen geçerli.

## Neden

1. **Partition pruning sorguyu tek aya daraltır.** Eski monolitik tablonun aksine, sorgu sadece ilgili aya değer; partition pruning sayesinde basit `select`'ler tekrar hızlanır.
2. **İndeksler partition başına oluşur.** Parent üstünde tanımladığınız indeksi Postgres her partition'a otomatik yayar (propagate). Yani global tek bir B-tree değil, her ay için ayrı indeks; bu aslında işinize yarar, eski ayların indeksi sıcak veriyi yormaz.
3. **UNIQUE ve FK kuralı şemanızı değiştirir.** Partitioned tabloda global bir UNIQUE/PK, **partition anahtarını içermek zorundadır** — yani PK'in `(id, created_at)` olur. Tabloya referans veren FK'ler de bundan etkilenir; modern PG'de partitioned tabloya FK kurmak çalışır ama sürümünüzü doğrulayın.

## Ne yapmalı

1. **Native declarative RANGE ile parçalayın.** Yeni bir partitioned parent (`PARTITION BY RANGE (created_at)`) oluşturun ve aylık partition'ları `ATTACH` edin.
2. **Geçişi sıfır kesinti ile yapın.** Yeni parent'ı kurun, partition'ları bağlayın, sonra geçmiş veriyi **batch'ler hâlinde** (örn. 50k'lık dilimlerle, off-peak) partition'lara backfill edin. Bu sırada uygulama yazmaya devam etsin: ya yeni yapıya yönlendiren bir trigger/view kullanın ya da hem eskiye hem yeniye **dual-write** yapın. Backfill bitince isimleri **tek bir transaction içinde** takaslayın — kesinti yok.
3. **Unique key'lere partition anahtarını ekleyin.** PK'i `(id, created_at)` biçimine taşıyın ve tabloya referans veren FK'leri buna göre gözden geçirin.
4. **Yaşam döngüsünü `pg_partman`'a bırakın.** Gelecek ayların partition'larını otomatik oluşturması için `pg_partman` kullanın; retention için eski partition'ları `DETACH` + `DROP` ile silin — toplu bir `DELETE`'ten çok daha hızlı.

**Sonuç:** `created_at` üzerinde declarative RANGE, batch'li backfill, her unique key'de partition anahtarı, yaşam döngüsü için `pg_partman`. Geçişi dual-write + tek transaction'lık isim takasıyla kurarsanız canlı sistem hiç durmaz. Bu tür DB derinliklerine girmeden önce ölçeklenme ihtiyacının gerçekten "büyük veri" mi olduğunu netleştirin; bu vakada öyle.

## İlgili Yazılar

- [PostgreSQL Her Şeye Yeter mi?](https://sade.dev/tr/journal/postgresql-her-seye-yeter-mi) — sade.dev
- [Zaman serisi verisi için TimescaleDB mi, InfluxDB mu?](https://muhammetsafak.com/tr/sor-bakalim/zaman-serisi-verisi-timescaledb-mi-influxdb-mu/) — Sor Bakalım
- [Heap erişimini önlemek ve index-only scan almak için covering index'e (INCLUDE) ne zaman geçmeliyim?](https://muhammetsafak.com/tr/sor-bakalim/index-only-scan-covering-indexe-ne-zaman-gecmeliyim/) — Sor Bakalım
- [Sadece 'pending' satırları taranan bir kuyruk tablosunda partial index kullanmalı mıyım?](https://muhammetsafak.com/tr/sor-bakalim/sadece-pending-satirlari-taranan-bir-kuyruk-tablosunda-partial-index-kullanmali-miyim/) — Sor Bakalım
