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):
| Versiyon | Bytes processed | Süre | Fatura |
|---|---|---|---|
| Unoptimized | 85 GB | 18 s | $0.55 |
| Partitioned | 250 MB | 1.4 s | $0.0016 |
| Partition + Cluster | 12 MB | 0.4 s | $0.0001 |
Cluster ekleyince 20x daha fazla kazanç. Maliyet farkı: 5500x.
Dikkat Edilecekler
- Cluster sırası önemli: Sık filtrelenen sütunu önce yaz.
CLUSTER BY user_id, country→ user_id dominant. - Max 4 cluster sütunu: Fazlası işlemci zamanını yer, sıralama faydasını öldürür.
- Partition pruning önce, cluster pruning sonra: BigQuery önce tarih filtresiyle partition eler, kalan içinde cluster’a göre blok atlar.
- Yeniden cluster’a alma: Mevcut tabloya
ALTER TABLE ... CLUSTER BYekleyebilirsin, BigQuery arka planda yavaşça yeniden organize eder (backfill). Hemen sorgulamayacaksan yeni tablo oluşturup eskisini silmek daha hızlı. - 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 = trueher zaman production tablolarında aç.partition_expiration_daysile eski veri otomatik silinir, storage maliyeti düşer.- Bytes processed azaldıkça fatura düşer — bu yüzden performans optimizasyonu = maliyet optimizasyonu.