Modül 4 · Veri Ambarı · ⏱ 6 dk

Partition & Cluster: büyük defteri yıllara böl

Partition and Cluster

Bu derste neler öğreneceksin

  • Partitioning tarih aralık kavrar
  • Clustering yüksek kardinalite açıklar
  • Performans farkını ölçer

Performans Optimizasyonunun İki Direği

Bir BigQuery tablosu milyarlarca satıra ulaştığında, partitioning ve clustering tabloda hızlı sorguların ön koşuludur. Bu ikisi farklı problemi çözer:

  • Partitioning: Veriyi fiziksel olarak ayrı parçalara böler. “Şu tarih aralığını istiyorum” dediğinde, gerisini hiç okumaz.
  • Clustering: Veriyi belirli sütunlara göre sıralı tutar. “Şu değere sahip satırları istiyorum” dediğinde, dosyanın sadece ilgili kısmına atlar.

Birlikte kullanıldığında 100x-1000x performans artışı sağlayabilir. BigQuery’de fatura “bytes processed” üzerinden geldiği için bu, doğrudan maliyet tasarrufu demektir.

Partitioning Türleri

BigQuery üç tür partitioning destekler:

1. Time-unit column partitioning (en yaygın)

DATE, DATETIME veya TIMESTAMP sütununa göre:

CREATE TABLE my_dataset.events
PARTITION BY DATE(event_timestamp)
OPTIONS (
  partition_expiration_days = 365,  -- 1 yıldan eski partition'lar otomatik silinsin
  require_partition_filter = true    -- sorgu her zaman partition filtresi içermek zorunda
)
AS SELECT * FROM source_events;

require_partition_filter = true kritik bir güvenlik önlemi: “SELECT * FROM events” yazıp tüm tabloyu tarayan acemi bir analist, devasa fatura üretebilir. Bu option ile BigQuery hata verir. Production tablolarında her zaman aç.

Partition çözünürlüğü: günlük (default), saatlik, ayluk, yıllık. Günlük çoğu senaryo için yeterli.

2. Integer range partitioning

Tamsayı bir sütuna göre aralık:

CREATE TABLE my_dataset.user_events
PARTITION BY RANGE_BUCKET(user_id, GENERATE_ARRAY(0, 1000000, 10000))
AS SELECT * FROM source_events;

user_id 0-10000 → partition 0, 10001-20000 → partition 1, … Genelde sharding için kullanılır, çok yaygın değil.

3. Ingestion time partitioning

_PARTITIONTIME (eski) veya _PARTITIONDATE (önerilen) pseudo-sütununa göre. Veri yüklendiği anın tarihi kullanılır. Veri kaynağında timestamp yoksa faydalı:

CREATE TABLE my_dataset.ingested_logs
PARTITION BY _PARTITIONDATE
AS SELECT * FROM external_logs;

Cluster: Sıralı Saklama

Clustering, yüksek kardinaliteli sütunlar için tasarlanmıştır. Cardinality, bir sütundaki farklı değer sayısıdır:

  • Düşük (≤ 100 farklı değer): country_code (200 ülke), status (5-10 değer). Bunlar partition iyi adayı, cluster gereksiz.
  • Yüksek (100-10M farklı değer): user_id, product_sku, session_id. Bunlar için cluster mükemmel.
  • Çok yüksek (> 10M): unique identifier sütunları. Cluster etkisiz, başka strateji lazım.

Cluster, BigQuery’ye “bu sütunun değerlerine göre dosya içinde sıralı tut” der:

CREATE TABLE my_dataset.events_optimized
PARTITION BY DATE(event_timestamp)
CLUSTER BY user_id, event_type
AS SELECT * FROM source_events;

Sorgu “user_id = 12345” dediğinde, BigQuery sadece o kullanıcının bloğuna atlar. Birden fazla sütun verdiğinde (en fazla 4), sıralı kümelenir — ilk sütun dominant, sonrakiler tie-breaker.

Partition vs Cluster: Karar Matrisi

Sorgu PaterniÖneri
Sık WHERE tarih BETWEEN ...Partition by DATE
Sık WHERE user_id = ...Cluster by user_id
Hem tarih hem user_id filtresiİkisi birlikte
Çok sık full scan (ETL’nin tamamı)Partition yeterli, cluster gereksiz
Sadece WHERE category = ...Cluster by category

Performans Farkını Ölç

Aynı tablo, üç versiyon:

-- 1. Optimize değil: 1.4 milyar satır, tüm sütunlar
CREATE TABLE my_dataset.taxi_unoptimized AS
SELECT * FROM `bigquery-public-data.new_york_taxi.taxi_trips`;

-- 2. Sadece partition
CREATE TABLE my_dataset.taxi_partitioned
PARTITION BY DATE(pickup_datetime) AS
SELECT * FROM `bigquery-public-data.new_york_taxi.taxi_trips`;

-- 3. Partition + Cluster
CREATE TABLE my_dataset.taxi_full
PARTITION BY DATE(pickup_datetime)
CLUSTER BY vendor_id, passenger_count AS
SELECT * FROM `bigquery-public-data.new_york_taxi.taxi_trips`;

Şimdi aynı sorguyu üçünde çalıştır:

SELECT COUNT(*), AVG(total_amount)
FROM [TABLO_ADI]
WHERE DATE(pickup_datetime) = '2024-06-15'
  AND vendor_id = 1
  AND passenger_count = 2;

Tipik sonuçlar (NYC taksi, 1.4 milyar satır):

VersiyonBytes processedSüreFatura
Unoptimized85 GB18 s$0.55
Partitioned250 MB1.4 s$0.0016
Partition + Cluster12 MB0.4 s$0.0001

Cluster ekleyince 20x daha fazla kazanç. Maliyet farkı: 5500x.

Dikkat Edilecekler

  1. Cluster sırası önemli: Sık filtrelenen sütunu önce yaz. CLUSTER BY user_id, country → user_id dominant.
  2. Max 4 cluster sütunu: Fazlası işlemci zamanını yer, sıralama faydasını öldürür.
  3. Partition pruning önce, cluster pruning sonra: BigQuery önce tarih filtresiyle partition eler, kalan içinde cluster’a göre blok atlar.
  4. Yeniden cluster’a alma: Mevcut tabloya ALTER TABLE ... CLUSTER BY ekleyebilirsin, BigQuery arka planda yavaşça yeniden organize eder (backfill). Hemen sorgulamayacaksan yeni tablo oluşturup eskisini silmek daha hızlı.
  5. Cluster sütununda require_partition_filter: Partition için var, cluster için yok. Sadece cluster yazıp partition filtresi unutursan tüm partition’lar okunur.

Partition Expiration ve Storage Cost

BigQuery active storage (son 90 gün değişen) ve long-term storage (90 gündür değişmeyen) ayrı fiyatlandırır. Long-term %50 ucuz.

ALTER TABLE my_dataset.events
SET OPTIONS (
  partition_expiration_days = 365,  -- 1 yıl sonra partition otomatik silinsin
  require_partition_filter = true
);

Sık erişilmeyen eski veri için partition_expiration_days ayarla, depolama maliyetini kontrol et.

Hands-on: Optimize Et, Ölç

Kendi veri setinde üç tablo oluştur (unopt, partition, partition+cluster). Farklı sorgularla bytes processed’i karşılaştır. Bir CSV’den başla, sıkıştırma + partition + cluster ekle. Bytes processed’in dramatik düştüğünü gözlemle. Bu, BigQuery’de gerçek bir production optimizasyon alışkanlığıdır.

Ne Öğrendik?

  • Partition tabloyu tarih/aralık bazında fiziksel böler, büyük filtreler için.
  • Cluster yüksek kardinaliteli sütunlar için sıralı saklama yapar, küçük-fakat-yoğun filtreler için.
  • Partition + Cluster birlikte en güçlü kombinasyon (100x-5500x hız).
  • require_partition_filter = true her zaman production tablolarında aç.
  • partition_expiration_days ile eski veri otomatik silinir, storage maliyeti düşer.
  • Bytes processed azaldıkça fatura düşer — bu yüzden performans optimizasyonu = maliyet optimizasyonu.

Sıradaki

BigQuery ML: SQL içinde makine öğrenmesi →