PHP performansı

Hızlı PHP satın alınmaz, ayarlanır.

Ekibimiz yirmi beş yıldır PHP ile çalışıyor ve bir isteğin zamanını nerede kaybettiğini biliyor. O zamanı sunucunuzda buluyor ve katman katman geri kazanıyoruz.

Bir isteğin anatomisi

Bir sayfa, beklemelerden oluşan bir zincirdir: sunucuyu bulmak, bağlantıyı açmak, PHP için sıraya girmek, derlemek, çalıştırmak, veritabanına sormak, göndermek. Hangi halkaları kısalttığımızı görmek için durumlar arasında geçiş yapın.

Temsilîdir: oranlar, ölçüm değil
  1. DNS sorgusu Ziyaretçinin çözümleyicisi tarafından yanıtlanır. Her durumda aynı: bu kısım ziyaretçinin ağıdır. Her durumda aynı: bu kısım ziyaretçinin ağıdır.
  2. Bağlantı ve TLS Eski bir TLS kurulumu, her dosya için yeni bir bağlantı. TLS 1.3 ve her dosya için yeniden kullanılan tek bir HTTP/2 bağlantısı. TLS 1.3 ve her dosya için yeniden kullanılan tek bir HTTP/2 bağlantısı.
  3. Bir PHP worker'ı beklemek Tüm worker'lar meşgul, bu yüzden istek kuyrukta bekler. Belleğe göre boyutlandırılmış bir havuz: boş bir worker hazırdır. Hiçbir PHP worker'ı gerekmez.
  4. Betikleri derlemek PHP her istekte her dosyayı okur ve derler. OPcache derlenmiş betikleri bellekten sunar. Derlenecek bir şey yok.
  5. Dosyaları ve sınıfları bulmak Autoloader her sınıf için dizinleri arar. Classmap tabanlı bir autoloader ve dolu bir realpath önbelleği. Yüklenecek bir şey yok.
  6. Kodunuzu çalıştırmak Uygulamanın kendi işi. Hemen hemen aynı: JIT tipik bir sitede bunu nadiren değiştirir. Bu ziyaret için kodunuz çalışmaz.
  7. Veritabanı sorguları Eksik indeksler ve her sayfada aynı sorgu. İndeksler eklendi, tekrarlanan yanıtlar Redis'te tutuluyor. Hiçbir sorgu gönderilmez.
  8. Sayfayı göndermek Sıkıştırılmadan gönderilir. Sunucudan çıkmadan önce sıkıştırılır. Hazır sayfa, doğrudan sayfa önbelleğinden.
Ağ Sunucumuzdaki iş İlk bayt ziyaretçiye ulaşır

Yukarıdaki çubuklar bir çizimdir. Bu rakam ise bir ölçümdür.

< 20 ms Bir sayfanın ilk baytı, sunucumuzdan, şifreleme dahil sunucunun kendisinde ölçüldü.

Geri kalan süre, Almanya'daki sunucularımızla aranızdaki mesafedir. Bu kısım nerede olduğunuza bağlıdır; kendi bağlantınızdan ölçün.

Bağlantınızdan ölçün

Beklemenin çoğunu ortadan kaldıran ayarlar

Derleme, dosyaları bulma ve sınıfları yükleme, kodunuz tek bir satır çalıştırmadan önce gerçekleşir. Bu ayarlar bunların her istekte değil, bir kez yapılmasını sağlar.

conf.d/10-opcache.iniBir örnek - değerler her site için ayrı belirlenir
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
opcache.preload=/srv/app/preload.php
opcache.preload_user=www-data
opcache.jit=tracing
opcache.jit_buffer_size=64M
realpath_cache_size=4096K
realpath_cache_ttl=600
deploy
$ composer install --no-dev --classmap-authoritative
  1. OPcache

    PHP her dosyayı çalıştırmadan önce derler. OPcache derlenmiş sonucu paylaşılan bellekte tutar, böylece bir sonraki istek bu işi atlar. Belleğini ve dosya sayısını siteye göre boyutlandırır, hiçbir şeyin dışarı atılmadığını isabet oranından doğrularız.

    Canlı ortamda PHP her dosyayı değişiklik için denetlemeyi bırakır; her dağıtım önbelleği kendisi temizler.

  2. Ön yükleme

    Bir framework için ön yükleme, çekirdek dosyalarını PHP başlarken bir kez yükler ve her istek için hazır tutar. Bu dosyalardaki bir değişiklik yeniden başlatma gerektirir; bu yüzden yeniden başlatma dağıtımın bir parçasıdır.

  3. JIT

    JIT sık çalışan PHP kodunu makine koduna çevirir. İşlemciyi meşgul eden işlere yardım eder - görüntü işleme, hesaplamalar, uzun döngüler. Tipik bir site zamanını veritabanını ve ağı bekleyerek geçirir; orada JIT pek bir şey değiştirmez. Onu her yerde değil, bir ölçümün fark gösterdiği yerde açarız.

  4. Realpath önbelleği

    Her include, bir dosyanın gerçekte nerede olduğunu diske sorar. Daha büyük bir realpath önbelleği bu yanıtları bellekte tutar; bu, istek başına yüzlerce dosya yükleyen framework'ler için önemlidir.

  5. Composer autoloader

    Yetkili bir classmap, dizinleri aramak yerine her sınıfı tek bir aramada bulur ve geliştirme paketleri sunucuya girmez.

Ayar düğmesi: PHP-FPM

PHP-FPM bir PHP worker havuzu tutar. Az olursa istekler sıraya girer; çok olursa sunucunun belleği tükenir ve herkes için yavaşlar. Havuz, sunucunun gerçekte sahip olduğuna göre ayarlanır.

pm = static
pm.max_children = 64

Sabit sayıda worker, her zaman çalışır

Tek bir yoğun siteye ayrılmış sunucu için. Süreç başlatmaya zaman harcanmaz ve kullandığı bellek önceden bilinir.

pm = dynamic
pm.max_children = 64
pm.start_servers = 8
pm.min_spare_servers = 4
pm.max_spare_servers = 12

Birkaç worker hazır, yük altında daha fazlası

Birkaç boşta worker'ı beklemede tutar ve trafik arttıkça sınıra kadar yenilerini ekler. Günlük yoğunlukları olan bir site için olağan tercih.

pm = ondemand
pm.max_children = 64
pm.process_idle_timeout = 10s

Worker yalnızca bir istek geldiğinde

Tek sunucudaki birçok küçük site için: bellek ziyaret edilen sitelere gider ve sakin bir dönemden sonraki ilk istek kısa bir başlatma bedeli öder.

pm.max_children değerini nasıl belirliyoruz

pm.max_children = PHP için bellek ÷ worker başına bellek

pm.max_children 64
$ ps --no-headers -o rss -C php-fpm8.3 | awk '{s+=$1} END {print s/NR/1024 " MB"}'

Bir hesap örneği, öneri değil: gerçek rakamlar sunucunuzdan gelir ve yoğun an için pay bırakırız.

pm.max_requests
Bir worker'ı belirli sayıda istekten sonra yeniler; böylece yavaş bir bellek sızıntısı asla kesintiye dönüşmez.
request_slowlog_timeout
Daha uzun süren bir istek, yığın izini yavaş günlüğe yazar; bu günlük incelenecek fonksiyonu gösterir.
request_terminate_timeout
Kontrolden çıkan bir isteği, bir worker'ı kalıcı olarak tutmadan durdurur.
pm.status_path
Kuyruğu ve havuzun sınırına ne sıklıkla ulaştığını gösterir - bir site yoğun saatte yavaşladığında ilk baktığımız yer.

Web sunucusundan PHP'ye iki yol

İkisini de işletiyor ve sitenin ihtiyacına göre seçiyoruz. Her iki durumda da en hızlı istek, PHP'nin hiç yanıtlamak zorunda kalmadığı istektir.

LiteSpeed + LSAPI

  1. Ziyaretçi
  2. LiteSpeed
  3. LSCachesayfa önbelleği
  4. LSAPI
  5. lsphp

LiteSpeed, PHP ile kendi LSAPI protokolü üzerinden konuşur ve tam sayfaları LSCache'te tutar; WordPress ve diğer uygulamalar içerik değiştiği anda bu önbelleği temizleyebilir.

Nginx + PHP-FPM

  1. Ziyaretçi
  2. Nginx
  3. fastcgi_cachesayfa önbelleği
  4. FastCGI
  5. PHP-FPM

Nginx statik dosyaları kendisi sunar, PHP'yi yerel bir soket üzerinden bir PHP-FPM havuzuna iletir ve oturum açmamış ziyaretçiler için hazır sayfaları FastCGI önbelleğinde tutabilir.

  • Nesneler ve oturumlar için Redis

    Bir sayfanın veritabanından tekrar tekrar istediği yanıtlar bellekte tutulur ve oturumlar diske dokunmaz olur.

  • Tam sayfa önbelleği

    Oturum açmamış bir ziyaretçi, PHP'ye veya veritabanına hiç sorulmadan hazır bir sayfa alır. Sepetler, hesaplar ve ödeme sayfaları bunun dışında tutulur.

  • HTTP/2 ve sıkıştırma

    Tek bir şifreli bağlantı sayfanın tüm dosyalarını taşır, metin gönderilmeden önce sıkıştırılır ve statik dosyalar sürümlü adlarla uzun önbellek sürelerine sahip olur.

  • Veritabanı süresi

    Yavaş sorgu günlüğü ve EXPLAIN hangi sorgunun neden beklediğini gösterir; çoğu zaman bunun büyük kısmı tek bir indeks ya da tekrarlanan tek bir sorgudur.

    Veritabanlarını nasıl ayarlıyoruz

Önce ölçer, sonra tek bir şeyi değiştiririz

Her değişiklik, öncesinde alınmış bir ölçüme karşı yapılır; böylece ne sağladığını size gösterebiliriz. Ölçümler şunlardır.

  • opcache_get_status()İsabet oranı, boş bellek ve betiklerin dışarı atılıp atılmadığı.
  • pm.status_pathBekleme kuyruğu ve havuzun sınırına ne sıklıkla ulaştığı.
  • slowlogFazla uzun süren her isteğin yığın izi.
  • slow_query_log + EXPLAINHangi sorguların beklediği, kaç satır okudukları ve hangi indeksin eksik olduğu.
  • redis-cli INFO statsÖnbellek isabetlerine karşı ıskalar ve anahtarların dışarı atılıp atılmadığı.
  • curl -w %{time_starttransfer}İlk bayta kadar geçen süre, öncesinde ve sonrasında aynı yöntemle ölçülür.

Rakamın diğer yarısını, yani sunucularımızla aranızdaki mesafeyi mi görmek istiyorsunuz? Hız testini çalıştırın

Hangi sayfanın yavaş olduğunu bize söyleyin

Adresi ve bugün nerede çalıştığını bize gönderin. Önce sunucuyu inceliyor, sonra neyi değiştireceğimizi ve bunun ne sağlaması gerektiğini söylüyoruz - ve bu görüşmeden sonra iş için teklif veriyoruz.