Azure PostgreSQL üzerinde Citus ile dağıtık sorgu kurulumu
Selamlar, bu yazımızda Azure Database for PostgreSQL Flexible Server üzerinde Citus eklentisini kurmaya, tabloları nasıl dağıttığımıza ve dağıtık sorguların kafa yormadan nasıl yazılacağına bakacağız. Konu biraz teknik ama ben olabildiğince sade tutmaya çalışacağım, hadi başlayalım.
PostgreSQL belli bir veriyi rahatça idare ediyor ama trafik büyüdükçe ya makineyi şişiriyorsunuz (scale up) ya da yatayda büyüyorsunuz (scale out). RAM ve CPU'nun da bir tavanı var, bir noktadan sonra cüzdan da, donanım da yetmiyor. İşte Citus tam bu noktada, tanıdık SQL arayüzünden kopmadan veriyi birden fazla PostgreSQL düğümüne yaymanızı sağlıyor.
Citus nedir?
Citus aslında bir PostgreSQL eklentisi (extension); tek düğümlü veri tabanınızı dağıtık bir kümeye çeviriyor. Üç temel iş yapıyor:
- Tabloları bir dağıtım sütununa (distribution column) göre shard'lara böler.
- Sorguları bu sütuna göre ilgili düğüme yönlendirir.
- Aggregation ve join'leri düğümler arasında paralel çalıştırır.
Mimaride bir koordinatör (coordinator) düğümü ve birden fazla worker düğümü oluyor. Uygulama sadece koordinatöre bağlanıyor, sahnenin arkasını bilmesine gerek yok. Dışarıdan bakınca sıradan bir PostgreSQL gibi görünüyor.
Flexible Server'da Citus'u açmak
Eski Hyperscale (Citus) ürününü kullanmaya gerek yok artık; Citus, Flexible Server'da bir extension olarak geliyor. Önce parametre tarafında izin vermemiz gerekiyor:
az postgres flexible-server parameter set \
--resource-group rg-citus-demo \
--server-name pg-coord \
--name azure.extensions \
--value "citus"
az postgres flexible-server parameter set \
--resource-group rg-citus-demo \
--server-name pg-coord \
--name shared_preload_libraries \
--value "citus"
az postgres flexible-server restart \
--resource-group rg-citus-demo \
--name pg-coord
Sunucu yeniden başladıktan sonra veri tabanına bağlanıp eklentiyi etkinleştirelim:
CREATE EXTENSION IF NOT EXISTS citus;
SELECT * FROM citus_version();
shared_preload_libraries ayarını es geçerseniz CREATE EXTENSION adımında hata alırsınız; ben de ilk denememde bunu unutmuştum.
Worker düğümleri ekleme
Gerçek dağıtık kurulum için ayrı Flexible Server instance'larını worker olarak kayıt ediyoruz. İki worker yeterli, sonradan istediğiniz zaman büyütebilirsiniz:
SELECT citus_add_node('pg-worker-1.postgres.database.azure.com', 5432);
SELECT citus_add_node('pg-worker-2.postgres.database.azure.com', 5432);
SELECT * FROM citus_get_active_worker_nodes();
Tabloları dağıtmak
Citus'ta verdiğiniz en kritik karar dağıtım sütununun seçimi. Genelde sorgularda WHERE şartında geçen, kardinalitesi yüksek ve verinin doğal sahibi olan bir sütun seçmek gerekiyor. Multi-tenant bir SaaS senaryosunda bu doğal olarak company_id oluyor.
CREATE TABLE companies (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255),
plan VARCHAR(50),
created_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE events (
id BIGSERIAL,
company_id BIGINT,
user_id BIGINT,
event_type VARCHAR(50),
payload JSONB,
created_at TIMESTAMP DEFAULT NOW(),
PRIMARY KEY (company_id, id)
);
SELECT create_distributed_table('companies', 'id');
SELECT create_distributed_table('events', 'company_id');
Aynı sütunla dağıtılan tablolar co-location denilen şeyle birbirine yapışıyor; aynı şirketin verisi aynı düğümde duruyor ve join'ler ağ üzerinden veri taşımadan, lokal olarak çalışıyor. Performansın can damarı tam olarak bu.
Küçük lookup tabloları için ise reference table kullanın; bu tablolar her düğüme kopyalanıyor:
CREATE TABLE plans (id SERIAL PRIMARY KEY, name VARCHAR(50), price DECIMAL(10,2));
SELECT create_reference_table('plans');
Dağıtık sorgu yazarken nelere dikkat etmeli?
Bir SQL'in tek düğüme mi yoksa tüm worker'lara mı düşeceğini belirleyen şey, dağıtım sütununu sorguya dahil edip etmediğiniz. Dahil ederseniz tek node'a iniyor, etmezseniz fan-out olup tüm cluster çalışıyor. İkisi de geçerli senaryolar:
-- Tek node'da çalışır, hızlı
SELECT COUNT(*) FROM events WHERE company_id = 42;
-- Tüm node'larda paralel çalışır, sonuçlar birleştirilir
SELECT date_trunc('day', created_at) AS gun, COUNT(*) AS toplam
FROM events
WHERE created_at > NOW() - INTERVAL '7 days'
GROUP BY gun
ORDER BY gun;
Bence bir sorgunun nasıl çalışacağına dair şüpheniz varsa, doğrudan EXPLAIN (ANALYZE, VERBOSE) ile bakın; Citus planı dağıtık ayrıntılarıyla yazıyor.
Sık karşılaşılan hatalar
- Yanlış dağıtım sütunu seçmek: Düşük kardinaliteli ya da sorgularda nadir görünen bir sütun seçerseniz hot shard'lar oluşur. Bir node şişer, diğerleri boş durur.
- Co-location'ı atlamak: İlişkili tabloları farklı sütunlardan dağıtırsanız her join veriyi network üzerinden taşır. Latency uçar.
- Foreign key'i unutmak: Dağıtık tablolar arasındaki foreign key'lerin dağıtım sütununu da içermesi gerekiyor; aksi halde Citus hata veriyor.
- Rebalance'ı production'da plansız çalıştırmak:
citus_rebalance_start()downtime istemese de I/O yükü doğuruyor. Trafik düşükken tetiklemek mantıklı.
Kapanış
Bu yazıda Azure Flexible Server üzerinde Citus'un nasıl açıldığına, tabloların nasıl dağıtıldığına ve sorgu yazarken nelere dikkat etmek gerektiğine baktık. Şahsi kanaatim, multi-tenant SaaS uygulamaları için Citus oldukça doğal bir seçenek; tenant id'niz zaten hem iş hem de veri tabanı tarafında ayrım anahtarı oluyor. Tek bir koordinatörle başlayın, veri büyüdükçe worker ekleyin, gerisi kendiliğinden geliyor. Umarım faydalı olur, bir sonraki yazıda görüşmek üzere.
