Performa PHP

PHP yang cepat itu disetel, bukan dibeli.

Tim kami telah bekerja dengan PHP selama dua puluh lima tahun dan tahu di mana sebuah permintaan kehilangan waktunya. Kami menemukannya di server Anda dan merebutnya kembali, lapis demi lapis.

Anatomi satu permintaan

Sebuah halaman adalah rantai penantian: menemukan server, membuka koneksi, antre untuk PHP, mengompilasi, menjalankan, bertanya ke database, mengirim. Beralihlah antarkeadaan untuk melihat mata rantai mana yang kami persingkat.

Ilustrasi: proporsi, bukan hasil pengukuran
  1. Pencarian DNS Dijawab oleh resolver milik pengunjung. Sama di setiap keadaan: bagian ini adalah jaringan pengunjung. Sama di setiap keadaan: bagian ini adalah jaringan pengunjung.
  2. Koneksi dan TLS Pengaturan TLS lama, koneksi baru untuk setiap file. TLS 1.3 dan satu koneksi HTTP/2 yang dipakai ulang untuk setiap file. TLS 1.3 dan satu koneksi HTTP/2 yang dipakai ulang untuk setiap file.
  3. Menunggu worker PHP Semua worker sibuk, sehingga permintaan menunggu di antrean. Pool yang disesuaikan dengan memori: worker kosong sudah siap. Tidak perlu worker PHP.
  4. Mengompilasi skrip PHP membaca dan mengompilasi setiap file pada setiap permintaan. OPcache menyajikan skrip yang sudah dikompilasi dari memori. Tidak ada yang perlu dikompilasi.
  5. Mencari file dan class Autoloader menelusuri direktori untuk setiap class. Autoloader classmap dan cache realpath yang sudah terisi. Tidak ada yang perlu dimuat.
  6. Menjalankan kode Anda Pekerjaan aplikasi itu sendiri. Hampir sama: JIT jarang mengubah ini pada situs biasa. Kode Anda tidak berjalan untuk kunjungan ini.
  7. Query database Indeks yang hilang, dan query yang sama di setiap halaman. Indeks ditambahkan, jawaban berulang disimpan di Redis. Tidak ada query yang dikirim.
  8. Mengirim halaman Dikirim tanpa kompresi. Dikompresi sebelum meninggalkan server. Halaman jadi, langsung dari cache halaman.
Jaringan Kerja di server kami Byte pertama sampai ke pengunjung

Batang di atas hanyalah gambar. Angka ini adalah hasil pengukuran.

< 20 ms Byte pertama sebuah halaman dari server kami, diukur di server itu sendiri termasuk enkripsi.

Sisa waktunya adalah jarak antara server kami di Jerman dan Anda. Bagian itu bergantung pada lokasi Anda, jadi ukurlah dari koneksi Anda sendiri.

Ukur dari koneksi Anda

Pengaturan yang menghapus sebagian besar penantian

Kompilasi, pencarian file, dan pemuatan class terjadi sebelum kode Anda menjalankan satu baris pun. Pengaturan ini membuatnya terjadi sekali, bukan pada setiap permintaan.

conf.d/10-opcache.iniSebuah contoh - nilainya ditetapkan per situs
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 mengompilasi setiap file sebelum menjalankannya. OPcache menyimpan hasil kompilasi di memori bersama, sehingga permintaan berikutnya melewati pekerjaan itu. Kami menyesuaikan memori dan jumlah filenya dengan situs, lalu membaca hit rate-nya untuk memastikan tidak ada yang dibuang.

    Di produksi, PHP berhenti memeriksa perubahan setiap file; setiap deploy mengosongkan cache dengan sendirinya.

  2. Preloading

    Untuk sebuah framework, preloading memuat file intinya sekali saat PHP dimulai dan menyiapkannya untuk setiap permintaan. Perubahan pada file itu membutuhkan restart, sehingga restart menjadi bagian dari deploy.

  3. JIT

    JIT mengubah kode PHP yang sering dijalankan menjadi kode mesin. Ia membantu pekerjaan yang menyibukkan prosesor - pengolahan gambar, perhitungan, loop panjang. Situs pada umumnya menghabiskan waktunya menunggu database dan jaringan, dan di sana JIT tidak banyak mengubah apa pun. Kami menyalakannya di tempat yang pengukurannya menunjukkan perbedaan, bukan di semua tempat.

  4. Cache realpath

    Setiap include bertanya ke disk di mana sebuah file sebenarnya berada. Cache realpath yang lebih besar menyimpan jawaban itu di memori, dan ini penting bagi framework yang memuat ratusan file per permintaan.

  5. Autoloader Composer

    Classmap yang otoritatif menemukan setiap class dalam satu pencarian alih-alih menelusuri direktori, dan paket pengembangan tidak ikut masuk ke server.

Tombol penyetel: PHP-FPM

PHP-FPM memelihara sebuah pool worker PHP. Terlalu sedikit, permintaan mengantre; terlalu banyak, server kehabisan memori dan melambat bagi semua orang. Pool diatur berdasarkan apa yang benar-benar dimiliki server.

pm = static
pm.max_children = 64

Jumlah worker tetap, selalu berjalan

Untuk server yang dikhususkan bagi satu situs yang ramai. Tidak ada waktu terbuang untuk memulai proses, dan memori yang dipakainya diketahui sejak awal.

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

Beberapa worker siap, lebih banyak saat beban naik

Menyiagakan beberapa worker dan menambah lagi saat lalu lintas naik, hingga batasnya. Pilihan umum untuk situs dengan puncak harian.

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

Worker hanya saat permintaan datang

Untuk banyak situs kecil di satu server: memori diberikan kepada situs yang sedang dikunjungi, dan permintaan pertama setelah masa sepi membayar waktu mulai yang singkat.

Cara kami menetapkan pm.max_children

pm.max_children = memori untuk PHP ÷ memori per worker

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

Contoh perhitungan, bukan rekomendasi: angka sebenarnya berasal dari server Anda, dan kami menyisakan ruang untuk beban puncak.

pm.max_requests
Mengganti worker setelah sejumlah permintaan tertentu, agar kebocoran memori yang lambat tidak pernah berubah menjadi gangguan.
request_slowlog_timeout
Permintaan yang berjalan lebih lama menulis stack trace-nya ke slow log, yang menunjuk fungsi yang perlu diperiksa.
request_terminate_timeout
Menghentikan permintaan yang lepas kendali sebelum ia menahan worker selamanya.
pm.status_path
Menampilkan antrean dan seberapa sering pool mencapai batasnya - tempat pertama yang kami periksa ketika situs melambat saat puncak.

Dua jalan dari web server ke PHP

Kami menjalankan keduanya dan memilih sesuai kebutuhan situs. Bagaimanapun juga, permintaan tercepat adalah yang tidak perlu dijawab PHP sama sekali.

LiteSpeed + LSAPI

  1. Pengunjung
  2. LiteSpeed
  3. LSCachecache halaman
  4. LSAPI
  5. lsphp

LiteSpeed berkomunikasi dengan PHP lewat protokol LSAPI miliknya dan menyimpan halaman utuh di LSCache, yang dapat dibersihkan oleh WordPress dan aplikasi lain tepat saat konten berubah.

Nginx + PHP-FPM

  1. Pengunjung
  2. Nginx
  3. fastcgi_cachecache halaman
  4. FastCGI
  5. PHP-FPM

Nginx menyajikan file statis sendiri, meneruskan PHP ke pool PHP-FPM melalui socket lokal, dan dapat menyimpan halaman jadi di cache FastCGI untuk pengunjung yang tidak login.

  • Redis untuk objek dan sesi

    Jawaban yang berulang kali diminta sebuah halaman dari database disimpan di memori, dan sesi tidak lagi menyentuh disk.

  • Cache halaman penuh

    Pengunjung yang tidak login mendapat halaman jadi tanpa PHP atau database ditanya sama sekali. Keranjang, akun, dan checkout dikecualikan.

  • HTTP/2 dan kompresi

    Satu koneksi terenkripsi membawa setiap file halaman, teks dikompresi sebelum dikirim, dan file statis memiliki masa cache panjang dengan nama berversi.

  • Waktu di database

    Slow query log dan EXPLAIN menunjukkan query mana yang menunggu dan mengapa; biasanya satu indeks atau satu query berulang menjadi penyebab utamanya.

    Cara kami menyetel database

Kami mengukur dulu, lalu mengubah satu hal

Setiap perubahan dibuat terhadap pembacaan yang diambil sebelumnya, sehingga kami dapat menunjukkan hasilnya kepada Anda. Inilah pembacaannya.

  • opcache_get_status()Hit rate, memori kosong, dan apakah skrip sedang dikeluarkan.
  • pm.status_pathAntrean dan seberapa sering pool mencapai batasnya.
  • slowlogStack trace setiap permintaan yang berjalan terlalu lama.
  • slow_query_log + EXPLAINQuery mana yang menunggu, berapa baris yang dibacanya, dan indeks mana yang hilang.
  • redis-cli INFO statsCache hit dibandingkan miss, dan apakah key sedang dikeluarkan.
  • curl -w %{time_starttransfer}Waktu hingga byte pertama, diukur dengan cara yang sama sebelum dan sesudah.

Ingin separuh lain dari angka itu - jarak dari server kami ke Anda? Jalankan tes kecepatan

Beri tahu kami halaman mana yang lambat

Kirimkan alamatnya dan di mana halaman itu berjalan saat ini. Kami membaca servernya dulu, lalu memberi tahu apa yang akan kami ubah dan hasil yang diharapkan - dan memberi penawaran setelah percakapan itu.