PostgreSQL Sharding ve GORM ile Kullanim: Detayli Teknik Rehber
Tek bir PostgreSQL instance'i bir noktadan sonra CPU, IOPS, storage ve baglanti limitlerine takilir. Vertical scaling bir sure daha nefes aldirir ama buyume devam ediyorsa er ya da gec yatay olceklenme ihtiyaci dogar. Bu noktada sharding devreye girer.
Bu yazida PostgreSQL sharding modelini, tasarim kararlarini ve Go ekosisteminde GORM ile nasil uygulanabilecegini detayli olarak ele aliyoruz.
Sharding Nedir?
Sharding, veriyi birden fazla bagimsiz veritabani node'una yatay olarak bolme teknigidir. Her node datanin bir alt kumesini tutar. Uygulama katmani, kaydin hangi shard'da oldugunu bilir ve sorguyu ilgili node'a yonlendirir.
Cizim: Router + Shard Mimarisi
+-----------------------+
| API / Service |
+-----------+-----------+
|
shard key hesapla
|
+-----------v-----------+
| Query Router |
| (hash/range map) |
+----+----------+-------+
| |
+----------+--+ +--+----------+
| Shard-01 | | Shard-02 |
| PostgreSQL | | PostgreSQL |
+------+-----+ +------+------+
| |
| |
+---v---+ +---v---+
| Read | | Read |
|Replica| |Replica|
+-------+ +-------+
Bu yapida "global tek tablo" yoktur; her shard kendi tablosunu tutar.
Ne Zaman Partitioning Yetmez, Sharding Gerekir?
Partitioning ve sharding farkli problemleri cozer:
- Partitioning: Tek cluster icinde tablo yonetimi ve pruning optimizasyonu
- Sharding: Veriyi birden fazla bagimsiz cluster/node'a dagitma
Asagidaki durumlarda sharding ciddi aday olur:
- Tek node kaynak limitlerine duzenli yaklasma
- Yazma trafigi tek node'u saturate etmesi
- Multi-tenant modelde tenant bazli izolasyon ihtiyaci
- Buyuk veri hacminde operational blast radius'i kucultme hedefi
Shard Key Secimi (En Kritik Karar)
Yanlis shard key tum mimariyi kilitleyebilir. Iyi shard key su ozellikleri tasir:
- Yuksek kardinalite
- Duzgun dagilim
- Sorgu desenleriyle uyum
- Gelecekte yeniden shardlama maliyetini minimize etme
Yaygin yaklasimlar
- Tenant ID tabanli
- SaaS sistemlerde en yaygin
- Veri izolasyonu ve uyumluluk avantajli
- User ID hash tabanli
- Genis user dagiliminda iyi load balancing
- Zaman tabanli shard
- Bazi event sistemlerinde kullanilir ancak hotspot riski vardir
Cizim: Hash Tabanli Yönlendirme
shard = hash(tenant_id) % N
tenant_1001 -> hash -> 1 -> shard_1
tenant_1002 -> hash -> 0 -> shard_0
tenant_1003 -> hash -> 3 -> shard_3
tenant_1004 -> hash -> 1 -> shard_1
Not: N degistiginde tum dagilim bozulabilir. Bu nedenle consistent hashing veya virtual node stratejileri sik kullanilir.
PostgreSQL Tarafinda Shard Semasi
Her shard ayni schema'yi tasir:
CREATE TABLE orders (
id bigserial PRIMARY KEY,
tenant_id bigint NOT NULL,
customer_id bigint NOT NULL,
amount numeric(12,2) NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX idx_orders_tenant_created
ON orders (tenant_id, created_at DESC);
Uygulama tarafinda kural net: tenant_id olmadan sorgu yok. Aksi halde cross-shard scan kacirilmaz hale gelir.
GORM ile Sharding Mimarisine Giris
GORM tek basina "dagitimi otomatik yoneteyim" demez. Genellikle uygulama katmaninda bir router katmani yazilir ve dogru *gorm.DB pool secilir.
1) Shard Manager yapisi
type ShardManager struct {
shards []*gorm.DB
}
func (sm *ShardManager) shardIndex(tenantID int64) int {
h := fnv.New32a()
_, _ = h.Write([]byte(strconv.FormatInt(tenantID, 10)))
return int(h.Sum32()) % len(sm.shards)
}
func (sm *ShardManager) DBForTenant(tenantID int64) *gorm.DB {
return sm.shards[sm.shardIndex(tenantID)]
}
2) Repository katmaninda routing
type Order struct {
ID int64 `gorm:"primaryKey"`
TenantID int64 `gorm:"index"`
CustomerID int64
Amount float64
CreatedAt time.Time
}
type OrderRepo struct {
shardManager *ShardManager
}
func (r *OrderRepo) Create(ctx context.Context, o *Order) error {
db := r.shardManager.DBForTenant(o.TenantID).WithContext(ctx)
return db.Create(o).Error
}
func (r *OrderRepo) ListByTenant(ctx context.Context, tenantID int64, limit int) ([]Order, error) {
db := r.shardManager.DBForTenant(tenantID).WithContext(ctx)
var out []Order
err := db.Where("tenant_id = ?", tenantID).
Order("created_at desc").
Limit(limit).
Find(&out).Error
return out, err
}
Bu modelde uygulama verinin hangi shard'a gidecegini deterministik olarak bilir.
Cross-Shard Query Problemi
Sharding'in en zorlu alani budur. "Tum tenant'larda aylik toplam gelir" gibi sorgular dogal olarak tum shardlara dagilir.
Yaklasimlar:
- Uygulama seviyesinde parallel fan-out + aggregate
- Ayrik analytics pipeline (ETL -> DWH)
- Materialized summary tablolar
OLTP shard cluster'ini OLAP sorgulari ile yormamak genellikle en dogru secimdir.
Transaction ve Tutarlilik Gercegi
Tek shard icindeki transactionlar kolaydir. Birden fazla shard'i kapsayan transactionlar ise dagitik sistem problemidir.
Pratik cozumler:
- Is akisini tek shard sinirlarinda tasarlamak
- SAGA / outbox pattern kullanmak
- Idempotent operasyonlar tasarlamak
2PC benzeri yaklasimlar teoride guzel, pratikte operasyonel maliyeti yuksektir.
Connection Pool ve Operasyonel Ayarlar
Shard sayisi arttikca toplam DB baglanti sayisi patlayabilir.
GORM + pgx / database/sql tarafinda:
- Her shard icin makul
MaxOpenConns MaxIdleConnsveConnMaxLifetimetuning- Read replica kullaniminda read/write ayrimi
Basit bir kontrol:
toplam_conn = shard_sayisi * servis_instance_sayisi * max_open_conns
Bu sayi PostgreSQL limitlerini asmamali.
Migration Stratejisi
Monolitik tek DB'den sharding'e gecis genellikle kademeli yapilir:
- Shard map tablosu / servis katmani ekle
- Yeni tenant'lari shard'li yapida ac
- Eski tenant'lari batch halinde tasima (dual write opsiyonel)
- Dogrulama (count/checksum/domain-level invariants)
- Eski path'i kapatma
Keskin bir big-bang gecis yerine adim adim ilerlemek risk ve downtime'i azaltir.
Gozlemlenebilirlik Checklist'i
- Shard bazli p95/p99 latency
- Shard bazli QPS ve error rate
- Uneven shard dagilimi (hot shard)
- Replica lag
- Connection saturation
Hot shard tespiti erken yapilmazsa tum sistem performansi en yavas shard'a baglanir.
Sık Yapılan Hatalar
- Shard key'i query patternlerinden bagimsiz secmek
- Cross-shard query hacmini hafife almak
- Rebalancing planini en basa koymamak
- Observability ve shard-level alarmlari kurmamak
Sonuc
PostgreSQL sharding, tek node sinirlarini asmak icin guclu bir mimari yaklasimdir. Ancak basari sadece "veriyi bolmekle" gelmez; shard key tasarimi, query routing, transaction sinirlari ve operasyonel olgunluk birlikte ele alinmalidir.
GORM ile uygulama tarafinda temiz bir shard router/repository mimarisi kuruldugunda hem kod okunabilirligi korunur hem de sistem buyumeye hazir hale gelir.