PHP Uygulamalarında Login Yavaşlığı: Session GC Sorunu ve Çözümü

Kategori: Sunucu Yönetimi / PHP / PerformansSüre: ~10 dakika okuma


Sorun

CloudPanel, Plesk veya cPanel gibi panellerle yönetilen bir Ubuntu/Linux sunucusunda PHP uygulamanız var. Uygulama genel olarak hızlı çalışıyor, ancak login sayfasında 1-2 dakika bekliyorsunuz. Giriş yaptıktan sonra her şey normal.

 

Bu yazıda bu sorunu nasıl teşhis ettiğimizi ve kalıcı olarak nasıl çözdüğümüzü adım adım anlatıyoruz.


Neden Olur?

PHP, session yönetimi için varsayılan olarak /var/lib/php/sessions/ klasörünü kullanır. Sunucunuzda birden fazla site varsa, tüm sitelerin session dosyaları bu tek klasörde birikir.

 

PHP'nin session GC (Garbage Collection) mekanizması, eski session dosyalarını temizlemek için ara sıra devreye girer. GC tetiklendiğinde klasördeki tüm dosyaları tek tek tarar. Eğer bu klasörde binlerce dosya birikmişse, tarama işlemi onlarca saniye veya birkaç dakika sürebilir.

 

Login işlemi sırasında session_regenerate_id(true) çağrısı GC'yi tetiklediği için tam login anında bu uzun bekleme yaşanır.

Özetle:

Login isteği geldi
  → session_regenerate_id(true) çağrıldı
    → PHP GC tetiklendi
      → /var/lib/php/sessions/ içindeki 1000+ dosya tarandı
        → 60 saniye bekleme
          → Dashboard'a yönlendirme

Teşhis

1. Nginx Access Log ile Zamanı Tespit Edin

tail -f /home/SITEADI/logs/nginx/access.log | grep -i "login"

Şuna benzer bir çıktı görürsünüz:

21:51:20 → POST /login   → 302 (login isteği)
21:52:25 → GET /         → 200 (dashboard)

İki istek arasında 65 saniye fark varsa sorun kesinlikle bu.

2. Session Dosyası Sayısını Kontrol Edin

ls /var/lib/php/sessions/ | wc -l

Çıktı 500'ün üzerindeyse sorunun kaynağı budur.


Geçici Çözüm (Anında Etki)

Biriken dosyaları temizleyin:

rm -f /var/lib/php/sessions/sess_*

⚠️ Bu komut aktif session'ları da siler. Kullanıcılar oturumdan çıkmış olur.

Login'i test edin — hızlanmış olmalı. Ancak bu geçici bir çözümdür; dosyalar zamanla yeniden birikir.


Kalıcı Çözüm

İki adımlı kalıcı çözüm: session dosyalarını projeye özel klasöre taşımak ve GC'yi cron'a devretmek.

Adım 1: public/index.php (veya uygulamanızın giriş noktası) Başına Ekleyin

session_start() çağrısından önce şu bloğu ekleyin:

// Session dosyalarını projeye özel klasörde tut
$_sessionPath = dirname(__DIR__) . '/storage/sessions';
if (!is_dir($_sessionPath) && !mkdir($_sessionPath, 0755, true) && !is_dir($_sessionPath)) {
    // Klasör oluşturulamazsa varsayılan path'e düş, uygulama çökmesin
    error_log('Session path olusturulamadi, varsayilan kullaniliyor.');
    $_sessionPath = (string) ini_get('session.save_path');
}
if (is_dir($_sessionPath) && is_writable($_sessionPath)) {
    ini_set('session.save_path', $_sessionPath);
}

ini_set('session.gc_maxlifetime', '7200');  // 2 saat
ini_set('session.gc_probability', '0');     // GC'yi request üzerinde kapatıyoruz
ini_set('session.gc_divisor',     '1000'); // gc_probability=0 ile birlikte etkisiz
ini_set('session.use_strict_mode', '1');
ini_set('session.lazy_write',      '1');   // Değişmeyen session'ı diske yazma

Bu yaklaşımın avantajları:

  • Her sitenin session dosyaları kendi klasöründe izole olur

  • GC sadece o projenin dosyalarını tarar, diğer siteleri etkilemez

  • gc_probability=0 ile GC hiçbir zaman bir kullanıcı isteğini yavaşlatmaz

  • lazy_write=1 ile session değişmemişse diske yazılmaz (ek performans)

  • Klasör oluşturulamazsa uygulama çökmez, varsayılan path'e düşer

Adım 2: Klasör İzinlerini Ayarlayın

mkdir -p /home/SITEADI/htdocs/DOMAIN/storage/sessions
chown SITEADI:SITEADI /home/SITEADI/htdocs/DOMAIN/storage/sessions
chmod 755 /home/SITEADI/htdocs/DOMAIN/storage/sessions

Adım 3: Cron Job Ekleyin

GC'yi request üzerinden kapattığımıza göre eski session dosyalarını temizleme işini cron'a veriyoruz:

crontab -e

Şu satırı ekleyin:

# Her gece 03:00'te 1 günden eski session dosyalarını temizle
0 3 * * * find /var/lib/php/sessions/ -name 'sess_*' -mtime +1 -delete

# Projeye özel session klasörü için de ekleyin
0 3 * * * find /home/SITEADI/htdocs/DOMAIN/storage/sessions/ -name 'sess_*' -mtime +1 -delete

Doğrulama

Değişiklikten sonra biri login olsun, ardından kontrol edin:

# Yeni session klasöründe dosya oluştu mu?
ls /home/SITEADI/htdocs/DOMAIN/storage/sessions/

# Artık /var/lib/php/sessions/ dolmuyor mu?
ls /var/lib/php/sessions/ | wc -l

Session klasöründe sess_ ile başlayan dosyalar görünüyorsa her şey doğru çalışıyor.


Neden gc_probability=0 Güvenli?

PHP'nin session GC'si iki şekilde çalışabilir:

Yöntem

Açıklama

Request tabanlı (varsayılan)

Her X istekte bir rastgele tetiklenir, o isteği yavaşlatır

Cron tabanlı

Belirli saatte çalışır, kullanıcıları etkilemez

gc_probability=0 yaparak GC'yi request'ten koparıp cron'a veriyoruz. Bu yaklaşım Laravel, Symfony gibi büyük framework'lerin de önerdiği yöntemdir.


Laravel Kullanıyorsanız

Laravel'de session driver'ı file olarak ayarlıysa aynı sorun yaşanabilir. .env dosyasına şunu ekleyin:

SESSION_DRIVER=file
SESSION_LIFETIME=120

Ve config/session.php dosyasında:

'files' => storage_path('framework/sessions'),

Laravel zaten session'ları storage/framework/sessions/ altında tutar, ancak GC ayarlarını yine de elle yapmanız önerilir.


Özet

 

Geçici Çözüm

Kalıcı Çözüm

Yöntem

rm -f /var/lib/php/sessions/sess_*

Session path izolasyonu + Cron GC

Etki

Anında

Kalıcı

Risk

Aktif oturumlar silinir

Yok

Tekrar eder mi?

Evet

Hayır


Kaynaklar


Bu makale gerçek bir sorun giderme sürecinden üretilmiştir. Benzer bir sorun yaşıyorsanız teşhis adımlarını sırayla uygulayın.

🤔

Was this answer helpful?

Görüşünüz bizim için değerli!