CPU limiti aşımı kaynaklı yavaşlama, sunucu sorunlarının içinde teşhisi en çok yanlış konulanıdır. Sebebi şu: bu sorun bir hata üretmez. Disk dolduğunda net bir hata alırsın, bellek bittiğinde süreç ölür ve günlüğe bir satır düşer — ama işlemci kotana çarptığında sistem sadece yavaşlar. Site çalışmaya devam eder, hata günlüğü temizdir, herkes birbirine "acaba internet mi yavaş" diye sorar.
Bu yazıda CPU baskısının gerçek belirtilerini, yük ortalamasının (load average) nasıl doğru okunacağını, işlemciyi kimin yediğini nasıl bulacağını ve en önemlisi yükü kalıcı olarak nasıl düşüreceğini anlatacağım. Hem paylaşımlı pakette hem de kendi sunucunda geçerli yöntemleri ayrı ayrı ele alacağım, çünkü ikisinde de teşhis araçları ve müdahale alanların farklı.
CPU Baskısının Belirtileri#
CPU'ya bağlı yavaşlığın karakteristik bir imzası vardır ve bunu tanımak seni yanlış yerde saatler harcamaktan kurtarır: sayfa üretimi uzar ama ağ ve indirme süreleri normal kalır. Yani ilk bayta kadar geçen süre (TTFB) tırmanır, buna karşılık dosyaların indirilmesi hızlıdır.
Bunu üç ölçümle doğrularsın. Birincisi, curl zaman kırılımında TTFB ile TLS arasındaki farkın büyümesi. İkincisi, yükün trafikle birlikte artıp gece düşmesi — kaynak kaynaklı yavaşlıklar trafiğe bağlıdır, kod hataları değildir. Üçüncüsü ise en ayırt edici olanı: statik bir dosya hızlı, dinamik bir sayfa yavaş geliyorsa sorun kesinlikle uygulama katmanındadır:
# Dinamik sayfa: PHP çalışır
curl -s -o /dev/null -w "dinamik ttfb: %{time_starttransfer}\n" https://firmaniz.com/
# Statik dosya: PHP çalışmaz, doğrudan diskten servis edilir
curl -s -o /dev/null -w "statik ttfb: %{time_starttransfer}\n" https://firmaniz.com/robots.txt
# Örnek çıktı:
# dinamik ttfb: 2.310
# statik ttfb: 0.086
Bu iki değer arasında böyle bir uçurum varsa ağ, DNS ve TLS katmanları sağlıklıdır; sorun sayfanın üretilmesindedir. Bu, sorunu üçte ikisinden ayırmış olman demektir.
| Belirti | CPU baskısı | Disk darboğazı | Ağ/mesafe sorunu |
|---|---|---|---|
| Statik dosya hızı | Normal | Yavaş | Yavaş |
| Dinamik sayfa hızı | Çok yavaş | Yavaş | Yavaş |
| TLS el sıkışma süresi | Normal | Normal | Yüksek |
| Gece / boş saat | Düzelir | Kısmen düzelir | Değişmez |
vmstat çıktısı | us + sy yüksek | wa yüksek | Etkilenmez |
Son satır teşhisin çekirdeğidir: vmstat çıktısında kullanıcı (us) ve sistem (sy) yüzdeleri toplamı yüksekse işlemci gerçekten çalışıyordur; wa (iowait) yüksekse işlemci aslında boştur ve diski bekliyordur. İkincisi durumunda CPU'yu yükseltmek hiçbir işe yaramaz — o teşhisin ayrıntısı disk I/O darboğazı tespiti yazısında.
Yük Ortalamasını Doğru Okumak#
uptime komutunun verdiği üç sayı en çok yanlış yorumlanan sunucu metriğidir. "Load average: 4.20, 3.90, 3.10" gördüğünde bunun iyi mi kötü mü olduğunu çekirdek sayını bilmeden söyleyemezsin. Yük ortalaması, çalışmaya hazır süreçlerin ortalama sayısıdır; bu sayı çekirdek sayısına eşitse sistem tam doludur, altındaysa boşluk vardır, üstündeyse kuyruk oluşmuştur.
# Çekirdek sayısı ve yük ortalaması yan yana
echo "Çekirdek: $(nproc)"
uptime
# Örnek: 4 çekirdek, load average 4.20 -> oran 1.05, doyum noktası
# Örnek: 8 çekirdek, load average 4.20 -> oran 0.52, rahat
# Yükün nereden geldiğini ayır: us=uygulama, sy=çekirdek, wa=disk bekleme, st=komşu çalıyor
vmstat 1 5
Kural şudur: yük ortalaması / çekirdek sayısı oranı 1'in altında sağlıklı, 1 civarında doyumda, sürekli 2'nin üstünde ise ciddi kuyruk var demektir. Üç sayıdan ilki son bir dakikayı, ikincisi beş dakikayı, üçüncüsü on beş dakikayı gösterir; ilk sayı yüksek ama üçüncüsü düşükse geçici bir tepe yaşıyorsun, üçü de yüksekse kalıcı bir baskı vardır.
vmstat çıktısındaki st (steal) sütunu sanal sunucularda ayrı bir öneme sahiptir. Bu değer sıfırdan belirgin biçimde büyükse, hipervizör senin CPU zamanını başka sanal makinelere veriyor demektir. Yani sen kotanı aşmadın, fiziksel makine aşırı satıldı. Bunu kendi tarafında düzeltemezsin; sağlayıcıya bildirmen ya da başka bir düğüme taşınmayı istemen gerekir. Bu, "sunucumu yükselttim ama hiçbir şey değişmedi" durumunun en yaygın açıklamasıdır.
İşlemciyi Kim Yiyor: Süreç Düzeyinde Teşhis#
Yükün var olduğunu doğruladıktan sonraki adım kaynağını bulmaktır. Kendi sunucunda bu doğrudan yapılır:
# CPU'ya göre sıralı ilk 15 süreç
ps -eo pid,user,pcpu,pmem,etime,args --sort=-pcpu | head -16
# Canlı izleme: 1 tuşu ile çekirdek başına dağılım açılır
top -b -n 1 | head -20
# Uzun süredir çalışan PHP süreçleri: takılmış istekleri gösterir
ps -eo pid,etimes,pcpu,args --sort=-etimes | grep -i "php" | head -10
Son komut özellikle değerlidir. etimes sütunu sürecin saniye cinsinden yaşını verir; normalde bir PHP-FPM işçisi bir isteği saniyeler içinde bitirir. Yüzlerce saniyedir yaşayan PHP süreçleri görüyorsan bunlar takılmış isteklerdir ve genellikle zaman aşımı olmayan bir dış API çağrısını ya da sonsuz bir döngüyü beklerler. Bu süreçler hem CPU hem de işlem havuzu tüketir.
PHP-FPM kullanıyorsan hangi isteğin ne kadar süredir çalıştığını doğrudan görebilirsin:
# FPM durum sayfası açıksa: yavaş istekleri isim isim gösterir
curl -s "http://127.0.0.1/status?full" | grep -E "request uri|request duration|state"
# Alternatif: yavaş istek günlüğünü aç (www.conf)
# request_slowlog_timeout = 5s
# slowlog = /var/log/php-fpm/slow.log
# Sonra günlükte hangi fonksiyonun takıldığını yığın izinden görürsün
tail -50 /var/log/php-fpm/slow.log
FPM'in yavaş istek günlüğü, bu işin en güçlü aracıdır: beş saniyeden uzun süren her isteğin PHP yığın izini (stack trace) yazar, yani "hangi dosyanın hangi satırında takıldı" sorusuna doğrudan cevap verir. Üretimde açık bırakabilirsin; maliyeti ihmal edilebilir.
Paylaşımlı pakette bu araçların çoğuna erişemezsin. Orada teşhis, kontrol panelindeki kaynak kullanımı grafikleri ve erişim günlükleri üzerinden yürür. Kotaların nasıl çalıştığını ve panelde neye bakman gerektiğini paylaşımlı hostingde hız limitleri yazısında ayrıntılı anlattım.
En Sık Görülen CPU Tüketicileri#
Yıllar içinde gördüğüm CPU tüketicilerinin büyük çoğunluğu şu altı başlıkta toplanıyor ve hiçbiri "sunucu zayıf" değil.
Bot ve tarayıcı trafiği. En yaygın olanı budur ve analitik araçlarında görünmediği için gözden kaçar. Özellikle mağazalarda filtre kombinasyonları sonsuz sayıda benzersiz URL üretir; bir tarayıcı botu bu alana girdiğinde tek başına sunucuyu doldurabilir:
# Son 10 bin istekte en aktif IP'ler ve kullanıcı aracıları
tail -10000 /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
tail -10000 /var/log/nginx/access.log | awk -F'"' '{print $6}' | sort | uniq -c | sort -rn | head -10
# En çok istenen dinamik yollar
tail -10000 /var/log/nginx/access.log | awk '{print $7}' | grep '?' | sort | uniq -c | sort -rn | head -10
Önbelleksiz çalışma. Her isteğin PHP çalıştırdığı bir sitede CPU kullanımı trafikle doğrusal artar. Tam sayfa önbelleği devredeyken aynı trafik işlemciye neredeyse hiç dokunmaz.
Kötü veritabanı sorguları. İndekssiz bir sorgu, veritabanı sunucusunun CPU'sunu tek başına doyurabilir. Yavaş sorgu günlüğü bunu dakikalar içinde ortaya çıkarır.
Zamanlanmış görevlerin üst üste binmesi. Beş dakikada bir çalışan ve altı dakika süren bir görev, zamanla birikip aynı anda onlarca kopya halinde çalışmaya başlar. Kilit dosyası kullanmayan her zamanlanmış iş bu riski taşır.
Görsel işleme ve PDF üretimi. Yükleme anında yapılan görsel yeniden boyutlandırma, yoğun bir sitede ciddi CPU yer. Bu tür işleri kuyruğa alıp arka planda yapmak yükü düzleştirir.
Eski PHP sürümü. Güncel sürümler aynı kodu belirgin biçimde daha az işlemciyle çalıştırır. Panelden yapılan tek tıklık bir yükseltme, çoğu zaman en ucuz CPU kazancıdır.
Yükü Kalıcı Olarak Düşürmek#
Teşhisi koyduktan sonra müdahale sırası şudur ve bu sıra maliyet-etki dengesine göre kurulmuştur.
Birinci adım: tam sayfa önbelleği. Bu, tartışmasız en yüksek getirili adımdır. Önbellekten servis edilen bir istek PHP'yi hiç başlatmaz. Anonim ziyaretçi oranı yüksek bir sitede CPU kullanımı onda birine iner. Nginx tarafında kurulum için Nginx FastCGI cache yapılandırma, LiteSpeed kullanıyorsan LiteSpeed Cache yazılarına bakabilirsin.
İkinci adım: opcode önbelleği. Kapalı bir OPcache her istekte tüm PHP dosyalarının yeniden derlenmesi demektir ve bu saf CPU israfıdır:
; php.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1
opcache.revalidate_freq=60
max_accelerated_files değeri projendeki PHP dosya sayısından büyük olmalıdır; küçükse önbellek sürekli taşar ve hiç işe yaramaz.
Üçüncü adım: bot ve tarama yükünü kısmak. Filtre ve arama sonucu URL'lerini taramaya kapat, kötü niyetli tarayıcıları engelle. Saldırı anlarında Cloudflare kullanıyorsan Under Attack modu trafiği hızlıca süzer; kalıcı çözüm için WAF katmanı uygulama seviyesinde filtreleme yapar.
Dördüncü adım: PHP-FPM havuzunu doğru boyutlandırmak. Havuz çok büyükse süreçler birbiriyle CPU için yarışır ve bağlam değiştirme maliyeti artar; çok küçükse istekler kuyruğa girer. Başlangıç noktası, kullanılabilir belleği bir işçinin ortalama bellek kullanımına bölmektir:
; www.conf - 4 GB kullanılabilir bellek, işçi başına ~80 MB varsayımı
pm = dynamic
pm.max_children = 45
pm.start_servers = 8
pm.min_spare_servers = 5
pm.max_spare_servers = 15
pm.max_requests = 500
; Takılan istekleri sonsuza kadar bekletme
request_terminate_timeout = 60s
pm.max_requests ayarı sızıntı yapan kodun etkisini sınırlar: her işçi 500 istekten sonra yeniden başlar ve biriktirdiği belleği bırakır. request_terminate_timeout ise takılan bir isteğin işçiyi süresiz meşgul etmesini engeller — dış API çağrılarının zaman aşımı yoksa bu ayar tek kurtarıcındır.
Beşinci adım: zamanlanmış işleri düzeltmek. Uygulamanın kendi tetikleyicisini kapatıp gerçek bir sistem zamanlayıcısına taşı ve üst üste binmeyi kilitle önle:
# Kilit dosyası ile üst üste binmeyi engelle
*/5 * * * * /usr/bin/flock -n /tmp/gorev.lock /usr/bin/php /var/www/firmaniz.com/gorev.php >/dev/null 2>&1
flock -n bayrağı, önceki çalışma hâlâ sürüyorsa yeni kopyayı hiç başlatmaz. Bu tek satır, biriken görevlerden kaynaklanan CPU patlamalarının büyük kısmını önler.
Ne Zaman Gerçekten Daha Fazla CPU Gerekir#
Yukarıdaki beş adımı uyguladıktan sonra hâlâ doyumdaysan, ihtiyacın gerçekten daha fazla işlemcidir. Bunu iki şekilde anlarsın: yük ortalaması / çekirdek oranı sürekli 1'in üstünde ve vmstat çıktısında us yüzdesi yüksek, wa ve st düşükse — yani işlemci gerçekten senin kodunu çalıştırıyorsa.
Bu noktada iki yol var. Dikey büyüme daha fazla çekirdek almaktır; en basit yoldur ve tek sunuculu mimaride tercih edilir. Yatay büyüme ise yükü birden fazla sunucuya dağıtmaktır; daha karmaşıktır ama kesintiye karşı da dayanıklılık kazandırır.
| Durum | Doğru hamle |
|---|---|
wa yüksek, us düşük | CPU değil disk sorunu |
st yüksek | Aşırı satılmış düğüm, sağlayıcıyla konuş |
us yüksek, önbellek yok | Önce önbellek kur |
us yüksek, önbellek var, trafik gerçek | Çekirdek sayısını artır |
| Tepe saatlerde doyum, gündüz boş | Otomatik ölçeklenen bulut yapısı |
| Sürekli doyum, öngörülebilir yük | Sabit ve güçlü VDS ya da dedicated |
Bir uyarı: çekirdek eklemek her iş yükünü hızlandırmaz. Tek bir isteğin süresi, tek bir çekirdeğin hızıyla belirlenir; çekirdek sayısı yalnızca kaç isteği aynı anda işleyebileceğini artırır. Yani "tek sayfa 3 saniyede açılıyor" sorunun varsa daha çok çekirdek bunu 3 saniyede tutar, sadece aynı anda daha fazla kişiye 3 saniyede açar. Tek istek süresini düşürmek için koda, sorgulara ve önbelleğe bakmalısın.
Sık Yapılan Hatalar#
Yük ortalamasını çekirdek sayısından bağımsız yorumlamak. "Load 4" bir sunucuda felaket, diğerinde rahat bir değerdir. Her zaman orana bak.
wa yüksekken CPU yükseltmek. iowait, işlemcinin boş oturup diski beklediğini gösterir; bu tabloda çekirdek eklemek para israfıdır.
st değerini görmezden gelmek. Sanal sunucuda steal yüksekse sorun sende değil, komşularında ve fiziksel düğümün doluluğundadır. Kendi tarafında yapabileceğin bir optimizasyon yoktur.
Zamanlanmış görevleri kilitsiz bırakmak. Üst üste binen görevler, sunucuyu düzenli aralıklarla kilitleyen en sinsi CPU tüketicisidir ve trafikle ilgisi olmadığı için teşhisi zordur.
PHP-FPM havuzunu gereğinden büyük yapmak. "Daha çok işçi daha çok kapasite" sezgisi yanlıştır; bellek bittiğinde sistem takasa düşer ve performans birkaç kat değil, on kat kötüleşir. Bu mekanizmanın ayrıntısı RAM yetersizliği ve swap etkisi yazısında.
Zaman aşımı koymadan dış servis çağırmak. Yavaşlayan bir dış API, PHP işçilerini teker teker kilitler ve sonunda tüm havuzu tüketir; site tamamen yanıt vermez hale gelir.
Sıkça Sorulan Sorular#
CPU limitine takıldığımı nasıl anlarım#
En hızlı sinyal, statik bir dosya ile dinamik bir sayfanın sürelerini karşılaştırmaktır: statik dosya milisaniyelerde gelirken dinamik sayfa saniyelerce sürüyorsa sorun uygulama katmanındadır. Kendi sunucundaysan uptime çıktısını nproc ile oranla ve vmstat 1 5 ile us, wa, st sütunlarına bak. Paylaşımlı paketteysen kontrol panelindeki kaynak kullanımı grafiklerinde ihlal (fault) sayısına bak.
Load average kaç olursa sorun sayılır#
Tek başına bir eşik yoktur; ölçüt yük ortalamasının çekirdek sayısına oranıdır. Bu oran 1'in altındaysa sistemde boşluk var, 1 civarındaysa doyumda, sürekli 2'nin üstündeyse ciddi kuyruk oluşuyor demektir. Ayrıca üç sayıya birlikte bak: yalnızca birinci sayı yüksekse geçici bir tepedesin, üçü de yüksekse kalıcı bir baskı vardır.
CPU yükünü hangi işlem tüketiyor nasıl bulurum#
Kendi sunucunda ps -eo pid,user,pcpu,etime,args --sort=-pcpu | head -16 komutu en çok tüketen süreçleri sıralar. PHP-FPM kullanıyorsan çok daha değerli bir araç var: yavaş istek günlüğünü açtığında beş saniyeden uzun süren her isteğin PHP yığın izi kaydedilir ve hangi dosyanın hangi satırında takıldığını doğrudan görürsün.
Sunucumu yükselttim ama hız değişmedi neden#
Üç olasılık var. Birincisi darboğaz CPU değildi; vmstat çıktısında wa yüksekse sorun diskteydi. İkincisi st yüksekti, yani fiziksel düğüm aşırı satılmıştı ve yeni paket de aynı düğümde açıldı. Üçüncüsü ve en yaygını, sorun tek istek süresindeydi: çekirdek eklemek aynı anda kaç kişiye hizmet verdiğini artırır, tek bir sayfanın üretim süresini kısaltmaz.
Zamanlanmış görevler CPU yükünü nasıl etkiler#
Beklenenden çok daha fazla etkiler, özellikle üst üste bindiklerinde. Beş dakikada bir tetiklenen ama altı dakika süren bir görev zamanla birikerek aynı anda onlarca kopya halinde çalışmaya başlar ve sunucuyu düzenli aralıklarla kilitler. Çözüm, görevleri flock gibi bir kilit mekanizmasıyla çalıştırmak ve uygulamanın ziyaretçi isteğiyle tetiklenen kendi zamanlayıcısını kapatmaktır.
PHP sürümünü yükseltmek CPU kullanımını düşürür mü#
Belirgin biçimde düşürür. Güncel PHP sürümleri, birkaç yıl önceki sürümlere göre aynı kodu daha az işlemci çevrimiyle çalıştırır ve daha az bellek kullanır. Çoğu paylaşımlı pakette bu, panelden yapılan tek tıklık bir değişikliktir ve maliyeti sıfırdır. Yükseltmeden önce mutlaka bir test kopyasında uyumluluğu dene; eski eklentiler yeni sürümlerde uyarı üretebilir.
Kapanış#
CPU kaynaklı yavaşlıkla baş etmenin dört alışkanlığı var. Birincisi, teşhisi ölçümle başlat: statik ile dinamik yanıt sürelerini karşılaştır, sonra vmstat çıktısında us, wa ve st sütunlarını ayır. İkincisi, yük ortalamasını asla çekirdek sayısından bağımsız yorumlama. Üçüncüsü, kaynak artırmadan önce sırayla önbellek, OPcache ve bot filtresini kur; bu üçü çoğu sitede yükü onda birine indirir. Dördüncüsü, zamanlanmış görevleri kilitle koru ve her dış servis çağrısına zaman aşımı ver.
Bu adımların sonunda gerçekten daha fazla işlemciye ihtiyacın olduğunu ölçtüysen, doğru paket seçimi kolaylaşır. Ayrılmış çekirdek ve kök erişimi isteyen projeler için VDS sunucu, tepe saatlerde esneyen yükler için bulut sunucu, sürekli yüksek ve öngörülebilir yük için dedicated sunucu seçenekleri var. Önbellek, PHP-FPM ve izleme kurulumunu kendin yapmak istemiyorsan sunucu yönetimi hizmetimiz bu işi üstlenir; bot ve saldırı kaynaklı yükleri süzmek için DDoS koruma katmanına bakabilirsin.