opcache_get_status() yang memperlihatkan isi cache, sisa memori, dan berapa kali cache sudah di-restart. Kita bahas dulu intuisinya, lalu mekanismenya, lalu cara membaca angkanya.
Gambaran sederhana: rak hasil terjemahan
Apa yang sebenarnya terjadi di dalam OPcache
opcache.memory_consumption adalah ukuran shared memory dalam megabyte. Di PHP modern default-nya biasanya 128.
opcache.max_accelerated_files adalah jumlah maksimum key di hash table cache. PHP membulatkan nilai ini ke atas ke bilangan prima tertentu, jadi angka yang benar-benar dipakai bisa sedikit lebih besar dari yang kamu tulis. Satu file juga bisa punya lebih dari satu key, misalnya kalau file itu diakses lewat path yang berbeda.
Lalu ada konsep wasted memory. Ketika file PHP berubah, misalnya setelah update plugin atau deploy kode baru, OPcache tidak menimpa versi lama di tempatnya. Versi lama ditandai tidak terpakai, dan memorinya baru kembali setelah cache di-restart. Batas toleransinya diatur opcache.max_wasted_percentage, default-nya 5 persen.
Dari sini ada dua skenario buruk.
Skenario pertama, memori atau slot key penuh dan wasted memory sudah melewati batas. OPcache menjadwalkan restart, seluruh cache dibuang, dan semua script di-compile ulang saat diakses. Selama beberapa saat setelah restart, CPU naik dan respons melambat. Kalau kapasitasnya memang kurang, siklus ini berulang. Dari sisi pengunjung, hasilnya ya "kadang lemot".
Skenario kedua lebih diam. Memori penuh, tapi wasted memory masih di bawah batas, jadi OPcache biasanya tidak restart. Script baru yang belum masuk cache tetap jalan, tapi tidak di-cache, sehingga di-compile ulang di setiap request. Tidak ada error, cuma lebih lambat dari seharusnya.
Cara membaca opcache_get_status
php -r di terminal memakai PHP CLI, yang punya cache sendiri dan sering malah tidak mengaktifkan OPcache sama sekali. Jadi cara paling jujur adalah membuat file PHP kecil dan membukanya lewat browser, di domain dan versi PHP yang sama dengan situs yang dicek.
Contoh script sederhana, taruh sementara di document root dengan nama yang sulit ditebak:
PHP
<?php
// Script cek sementara. Hapus setelah selesai: output-nya membuka info server.
if (!function_exists('opcache_get_status')) {
exit('Ekstensi OPcache tidak dimuat');
}
$s = opcache_get_status(false); // false = tanpa daftar script, lebih ringan
if ($s === false) {
exit('OPcache tidak aktif untuk SAPI ini');
}
header('Content-Type: text/plain');
m =s['memory_usage'];
st =s['opcache_statistics'];
$mb = 1048576;
echo 'cache_full: ', var_export($s['cache_full'], true), PHP_EOL;
echo 'used MB: ', round(m['used_memory'] /mb, 1), PHP_EOL;
echo 'free MB: ', round(m['free_memory'] /mb, 1), PHP_EOL;
echo 'wasted MB: ', round(m['wasted_memory'] /mb, 1), PHP_EOL;
echo 'wasted %: ', round($m['current_wasted_percentage'], 2), PHP_EOL;
echo 'scripts: ', $st['num_cached_scripts'], PHP_EOL;
echo 'keys: ', st['num_cached_keys'], ' / ',st['max_cached_keys'], PHP_EOL;
echo 'hit rate %: ', round($st['opcache_hit_rate'], 2), PHP_EOL;
echo 'oom_restarts: ', $st['oom_restarts'], PHP_EOL;
echo 'hash_restarts: ', $st['hash_restarts'], PHP_EOL;
echo 'manual_restarts: ', $st['manual_restarts'], PHP_EOL;
echo 'start: ', date('c', $st['start_time']), PHP_EOL;
echo 'last restart: ', st['last_restart_time'] ? date('c',st['last_restart_time']) : '-', PHP_EOL;cache_full bernilai true berarti OPcache sudah tidak punya ruang untuk script baru. Ini tanda paling langsung.
free MB dan wasted MB perlu dilihat berdua. Free kecil dan wasted kecil berarti memorinya memang kurang. Free kecil tapi wasted besar berarti banyak versi lama yang menumpuk, biasanya karena kode sering berubah.
keys membandingkan key terpakai dengan batas sebenarnya. Kalau angka kiri sudah mepet ke angka kanan, slot key hampir habis meskipun memori masih lega.
oom_restarts menghitung restart karena memori habis. hash_restarts menghitung restart karena slot key habis. manual_restarts menghitung restart yang dipicu opcache_reset() atau tindakan manual, misalnya dari plugin cache atau script deploy.
start dan last restart membantu menilai seberapa sering cache dibuang. Kalau restart terakhir baru beberapa menit lalu padahal tidak ada deploy, itu patut dicurigai.
Menilai angkanya dan memilih apa yang dinaikkan
start_time. Kalau server sudah lama tidak restart, hit rate bisa tetap terlihat bagus padahal beberapa jam terakhir banyak miss. Lebih berguna mengambil dua snapshot dengan jeda tertentu, lalu melihat selisih hits dan misses di antaranya. Kalau misses terus bertambah cepat di situs yang kodenya tidak berubah, ada yang tidak beres.
Setelah itu, pilihannya biasanya cukup jelas.
Kalau oom_restarts terus bertambah, atau cache_full true dengan free memory tipis, naikkan opcache.memory_consumption. Naikkan bertahap. Patokan kasarnya: used MB saat cache sudah terisi penuh, ditambah ruang cadangan untuk wasted memory dan pertumbuhan kode.
Kalau hash_restarts bertambah, atau jumlah key mendekati batas, yang perlu dinaikkan adalah opcache.max_accelerated_files, bukan memorinya. Untuk menentukan angkanya, hitung jumlah file .php di semua situs yang dilayani oleh pool atau master PHP yang sama, lalu beri ruang lebih. Ingat, satu master PHP-FPM bisa melayani banyak akun sekaligus, tergantung setup-nya.
Kalau wasted memory sering tinggi, coba lihat pola update kodenya. Restart setelah deploy besar itu wajar. Yang tidak wajar adalah restart beruntun di jam-jam biasa.
Satu lagi yang layak dilirik: interned_strings_usage di hasil opcache_get_status(). Buffer ini menyimpan string yang sering berulang dan diatur oleh opcache.interned_strings_buffer. Kalau buffer ini penuh, efisiensi cache juga ikut turun.
Kedua directive utama di atas bertipe system-level. Artinya, keduanya tidak bisa diubah lewat .user.ini atau .htaccess. Perubahan harus dilakukan di konfigurasi PHP global atau lewat menu konfigurasi PHP di control panel, tergantung panelnya. Setelah itu PHP-FPM atau proses PHP yang dipakai web server perlu di-restart, karena shared memory dialokasikan saat proses mulai.
Kesalahan yang sering bikin diagnosis meleset
phpinfo() sudah cukup. phpinfo() memang menampilkan konfigurasi dan beberapa statistik, tapi yang dibutuhkan adalah tren restart dan pemakaian. Data itu lebih mudah dibaca dari opcache_get_status() dan dibandingkan dari waktu ke waktu.
Ketiga, menaikkan memori terlalu besar "biar aman". Shared memory dialokasikan per master process. Di server dengan beberapa versi PHP atau beberapa master terpisah, total RAM yang terpakai bisa bertambah tanpa terasa, apalagi di VPS kecil. Naikkan sesuai data, bukan feeling.
Keempat, memasang opcache_reset() di cron sebagai "perawatan". Ini justru memaksa cache dingin secara berkala, yaitu persis gejala yang sedang kita hindari. Kalau masalahnya kapasitas, perbaiki kapasitasnya.
Kelima, menyalahkan OPcache untuk semua kelambatan. Kalau lambatnya konsisten di setiap halaman admin, bukan berkala, penyebabnya sering di tempat lain. Contohnya opsi autoload WordPress yang membengkak, seperti dibahas di Cara Cek Ukuran Opsi Autoload di wp_options yang Bikin Admin WordPress Lemot. OPcache cocok dicurigai kalau polanya naik-turun dan berbarengan dengan lonjakan CPU singkat.
Dari sisi support, pertanyaan yang biasanya paling cepat mengarah ke jawaban adalah ini: "apakah lambatnya terus-menerus, atau datang dan pergi?" Kalau datang dan pergi, membuka status OPcache cuma butuh beberapa menit dan sering langsung menjawab.
Jadi, jadikan cek OPcache bagian dari rutinitas. Ambil status dari SAPI yang benar, lihat cache_full, sisa memori, rasio key, dan tiga counter restart. Ulangi cek setelah beberapa waktu untuk melihat trennya. Kalau oom_restarts naik, tambah memori. Kalau hash_restarts naik, tambah max_accelerated_files. Setelah itu, restart PHP dan cek lagi untuk memastikan angkanya sudah tenang.