Buyukweb
PostgreSQL Kurulum ve Temel Yönetim Rehberi 2026: VDS Üzerinde Production

PostgreSQL Kurulum ve Temel Yönetim Rehberi 2026: VDS Üzerinde Production

PostgreSQL 17 kurulumu, pg_hba.conf, postgresql.conf tuning, role/database/schema, MVCC ve autovacuum, EXPLAIN ANALYZE, WAL + PITR ve streaming replication — Buyukweb VDS perspektifinden DBA pratik rehberi.

Büyükweb Editör EkibiHosting, Sunucu ve Sistem Yönetimi Editörü21 dakika okuma

PostgreSQL Kurulum ve Temel Yönetim Rehberi 2026: VDS Üzerinde Sıfırdan Production'a

PostgreSQL — özellikle 16 ve 17 sürümleriyle birlikte — JSONB indeksleme, generated column, hot standby replication ve gelişmiş extension ekosistemi gibi özelliklerle 2026'da ciddi web uygulamaları için ilk tercih olmaya devam ediyor. Türkiye'de barındırılan bir VDS üzerinde PostgreSQL'i apt install adımından point-in-time recovery'ye kadar production'a hazır hale getirmek için pratik bir DBA rehberine ihtiyacınız varsa bu yazı tam size göre.

Bu rehberde Buyukweb VDS perspektifinden Ubuntu 22.04 üzerinde PostgreSQL 16/17 kurulumu, pg_hba.conf ile auth katmanı, postgresql.conf tuning değerleri, role/database/schema kavram farklılıkları, MVCC ve autovacuum, EXPLAIN ANALYZE okuma, WAL + PITR, streaming replication ve bağlantı havuzlamayı tek seferde ele alıyoruz. Komutlar gerçek bir DBA'nın günlük yaşadığı pratik akışla anlatılıyor — kopyala-yapıştır değil, neden şu değer şu rakam, bunu açıklıyoruz.

Buyukweb perspektifi — dürüst notlar: cPanel paylaşımlı hosting paketlerimizde standart veritabanı motoru MariaDB 10.6 LTS'dir; PostgreSQL paylaşımlı paketlerde standart olarak gelmez ve kullanıcı yönetim arayüzü kısıtlıdır. PostgreSQL 16/17'yi tam kontrolle (sürüm seçimi, extension yükleme, postgresql.conf özelleştirme, replication kurulumu) çalıştırmak için VDS root erişimi gereklidir. Bu rehberin tüm adımları Buyukweb VDS (Bursa Tier 3 veri merkezi, ₺250/ay başlangıç) üzerinde doğrulanmıştır.

Neden PostgreSQL? Open Source DB Tercihinde 2026 Tablosu

PostgreSQL'i seçmek için somut gerekçeler — pazarlama dilinden uzak:

  • Tam ACID + serileştirilebilir izolasyon: SERIALIZABLE izolasyon seviyesi gerçek anlamda çalışır; mali işlemler ve stok yönetimi için kritik.
  • JSONB veri tipi: İndekslenebilir binary JSON; document-store ihtiyaçlarınızın çoğunu tek motorda karşılar.
  • Extension ekosistemi: PostGIS (coğrafi sorgular), pg_trgm (fuzzy text search), pg_stat_statements (sorgu profilleme), TimescaleDB (zaman serisi), Citus (sharding), uuid-ossp, hstore, vector (pgvector — embedding araması).
  • Generated column ve constraint exclusion: Schema seviyesinde hesaplanmış sütunlar; partition pruning ile büyük tablolarda performans.
  • BSD-style PostgreSQL lisansı: Ticari kullanım için kısıt yok; vendor lock-in riski sıfır.
  • Akademik miras: Berkeley POSTGRES projesinin doğrudan devamı; standartlara (SQL:2016, SQL/JSON 2023) titiz uyum.
  • PostgreSQL Global Development Group her yıl bir ana sürüm yayınlar; her ana sürüm 5 yıl boyunca güvenlik güncellemesi alır. PostgreSQL 16 (Eylül 2023) için EOL Kasım 2028.

PostgreSQL vs MySQL: Temel Mimari Farklar

MySQL/MariaDB'den PostgreSQL'e geçenlerin sıklıkla şaşırdığı kavramsal farklar:

Konu PostgreSQL MySQL/MariaDB
Concurrency model MVCC (snapshot per transaction) InnoDB row-level lock + MVCC
Schema namespace Database > Schema > Table (3 seviye) Database > Table (2 seviye)
Kullanıcı kavramı ROLE (LOGIN ile user, NOLOGIN ile group) USER + ayrı GRANT tabanlı role
DDL transaction Tam (CREATE TABLE bile ROLLBACK olur) Çoğu DDL otomatik commit
JSON JSON (text) + JSONB (binary, indekslenebilir) JSON (binary, MySQL 8 sonrası)
Generated column STORED ve VIRTUAL (PG 12+) STORED ve VIRTUAL
Tablo kalıtım INHERITS (partitioning öncesi yaygındı) Yok
Regex POSIX + PCRE benzeri (~, ~*) POSIX, sınırlı
Replication Streaming (physical) + logical Statement / Row / Mixed binlog
Default encoding UTF8 (initdb anında belirlenir) utf8mb4 (önerilen)
Default port 5432 3306

Kritik fark: PostgreSQL'de bir "database" içine girip ayrı veritabanlarına USE ile geçemezsiniz; bağlantı kurulurken DB seçilir. Aynı bağlantı içinde "tüm database'lere" sorgu atılmaz. Schema farklı: aynı DB içinde mantıksal namespace; search_path ile sıralı aranır.

PostgreSQL 16 mı 17 mi? Sürüm Seçimi

2026 başı itibarıyla:

  • PostgreSQL 16 (Eylül 2023) — Production'da en yaygın; logical replication parallel apply, libpq sslnegotiation, pg_stat_io. EOL: Kasım 2028.
  • PostgreSQL 17 (Eylül 2024) — Vacuum bellek verimliliği, MERGE ... RETURNING, JSON_TABLE, incremental backup desteği. EOL: Kasım 2029.

Yeni kurulum için PostgreSQL 17 önerilir. Mevcut bir 14/15 kurulumu varsa pg_upgrade ile 17'ye geçiş test ortamında doğrulanmadan production'a alınmaz.

Ubuntu 22.04 VDS Üzerinde Kurulum (PGDG Reposu)

Buyukweb VDS'iniz Ubuntu 22.04 ile geldiyse Ubuntu'nun universe deposundaki sürüm güncel olmayabilir. PostgreSQL Global Development Group'un PGDG deposunu kullanın — her ana sürümü ayrı paket olarak sunar.

# Sistem paketlerini güncelle
sudo apt update && sudo apt upgrade -y

# PGDG repo anahtarı ve liste
sudo apt install -y curl ca-certificates gnupg lsb-release
sudo install -d /usr/share/postgresql-common/pgdg
sudo curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc \
    --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc

echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" | \
    sudo tee /etc/apt/sources.list.d/pgdg.list

sudo apt update
sudo apt install -y postgresql-17 postgresql-contrib-17

# Servis durumu
sudo systemctl status postgresql
sudo systemctl enable postgresql

# Sürüm kontrolü
sudo -u postgres psql -c "SELECT version();"

PGDG paketi kurulduktan sonra:

  • Veri dizini: /var/lib/postgresql/17/main
  • Yapılandırma: /etc/postgresql/17/main/postgresql.conf
  • Auth: /etc/postgresql/17/main/pg_hba.conf
  • Log: /var/log/postgresql/postgresql-17-main.log

postgres adlı sistem kullanıcısı otomatik oluşur ve PostgreSQL süper kullanıcısı olarak peer auth ile yerelden bağlanır.

AlmaLinux 9 / Rocky Linux 9 Üzerinde Kurulum

# PGDG repo
sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm

# Built-in modülü devre dışı bırak
sudo dnf -qy module disable postgresql

# PostgreSQL 17 kur
sudo dnf install -y postgresql17-server postgresql17-contrib

# Cluster initdb (encoding ve locale UTF8 + tr_TR önerilir)
sudo PGSETUP_INITDB_OPTIONS="--locale=tr_TR.UTF-8 --encoding=UTF8" \
    /usr/pgsql-17/bin/postgresql-17-setup initdb

# Servis aktif
sudo systemctl enable --now postgresql-17

RHEL ailesinde dosya yolları farklıdır: /var/lib/pgsql/17/data/ veri dizini, yapılandırma ve pg_hba.conf aynı dizin içindedir.

pg_hba.conf: Host-Based Authentication

pg_hba.conf (PostgreSQL'in kalbi denilebilecek dosya) hangi istemcinin, hangi DB'ye, hangi kullanıcıyla, hangi auth metoduyla bağlanabileceğini sırayla tanımlar. Satırlar yukarıdan aşağı okunur, ilk eşleşen kural uygulanır — sıralama kritik.

# TYPE   DATABASE   USER          ADDRESS         METHOD

# 1) Yerel postgres süper kullanıcısı (Linux peer)
local    all        postgres                       peer

# 2) Diğer yerel uygulamalar — SCRAM-SHA-256 ile parolayla
local    all        all                            scram-sha-256

# 3) Loopback IPv4 — uygulama 127.0.0.1'den bağlanırsa
host     all        all           127.0.0.1/32     scram-sha-256
host     all        all           ::1/128          scram-sha-256

# 4) Sadece belirli iç ağdan, sadece uygulama_db'ye, sadece uygulama_user
hostssl  uygulama_db uygulama_user 10.0.0.0/24    scram-sha-256

# 5) Replication için ayrı kural (streaming replication)
hostssl  replication replicator    10.0.0.50/32   scram-sha-256

# Genel internet erişimi açmayın — VDS'in pg portu (5432) firewall ile dışa kapalı tutulmalı

Auth Metodları Özeti

Metod Açıklama
trust Parola sormadan kabul — sadece izole test ortamı
peer Linux UID = PG kullanıcı adı eşleşmesi (sadece local)
md5 MD5 parola hash — eski; yeni kurulumlarda kullanmayın
scram-sha-256 PG 10+ önerilen parola yöntemi (default 14+)
cert TLS istemci sertifikası
ident OS-level kimlik eşlemesi (RFC 1413)
reject Açıkça reddet

pg_hba.conf değişikliği SELECT pg_reload_conf(); veya sudo systemctl reload postgresql ile aktif olur — restart gerekmez.

postgresql.conf Temel Tuning Değerleri

Buyukweb VDS'inde 8 GB RAM 4 vCPU bir cluster için pratik başlangıç değerleri:

# /etc/postgresql/17/main/postgresql.conf

# --- Bağlantı ---
listen_addresses = 'localhost'      # Sadece yerel; uygulama aynı VDS'de
                                    # Farklı sunucudan erişim için: '10.0.0.5,localhost'
port = 5432
max_connections = 100               # 100-200 üzeri için pgBouncer önerilir

# --- Bellek ---
shared_buffers = 2GB                # ~25% RAM (8GB RAM için 2GB)
effective_cache_size = 6GB          # ~75% RAM (planner için ipucu)
work_mem = 16MB                     # Her sort/hash op başına; (RAM-shared_buffers)/max_conn/2
maintenance_work_mem = 512MB        # VACUUM, CREATE INDEX için
huge_pages = try                    # Linux huge pages varsa kullan

# --- WAL ve checkpoint ---
wal_level = replica                 # replica (default) / logical
max_wal_size = 2GB
min_wal_size = 80MB
checkpoint_timeout = 15min
checkpoint_completion_target = 0.9
wal_compression = on                # PG 15+ pglz veya zstd

# --- Disk planner (NVMe SSD için) ---
random_page_cost = 1.1              # NVMe için ~1.1; HDD için 4.0
effective_io_concurrency = 200      # SSD/NVMe yüksek; HDD = 2

# --- Paralel sorgu (4 vCPU) ---
max_worker_processes = 4
max_parallel_workers_per_gather = 2
max_parallel_workers = 4

# --- Autovacuum (aşağıda detay) ---
autovacuum = on
autovacuum_max_workers = 3
autovacuum_naptime = 30s

# --- Loglama ---
log_destination = 'stderr'
logging_collector = on
log_directory = 'log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_min_duration_statement = 500    # 500ms üstü sorguları logla
log_checkpoints = on
log_connections = on
log_disconnections = on
log_lock_waits = on
log_temp_files = 0                  # Tüm temp dosyaları logla (work_mem küçükse uyarı)
log_line_prefix = '%m [%p] %q%u@%d '

# --- Statistics ---
shared_preload_libraries = 'pg_stat_statements'
pg_stat_statements.track = all

Tuning Hızlı Kuralları

Parametre Hesap
shared_buffers RAM × 0.25 (max 8GB üstü genelde fayda azalır)
effective_cache_size RAM × 0.75 (gerçek bellek tahsisi değil; planner ipucu)
work_mem (RAM − shared_buffers) / (max_connections × 2)
maintenance_work_mem 256MB-1GB (VACUUM ve REINDEX hızını etkiler)
max_wal_size Yazma yoğunluğuna göre 2-16 GB; checkpoint sıklığını dengeler
random_page_cost NVMe: 1.1, SAS SSD: 1.5, HDD: 4.0
# Değişiklik sonrası
sudo systemctl restart postgresql   # listen_addresses, max_connections, shared_buffers için
# Diğer çoğu için
sudo -u postgres psql -c "SELECT pg_reload_conf();"

Role, Database ve Schema Kavramı

PostgreSQL'de "kullanıcı" yoktur — yalnızca role vardır. LOGIN özelliği olan role bir kullanıcıdır; NOLOGIN bir gruptur.

-- Süper kullanıcı olarak (sudo -u postgres psql)

-- Uygulama login role
CREATE ROLE uygulama_user WITH LOGIN PASSWORD 'GucluP@rola2026' NOSUPERUSER NOCREATEDB NOCREATEROLE;

-- Salt-okuma rolü (group)
CREATE ROLE readonly_group NOLOGIN;

-- Bir okuyucu kullanıcı, gruba üye
CREATE ROLE rapor_user WITH LOGIN PASSWORD 'OkumaP@rola';
GRANT readonly_group TO rapor_user;

-- Veritabanı oluştur, sahibi uygulama_user
CREATE DATABASE uygulama_db OWNER uygulama_user
    ENCODING 'UTF8'
    LC_COLLATE 'tr_TR.UTF-8'
    LC_CTYPE 'tr_TR.UTF-8'
    TEMPLATE template0;

-- Schema yapısı (uygulama_db içine bağlanıp çalıştırılır: \c uygulama_db)
CREATE SCHEMA app AUTHORIZATION uygulama_user;
CREATE SCHEMA logs AUTHORIZATION uygulama_user;

-- Schema yetkileri
GRANT USAGE ON SCHEMA app TO readonly_group;
GRANT SELECT ON ALL TABLES IN SCHEMA app TO readonly_group;
ALTER DEFAULT PRIVILEGES IN SCHEMA app
    GRANT SELECT ON TABLES TO readonly_group;

-- search_path: schema arama sırası
ALTER ROLE uygulama_user SET search_path = app, public;

search_path aktif sırayı belirler. SELECT * FROM kullanicilar; sorgusunda PostgreSQL önce app.kullanicilar, sonra public.kullanicilar arar. Yanlış schema'ya yazmak istemiyorsanız search_path netleştirmek üretimde kritiktir.

psql: Günlük DBA Komutları

psql bir terminal istemcisi değil — adeta bir editör; meta-komutlar (\-prefixed) inanılmaz hız kazandırır.

$ sudo -u postgres psql
psql (17.0)
Type "help" for help.

postgres=# \l                      -- Tüm DB'leri listele (size, encoding, owner)
postgres=# \c uygulama_db          -- DB'ye bağlan
You are now connected to database "uygulama_db".

uygulama_db=# \dt                  -- public schema tabloları
uygulama_db=# \dt app.*            -- app schema tabloları
uygulama_db=# \d app.kullanicilar  -- Tablo yapısı + indeksler + constraint
uygulama_db=# \du                  -- Tüm role'ler
uygulama_db=# \dn                  -- Schema'ları listele
uygulama_db=# \dx                  -- Yüklü extension'lar
uygulama_db=# \df app.*            -- app schema fonksiyonları
uygulama_db=# \timing on           -- Sorgu süresi ölçümünü aç
uygulama_db=# \x                   -- Genişletilmiş çıktı (uzun satır için)
uygulama_db=# \e                   -- Sorguyu $EDITOR ile düzenle
uygulama_db=# \i /tmp/script.sql   -- Dosyadan SQL çalıştır
uygulama_db=# \copy urunler FROM 'urunler.csv' CSV HEADER
uygulama_db=# \?                   -- Tüm meta-komut yardım
uygulama_db=# \q                   -- Çıkış

COPY ... FROM PostgreSQL'in en hızlı toplu yükleme aracıdır — INSERT'lerden 10-100x daha hızlı. CSV import için \copy istemci tarafı dosyayı okur, COPY sunucu tarafını.

CRUD ve Transaction Yönetimi

-- Tablo (zengin tip kullanımı)
CREATE TABLE app.urunler (
    id BIGSERIAL PRIMARY KEY,
    sku VARCHAR(32) NOT NULL UNIQUE,
    isim TEXT NOT NULL,
    fiyat NUMERIC(12,2) NOT NULL CHECK (fiyat >= 0),
    stok INTEGER NOT NULL DEFAULT 0,
    ozellikler JSONB,
    etiketler TEXT[],
    aktif BOOLEAN NOT NULL DEFAULT TRUE,
    olusturulma TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    guncelleme TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- INSERT ... RETURNING — eklenen satırı geri döner (auto-increment ID için pratik)
INSERT INTO app.urunler (sku, isim, fiyat, stok, ozellikler, etiketler)
VALUES ('NB-001', 'İş Laptopu 14"', 24999.90, 25,
        '{"marka":"Generic","ram":"16GB","ssd":"512GB"}',
        ARRAY['laptop','i5','ofis'])
RETURNING id, isim;

-- JSONB sorguları
SELECT id, isim FROM app.urunler WHERE ozellikler->>'marka' = 'Generic';
SELECT id FROM app.urunler WHERE ozellikler @> '{"ram":"16GB"}';

-- Array sorgu
SELECT id FROM app.urunler WHERE 'laptop' = ANY(etiketler);

-- Transaction ve savepoint
BEGIN;
    UPDATE app.urunler SET stok = stok - 1 WHERE sku = 'NB-001';
    SAVEPOINT after_decrement;

    UPDATE app.urunler SET aktif = FALSE WHERE stok = 0;
    -- Hata olursa savepoint'e dön
    -- ROLLBACK TO SAVEPOINT after_decrement;
COMMIT;

-- İzolasyon seviyesi (mali işlem için)
BEGIN ISOLATION LEVEL SERIALIZABLE;
    -- iş mantığı
COMMIT;

SERIALIZABLE izolasyonda phantom read engellenir; PostgreSQL gerekirse could not serialize access hatasıyla işlemi durdurur — uygulama bunu yakalayıp retry mantığı kurmalı.

MVCC, Dead Tuple, VACUUM ve Autovacuum

PostgreSQL Multi-Version Concurrency Control (MVCC) kullanır. Bir UPDATE eski satırı silmez; yeni bir versiyon yazar, eskisini "dead tuple" olarak işaretler. Aktif okuyucular kendi snapshot'larındaki eski versiyonu görebilsin diye eski versiyonlar tutulur.

Sonuç: yoğun yazma altında tablolar bloat eder. Dead tuple'ları temizleyen mekanizma VACUUM'dur, otomatik versiyonu autovacuum.

-- Dead tuple ölçümü
SELECT schemaname, relname,
       n_live_tup,
       n_dead_tup,
       round(100.0 * n_dead_tup / NULLIF(n_live_tup + n_dead_tup, 0), 2) AS dead_pct,
       last_vacuum,
       last_autovacuum
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 10;

-- Manuel vacuum (tabloyu kilitlemez; autovacuum'un sıkışık olduğu durumlar için)
VACUUM (VERBOSE, ANALYZE) app.urunler;

-- VACUUM FULL — disk alanı geri kazanır AMA tabloyu ACCESS EXCLUSIVE lock ile kilitler
-- Üretimde nadiren; ekonomik değildir. pg_repack extension'ı online alternatif sunar.
VACUUM FULL app.eski_loglar;

Autovacuum Tuning İpuçları

# postgresql.conf — yoğun yazılan tablo varsa daha agresif
autovacuum_vacuum_scale_factor = 0.1     # %10 dead tuple → vacuum (default 0.2)
autovacuum_analyze_scale_factor = 0.05   # %5 değişiklik → analyze (default 0.1)
autovacuum_vacuum_cost_limit = 2000      # Daha az throttle (default 200)

Tek tablo bazında:

ALTER TABLE app.aktif_oturumlar SET (
    autovacuum_vacuum_scale_factor = 0.05,
    autovacuum_analyze_scale_factor = 0.02
);

pg_stat_user_tables ekran çıktısında dead_pct > 20 olan tablo varsa autovacuum yetişmiyordur — ya autovacuum_max_workers artırın ya da tablo özelinde scale_factor düşürün.

Index Türleri ve Ne Zaman Hangisi

PostgreSQL'in index çeşitliliği MySQL'den belirgin biçimde geniş:

Türü Kullanım Örnek
B-Tree (default) Eşitlik + aralık (=, <, BETWEEN, ORDER BY) CREATE INDEX ON urunler(fiyat);
Hash Sadece eşitlik; nadiren faydalı, B-Tree çoğu zaman yeter CREATE INDEX ... USING HASH(sku);
GIN JSONB, full-text search, array, hstore CREATE INDEX ON urunler USING GIN(ozellikler);
GiST Geometric, PostGIS, range type, fuzzy text (pg_trgm) CREATE INDEX ON poligonlar USING GIST(geom);
SP-GiST Non-balanced tree; quad-tree, k-d tree Nadiren özel kullanım
BRIN Çok büyük zaman serisi tablolarda fiziksel sırayla korelasyon CREATE INDEX ON loglar USING BRIN(zaman);
-- Composite index (sıra önemli — en sık WHERE'de kullanılan başta)
CREATE INDEX idx_urunler_aktif_fiyat ON app.urunler(aktif, fiyat) WHERE aktif = TRUE;

-- Partial index — sadece aktif ürünleri indeksle (boyut küçük, hızlı)
CREATE INDEX idx_urunler_aktif ON app.urunler(olusturulma) WHERE aktif = TRUE;

-- Expression index
CREATE INDEX idx_urunler_lower_isim ON app.urunler(LOWER(isim));

-- JSONB GIN
CREATE INDEX idx_urunler_ozellik_gin ON app.urunler USING GIN(ozellikler);

-- BRIN: 100M satırlı log tablosunda
CREATE INDEX idx_loglar_brin ON loglar USING BRIN(zaman) WITH (pages_per_range = 32);

pg_stat_user_indexes üzerinden kullanılmayan indeksler tespit edilebilir — idx_scan = 0 olanlar muhtemelen drop edilmeli (yazma performansını yavaşlatırlar).

EXPLAIN ANALYZE: Sorgu Planını Okumak

Her DBA'nın günlük aracı. EXPLAIN planı tahmin eder, EXPLAIN ANALYZE gerçekten çalıştırır ve süreyi raporlar.

EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
SELECT u.id, u.isim, k.email
FROM app.urunler u
JOIN app.kullanicilar k ON k.id = u.olusturan_id
WHERE u.aktif = TRUE AND u.fiyat > 1000
ORDER BY u.olusturulma DESC
LIMIT 50;

Çıktı parçası:

                                                              QUERY PLAN
-----------------------------------------------------------------------
 Limit  (cost=0.85..123.45 rows=50 width=72) (actual time=0.124..2.456 rows=50 loops=1)
   ->  Nested Loop  (cost=0.85..2456.78 rows=1000 width=72) (actual time=0.121..2.401 rows=50 loops=1)
         Buffers: shared hit=145 read=8
         ->  Index Scan Backward using idx_urunler_olusturulma on urunler u
               Index Cond: (aktif = true)
               Filter: (fiyat > 1000)
               Rows Removed by Filter: 12
         ->  Index Scan using kullanicilar_pkey on kullanicilar k
               Index Cond: (id = u.olusturan_id)
 Planning Time: 0.234 ms
 Execution Time: 2.512 ms

Plan Okuma Hızlı Rehberi

Operatör Anlam
Seq Scan Tablo baştan sona okunuyor — küçük tablo iyi, büyük tabloda kötü işaret
Index Scan Index üzerinden okuma — genelde iyi
Index Only Scan Sadece indeks yeterli (covering index) — en hızlı
Bitmap Heap Scan Index ile önce tabloda yer bul, sonra topla — orta yoğun WHERE'de yaygın
Nested Loop Küçük outer × index lookup — küçük sonuç setinde iyi
Hash Join Büyük tablolar arası eşitlik join — tipik orta-büyük veri
Merge Join İki sıralı set birleştirme

actual time ile cost arasında büyük fark varsa istatistikler güncel değildir → ANALYZE tablo;. Rows Removed by Filter çok yüksek ise indekste WHERE koşulu yer almıyordur — partial veya composite index düşünün.

WAL ve Point-in-Time Recovery (PITR)

Write-Ahead Log her yazma işleminin değişikliğini önce log dosyasına yazar; çökmede bütünlük garantisi WAL'dir. PITR, baz alınmış (base backup) bir snapshot + ardından gelen WAL dosyalarını ileri oynatarak istenen ana geri yükleme yapar.

# postgresql.conf — WAL arşivleme aç
wal_level = replica
archive_mode = on
archive_command = 'test ! -f /var/backups/pg_wal_archive/%f && cp %p /var/backups/pg_wal_archive/%f'
archive_timeout = 60        # En geç 60sn'de bir WAL dosyasını döndür

Manuel base backup:

# Replication kullanıcısı
sudo -u postgres psql -c "CREATE ROLE repuser WITH REPLICATION LOGIN PASSWORD 'RepP@rola';"

# Base backup (online — uygulamayı durdurmaz)
sudo -u postgres pg_basebackup -D /var/backups/pg_base/2026-05-10 \
    -F tar -z -P -U postgres

pg_basebackup PostgreSQL ile gelen yerleşik araçtır. Daha gelişmiş senaryolarda (incremental backup, paralel restore, WAL archive yönetimi) pgBackRest veya Barman topluluk araçları tercih edilir.

# pgBackRest tipik kurulum (apt veya dnf)
sudo apt install pgbackrest

# /etc/pgbackrest/pgbackrest.conf örnek
# [global]
# repo1-path=/var/lib/pgbackrest
# repo1-retention-full=2
# [main]
# pg1-path=/var/lib/postgresql/17/main

PITR senaryosu — "yanlışlıkla DROP TABLE çalıştı, 14:30'a dön":

# 1) PostgreSQL'i durdur
sudo systemctl stop postgresql

# 2) Mevcut data dizinini taşı (silmek yerine)
sudo mv /var/lib/postgresql/17/main /var/lib/postgresql/17/main.broken

# 3) Base backup'ı geri yükle
sudo -u postgres tar -xzf /var/backups/pg_base/2026-05-10/base.tar.gz \
    -C /var/lib/postgresql/17/main

# 4) recovery.signal dosyası oluştur (PG 12+ için)
sudo -u postgres touch /var/lib/postgresql/17/main/recovery.signal

# 5) postgresql.conf'a recovery hedefi ekle
# restore_command = 'cp /var/backups/pg_wal_archive/%f %p'
# recovery_target_time = '2026-05-10 14:30:00 +03'

# 6) Servisi başlat — recovery otomatik
sudo systemctl start postgresql

PostgreSQL hedef zamana kadar WAL'ları oynatır, sonra recovery moduna geçer. Promotion için pg_promote() çağrılır.

Streaming Replication: Primary + Hot Standby

Yüksek erişilebilirlik veya okuma replikası için streaming replication. Primary WAL'ı ağ üzerinden standby'a stream eder.

# Primary: postgresql.conf
wal_level = replica
max_wal_senders = 5
wal_keep_size = 1GB
synchronous_commit = on              # senkron mod (gecikmeli ama veri kaybı yok)
synchronous_standby_names = 'standby1'
# Primary: pg_hba.conf
hostssl  replication  repuser  10.0.0.50/32  scram-sha-256

Standby kurulumu:

# Standby sunucuda — boş data dizini
sudo systemctl stop postgresql
sudo rm -rf /var/lib/postgresql/17/main/*

# Primary'den base backup çek
sudo -u postgres pg_basebackup -h primary.dahili -U repuser \
    -D /var/lib/postgresql/17/main -P -R

# -R standby.signal ve primary_conninfo'yu otomatik yazar
sudo systemctl start postgresql

hot_standby = on (default) ile standby okuma sorguları kabul eder. Replikasyon gecikmesi:

-- Primary üzerinde
SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn,
       pg_wal_lsn_diff(sent_lsn, replay_lsn) AS replay_lag_bytes
FROM pg_stat_replication;

-- Standby üzerinde
SELECT now() - pg_last_xact_replay_timestamp() AS replication_delay;

Otomatik failover için Patroni + etcd/Consul topluluk çözümü standarttır; Patroni cluster lider seçimi, otomatik standby promotion ve split-brain önleme yönetir.

Bağlantı Havuzu: pgBouncer Notu

PostgreSQL her bağlantı için ayrı bir backend process açar (~5-10 MB RAM/bağlantı). 200+ eşzamanlı bağlantı için bağlantı havuzu kritik. Bu konuda Buyukweb blog'unda ayrıntılı yazımız var; özetle:

  • pgBouncer 6432 portunda dinler
  • Üç pool modu: session / transaction / statement (transaction en yaygın)
  • max_client_conn (uygulama tarafı) >> default_pool_size (gerçek PG bağlantısı)
  • Transaction mode'da prepared statement ile dikkat — uygulama tarafında prepareThreshold=0 veya benzer ayar

cPanel paylaşımlı paketlerimizde: PostgreSQL standart olarak gelmediğinden pgBouncer da paylaşımlı paketler kapsamında değildir. Self-host PostgreSQL + pgBouncer kombinasyonu VDS üzerinde uygulanır.

Yedekleme Stratejisi: Logical + Physical

Üretimde her ikisi de gerekli:

# Logical (pg_dump) — schema/veri SQL veya custom format
sudo -u postgres pg_dump -Fc -d uygulama_db -f /var/backups/uygulama_db.dump

# Belirli schema
sudo -u postgres pg_dump -Fc -n app -d uygulama_db -f /var/backups/app_schema.dump

# Tüm cluster (role'ler + DB'ler)
sudo -u postgres pg_dumpall > /var/backups/all_$(date +%F).sql

# Restore (custom format için)
sudo -u postgres pg_restore -d uygulama_db -j 4 /var/backups/uygulama_db.dump

# Physical (pg_basebackup) — yukarıda PITR bölümünde

Buyukweb VDS'lerinde JetBackup ile haftalık otomatik snapshot ve manual recurring seçenekleri kullanılabilir; veritabanı tutarlılığı için pg_dump çalıştıran cron + JetBackup ikisinin birlikte çalıştırılması önerilir (cron pg_dump, JetBackup file system snapshot).

# /etc/cron.daily/pgbackup — günlük logical dump (7 gün retention)
#!/bin/bash
DUMP_DIR=/var/backups/postgresql
mkdir -p \$DUMP_DIR
sudo -u postgres pg_dump -Fc uygulama_db > \$DUMP_DIR/uygulama_db_$(date +%F).dump
find \$DUMP_DIR -name "*.dump" -mtime +7 -delete

Buyukweb VDS'de Production Checklist

Bursa Tier 3 veri merkezimizdeki VDS'lerde PostgreSQL'i internete açmak yerine güvenli mimari:

  • Firewall (ufw veya firewalld) ile 5432 portu sadece uygulama sunucularına açık (varsayılan: dış kapalı)
  • Uygulama aynı VDS'de değilse SSH tunnel veya WireGuard üzerinden iç ağda
  • hostssl zorunlu, sertifika /etc/postgresql/17/main/server.crt
  • Role'ler least-privilege: uygulama_user NOSUPERUSER NOCREATEDB NOCREATEROLE
  • scram-sha-256 zorunlu, md5 ve trust kaldırıldı
  • log_min_duration_statement = 500; yavaş sorgu izleme
  • pg_stat_statements ile sorgu profilleme
  • Autovacuum izleniyor (pg_stat_user_tables haftalık denetim)
  • fail2ban PostgreSQL log filter eklendi (parola brute force engelleme)
  • WAL arşivleme + günlük pg_dump + JetBackup kombinasyonu
  • Disk doluluğu pg_database_size ve pg_total_relation_size ile izleniyor
  • Yıllık major sürüm yükseltme planı (pg_upgrade test ortamında doğrulandı)

cPanel Paylaşımlı Paketlerde PostgreSQL Durumu — Dürüst Açıklama

Bunu net belirtelim: Buyukweb cPanel paylaşımlı hosting paketlerinde standart veritabanı motoru MariaDB 10.6 LTS'dir. cPanel arayüzünde phpPgAdmin/PostgreSQL menüsü kısıtlıdır; küçük WordPress/PHP siteler için MariaDB tamamen yeterlidir ve bizim önerimizdir.

PostgreSQL kullanan bir uygulama (Django, Rails, Node.js + Sequelize/Prisma, Java + Spring) çalıştıracaksanız:

  • Küçük ölçek: VDS ile root erişim, kendi PostgreSQL'i yönetirsiniz
  • Orta ölçek: E5 v4 VDS ile NVMe IOPS avantajı
  • Büyük ölçek: Fiziksel Dedicated ile tam donanım kontrol

Hangisi sizin senaryonuza uygun, paket karşılaştırma sayfamıza bakabilir veya 0850 302 60 70 numaramızdan teknik ekibe sorabilirsiniz.

Sıkça Sorulan Sorular

PostgreSQL 16 mı 17 mi seçmeliyim?

Yeni kurulum için PostgreSQL 17. Vacuum bellek verimliliği, MERGE ... RETURNING ve JSON_TABLE 17'de geliyor. Mevcut bir 14/15 üretim sisteminiz varsa pg_upgrade'i test ortamında doğrulamadan production'a almayın; major upgrade extension uyumluluğu (örn. PostGIS, TimescaleDB) gerektirir.

work_mem değerini nasıl seçmeliyim?

Formül: (RAM − shared_buffers) / (max_connections × 2). 8 GB RAM, 100 max_connections, 2 GB shared_buffers için yaklaşık 30 MB. Karmaşık sort/hash içeren analitik sorgular için tek seans bazında SET work_mem = '256MB'; ile artırılabilir — global olarak yüksek tutmak bellek tükenmesi riski oluşturur (her bağlantı × her sort/hash op kadar tahsis eder).

autovacuum yetişmiyor, dead_pct sürekli %30 üstünde — ne yapmalıyım?

Önce pg_stat_progress_vacuum ile aktif vacuum süreçlerini izleyin. Çözümler: autovacuum_max_workers 3'ten 5-6'ya çıkarın, autovacuum_vacuum_cost_limit artırın (default 200, 1000-2000 yapın), problemli tablo özelinde ALTER TABLE ... SET (autovacuum_vacuum_scale_factor = 0.05);. Tablo zaten ciddi bloat olmuşsa pg_repack ile online reorganize edin (VACUUM FULL üretimde tabloyu kilitler).

Streaming replication mi logical replication mı?

Streaming (physical) replication tüm cluster'ı bir bütün olarak replike eder — disaster recovery ve okuma standby'ı için ideal. Logical replication (PG 10+) tablo seviyesinde, farklı major sürümler arası taşıma ve sadece bazı tabloları replike etme senaryolarında kullanılır. Tipik HA için streaming + Patroni; selektif veri taşıma için logical.

PostgreSQL'i internete (5432) açmak güvenli mi?

Hayır. Otomatik tarayıcılar (shodan benzeri tarama, brute force) varsayılan portu sürekli yokluyor. Best practice: VDS firewall'ında 5432 dış kapalı, uygulama aynı VDS veya iç ağda; uzaktan yönetim için SSH tunnel (ssh -L 5432:localhost:5432 user@vds) veya WireGuard. Mutlaka açmak gerekiyorsa: hostssl zorunlu + IP allowlist + scram-sha-256 + fail2ban.

Sonuç

PostgreSQL 17, doğru yapılandırılmış bir VDS üzerinde yıllarca güvenli ve hızlı çalışacak temeli ücretsiz olarak sunuyor. Bu rehberde apt install adımından autovacuum tuning ve PITR'a kadar olan yolu DBA gözüyle gezdik; production checklist ile kritik 12 maddeyi listeledik.

Buyukweb VDS (Bursa Tier 3, root yetkili, ₺250/ay başlangıç) üzerinde PostgreSQL 17 + uygun extension'lar tamamen sizin kontrolünüzdedir. cPanel paylaşımlı paketler MariaDB ile küçük PHP/WordPress sitelere uygundur; PostgreSQL stack için VDS veya üzeri bir paket gereklidir.

Teknik destek için 0850 302 60 70 numaralı destek hattımıza veya iletişim sayfamıza ulaşabilirsiniz.


İlgili Büyükweb Hizmetleri

PostgreSQL self-host için altyapı seçenekleri:

Sorularınız için 0850 302 60 70 numaralı destek hattımıza veya iletişim sayfamıza yazabilirsiniz.

Veritabanı Yönetimi İlgili Hizmetlerimiz

Bu yazıda anlatılan teknik konuyu profesyonel altyapıyla deneyimleyin

Etiketler:

#postgresql#kurulum rehberi#veritabanı#database#veri yönetimi

Bu yazıyı paylaş