PHP 8.1'de session dosyaları birikince disk doluyor: gc ayarlarını ölçmek
这篇内容暂时只有 Türkçe 版本,以下是原文。
Geçen ay bir iç panel sunucusunda disk %100’e vurdu. PHP 8.1, Debian 11, php-fpm 7.4’ten yükseltilmiş bir kurulum. du çıktısı netti: /var/lib/php/sessions altında 6.2 GB, içinde 1.8 milyon dosya. Uygulama tarafında session kullanımı normaldi; giriş yapan kullanıcı sayısı günde birkaç bin. Ama dosyalar silinmiyordu.
Önce varsayılanları kontrol ettim:
php -i | grep -E "session.gc_(probability|divisor|maxlifetime)"
# session.gc_probability => 0
# session.gc_divisor => 1000
# session.gc_maxlifetime => 1440
gc_probability=0 görünce durdum. Debian paketlerinde bu değer bilerek 0 yapılıyor; temizliği cron’a bırakıyorlar. Ama bu sunucuda /etc/cron.d/php dosyası yoktu, muhtemelen imaj hazırlanırken atlanmış. Yani hiçbir şey silmiyordu.
Geçici çözüm olarak cron’a standart komutu koydum:
find /var/lib/php/sessions -type f -name 'sess_*' -mmin +1440 -delete
Kalıcı tarafta iki şeyi değiştirdim. Birincisi session.gc_maxlifetime değerini uygulamanın gerçek oturum süresiyle eşitledim (bizde 7200 saniye), ikincisi temizliği PHP’nin kendisine bırakmadım; çünkü gc_probability=1, gc_divisor=100 yapmak her 100 istekte bir dosya taraması demek ve 1.8 milyon dosyada bu tarama istek süresine yansıyor. Ölçtüğümde tek bir gc çalışması 300-400 ms sürüyordu; yoğun saatte bu kabul edilebilir değil.
Bunun yerine session handler’ı Redis’e taşıdım, dosya tabanlı session’ı sadece fallback olarak bıraktım:
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?database=2&timeout=1"
Redis’te TTL otomatik çalıştığı için disk sorunu kökten kalktı. Ama bunun bir bedeli var: Redis düşerse oturumlar da düşer. Bu yüzden timeout=1 ve yerel bir replika ile yetindim; session kaybını kabul edilebilir bulduk çünkü kullanıcılar yeniden giriş yapabiliyor.
Nerede işe yaramaz: Eğer uygulama session_write_close() çağırmadan uzun süren istekler yapıyorsa, dosya tabanlı handler’da kilitlenme görürsünüz; Redis’e geçince bu kilit sorunu azalır ama yarış koşulları bitmez. Ayrıca session.gc_maxlifetime ile Redis TTL’ini ayrı ayrı yönetmek zorunda kalıyorsunuz; ben ikisini de aynı config değerinden okutmak için küçük bir bootstrap dosyası yazdım. Bir de dikkat: find -delete komutunu -mmin +1440 ile çalıştırırken saat dilimi farkı varsa taze oturumları silebilirsiniz; ben ilk denemede bunu yaşadım ve birkaç kullanıcı oturumu düştü.
Sizin kurulumunuzda session temizliği cron’a mı bırakılmış, yoksa PHP gc’si açık mı? Özellikle yükseltme sonrası bu ayarın sessizce sıfırlandığını gördünüz mü?


0 条评论
还没有评论,写下第一条。