İçeriğe geç

Tablo Bölümleme — Eski Yılı Silmek Neden Bütün Geceyi Aldı?

Orta 8 dk Sık karşılaşılır

Önce şunu oku: MVCC ve Kilitler — Eski Satır Sürümleri Nerede Yaşar?

30 saniyede özet

Hareket tablosu büyüdükçe hem aylık raporlar hem de eski veriyi silmek ağırlaşır. Tabloyu aylara bölmek, tarihe göre süzen sorguların gereksiz ayları hiç okumamasını ve eski ayların satır satır silinmek yerine bir anda atılmasını sağlar.

Bir ofis iki yılın fişlerini tek bir koliye atıyor. Ekim’in fişlerini bulmak için bütün koliyi karıştırmak gerekiyor. Eski yılı atmak içinse fişleri tek tek ayıklamak.

  1. Bayt: Hareket tablosunda iki yıllık veri var. Ekim raporu her ay biraz daha yavaş.

  2. Sen: Saklama süresi dolan geçen yılı silmek için gece bir DELETE başlattım. Sabah hâlâ çalışıyordu.

  3. Bayt: Üstelik tablo küçülmedi bile! Silinen satırlar yer kaplamaya devam ediyor.

  4. Bayt: Fişleri aylık klasörlere ayırsaydık, eski yıl tek bir raf olurdu.

Tek tablo, birçok parça

tablo bölümlemeTek bir mantıksal tabloyu, örneğin aylara göre, aynı veritabanında ayrı parçalarda saklamak. Sorgular yine tek tabloya yazılır.Sözlükte gör →, tek bir mantıksal tabloyu aynı veritabanında ayrı parçalarda saklar. Hareketler için en doğal anahtar tarihtir: her ay bir parça.

Uygulama hâlâ transactions tablosuna yazar. Veritabanı her satırı, tarihine bakarak doğru aya koyar.

Kafam karıştı, daha basit anlat

Tablo tek görünür, ama içi aylara bölünmüştür.

Hızlı kontrolBaşlangıç

Tablo bölümleme nedir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Bölümleme ile sharding arasındaki fark nedir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Okumadığın ay, attığın ay

Tablo aylara bölündü. Bir müşterinin bütün hareketlerini müşteri numarasıyla arayan, tarih içermeyen bir sorgu kaç aya bakar? Cevabı göster

Hepsine. Sorguda tarih yok; veritabanı müşterinin hangi aylarda hareketi olduğunu bilemez ve her parçaya bakar. Bölümleme yalnızca sorgu bölümleme anahtarını kullanıyorsa parça atlar.

Ekim tek klasör; eski yıl tek raf.
Adım adım oku
  1. İki yılın fişleri tek bir kolide.
  2. Ekim'i bulmak için hepsini karıştır.
  3. Her ay ayrı klasör, sırtında adı.
  4. Ekim tek klasör; eski yıl tek raf.

Sorgu tarihe göre süzüyorsa, veritabanı diğer ayların parçalarını hiç okumaz. Buna bölüm budamaSorgudaki koşula bakarak işe yaramayacak parçaların hiç okunmaması. Sorgu bölümleme anahtarını kullanmıyorsa olmaz.Sözlükte gör → denir.

En büyük kazanç ise çoğu zaman silmede görülür. DELETE her satırı tek tek işaretler ve geride MVCCMulti-version concurrency control: yazma yeni bir satır sürümü üretir, okuyan commit edilmiş eski sürümü görür. Okuyan ile yazan birbirini beklemez.Sözlükte gör →’nin temizlemesi gereken ölü satırlar bırakır. Bölünmüş tabloda eski ayın parçası bir anda atılır.

Kafam karıştı, daha basit anlat

Tarihle süzen sorgu yalnızca gereken ayı okur. Eski ay satır satır silinmez, bir anda atılır.

Hızlı kontrolBaşlangıç

Ayrı ayrı aylara bölünmüş bir tabloda Ekim raporu neden daha az veri okur?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Bölümlenmiş bir tabloda müşteri numarasıyla, tarihsiz bir sorgu ne yapar?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Kendin gör

İki yıllık hareket tablosu

Tohum 524492
  1. Oca·
  2. Şub·
  3. Mar·
  4. Nis·
  5. May·
  6. Haz·
  7. Tem·
  8. Ağu·
  9. Eyl·
  10. Eki·
  11. Kas·
  12. Ara·
  13. Oca·
  14. Şub·
  15. Mar·
  16. Nis·
  17. May·
  18. Haz·
  19. Tem·
  20. Ağu·
  21. Eyl·
  22. Eki·
  23. Kas·
  24. Ara·

▤ okundu · ✕ satır satır silindi · ✓ parça atıldı · – atlandı

Satırlarına dokunulan ay: 0/24

Hız
Adım 0

Şu an ne oldu?

Tek büyük tablo · Ekim ayının toplam raporu

Her kare bir aylık hareket.

Görevler0/3

  • Tek ayın raporu için iki yılın hepsine bakılsınaçık

    İpucu

    Tek büyük tablo.

  • Bölünmüş tabloda bile her parçaya bakılsınaçık

    İpucu

    Sorguda tarih olmasın.

  • Eski yılı tek bir satıra dokunmadan silaçık

    İpucu

    Aylara böl, sonra sil.

Olay günlüğü (0)

Henüz olay yok. Oynat veya adımla.

  1. Varsayılanla oynat. Tek büyük tablo, Ekim raporu: yirmi dört ayın hepsine bakıldı.
  2. “Aylara bölünmüş tablo” seç. Yalnızca Ekim okundu.
  3. “Bir müşterinin bütün hareketleri” seç. Bölünmüş tabloda bile her aya bakıldı.
  4. “En eski 12 ayı sil” seç, iki düzeni de dene. Tek tabloda on iki ay satır satır silindi; bölünmüş tabloda on iki parça bir anda atıldı.
Hızlı kontrolOrta

Eski bir yılın hareketlerini silmenin bölümlenmiş tablodaki en ucuz yolu nedir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Tek büyük tablodan bir yıllık veriyi DELETE ile silmenin yan etkisi nedir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Tuzaklar

Gelecek ayın parçasını unutmak. PostgreSQL yeni ayın parçasını kendiliğinden açmaz; parça yoksa o ayın hareketleri eklenemez. Gelecek birkaç ayın parçalarını önceden oluşturan bir iş kur.

Birincil anahtarı eski hâliyle bırakmak. Benzersizlik her parçada ayrı kontrol edildiği için birincil anahtar bölümleme sütununu da içermelidir: (id, booked_at).

Çok küçük parçalar. Günlük parçalar, planlayıcının her sorguda düşündüğü parça sayısını büyütür. Parça boyunu sorgulara ve saklama kuralına göre seç.

Kafam karıştı, daha basit anlat

Parçaları önceden aç, anahtara tarihi ekle, parçaları gereğinden küçük yapma.

Hızlı kontrolOrta

Bölümleme anahtarı nasıl seçilmelidir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.
Derinleş · Hesap hareketleri: aylık parçalar ve saklama işi 4 dosya · ~55 satır · ilk okumada atlayabilirsin
Proje dosyaları

ledger/src/main/resources/db/migration/ V20__partition_transactions.sql Tablo: tarihe göre RANGE bölümleme; birincil anahtar tarihi de içerir.

ledger/src/main/resources/db/migration/V20__partition_transactions.sql
CREATE TABLE transactions (
id BIGINT GENERATED ALWAYS AS IDENTITY,
account_id UUID NOT NULL,
booked_at TIMESTAMPTZ NOT NULL,
amount NUMERIC(19, 2) NOT NULL,
description TEXT NOT NULL,
PRIMARY KEY (id, booked_at) -- must include the partition column
) PARTITION BY RANGE (booked_at);
CREATE INDEX ON transactions (account_id, booked_at);

ledger/src/main/resources/db/migration/ V21__transactions_2026_10.sql Parçalar: her ay için bir tablo; gelecek aylar önceden açılır.

ledger/src/main/resources/db/migration/V21__transactions_2026_10.sql
CREATE TABLE transactions_2026_10 PARTITION OF transactions
FOR VALUES FROM ('2026-10-01') TO ('2026-11-01');
CREATE TABLE transactions_2026_11 PARTITION OF transactions
FOR VALUES FROM ('2026-11-01') TO ('2026-12-01');

ledger/src/main/java/com/bank/ledger/retention/ PartitionMaintenance.java Saklama işi: her ay en eski parçayı ayırıp arşivler, sonra atar.

ledger/src/main/java/com/bank/ledger/retention/PartitionMaintenance.java
@Component
class PartitionMaintenance {
private static final DateTimeFormatter SUFFIX = DateTimeFormatter.ofPattern("yyyy_MM");
private final JdbcTemplate jdbc;
private final Clock clock;
PartitionMaintenance(JdbcTemplate jdbc, Clock clock) {
this.jdbc = jdbc;
this.clock = clock;
}
@Scheduled(cron = "0 0 3 1 * *")
@SchedulerLock(name = "partition-maintenance", lockAtMostFor = "PT30M")
void monthly() {
YearMonth now = YearMonth.now(clock);
// Open the next three months ahead of time.
for (int i = 1; i <= 3; i++) {
YearMonth month = now.plusMonths(i);
jdbc.execute("CREATE TABLE IF NOT EXISTS transactions_%s PARTITION OF transactions FOR VALUES FROM ('%s') TO ('%s')"
.formatted(month.format(SUFFIX), month.atDay(1), month.plusMonths(1).atDay(1)));
}
// Retention: detach the month that just turned too old, archive it, then drop it.
YearMonth expired = now.minusMonths(25);
String name = "transactions_" + expired.format(SUFFIX);
jdbc.execute("ALTER TABLE transactions DETACH PARTITION " + name);
archive(name);
jdbc.execute("DROP TABLE " + name);
}
private void archive(String table) {
// Copies the detached table to cold storage before it is dropped.
}
}

ledger/src/main/resources/sql/ monthly-report.sql Rapor sorgusu: tarih aralığı sayesinde yalnızca o ayın parçası okunur.

ledger/src/main/resources/sql/monthly-report.sql
-- The date range lets the planner read only October's partition.
SELECT account_id, SUM(amount) AS total
FROM transactions
WHERE booked_at >= '2026-10-01' AND booked_at < '2026-11-01'
GROUP BY account_id;

Kendini sına

Şimşek turu1/4

Tablo bölümleme veriyi farklı sunuculara dağıtır.

Soru 1/3Orta

Yeni ay başladı ama o ayın parçası oluşturulmamış. Ne olur?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Aklında kalacak üç şey

  1. 1 Tablo bölümleme, tek bir mantıksal tabloyu aynı veritabanında, örneğin aylara göre, ayrı parçalarda saklar.
  2. 2 Sorgu bölümleme anahtarıyla süzüyorsa gereksiz parçalar hiç okunmaz. Tarihsiz bir sorgu ise yine bütün parçalara bakar.
  3. 3 Eski veriyi DELETE ile satır satır silmek yerine, o ayın parçası bir anda atılır.
Sonraki kapı Yirmi şube müdürü aynı panoyu açtı; veritabanı aynı ağır toplamı yirmi kez hesapladı. Bir kez hesaplayıp herkese göstermek mümkün mü? Materialized View — Pano Her Açılışta Neden Bütün Günü Yeniden Topluyor? · 8 dk

4 kart sonraki derste seni bekliyor

0/4 kart bu dersten toplandı