PHP OPcache là lớp bytecode cache giúp PHP tái sử dụng mã đã biên dịch thay vì phân tích và biên dịch lại file PHP trong mỗi request. Với website Laravel hoặc PHP chạy production, OPcache thường là một trong những cấu hình nền tảng để giảm CPU và cải thiện thời gian phản hồi. Tuy nhiên, bật cache không đồng nghĩa với việc chỉ cần đặt `opcache.memory_consumption=256` là xong; cấu hình phải khớp với số file, cách deploy, RAM và mô hình PHP-FPM thực tế.
Bài viết này tập trung vào cấu hình OPcache production có thể kiểm tra và rollback. Bạn sẽ biết cách xác nhận OPcache đang bật ở đúng SAPI, đọc dung lượng cache, phân biệt CLI với PHP-FPM, chọn `validate_timestamps`, xử lý cache sau deploy và phát hiện tình trạng cache đầy. Các giá trị trong bài là baseline để đo lường, không phải con số bắt buộc cho mọi máy chủ. PHP Manual là nguồn tham chiếu cho ý nghĩa của từng directive [1] [2].
Nguyên tắc production: hãy đo trước, thay đổi từng nhóm directive, reload đúng PHP-FPM và kiểm tra lại từ request web thật. Đừng tắt kiểm tra timestamp nếu quy trình deploy chưa có bước reset hoặc restart OPcache rõ ràng.
1. OPcache hoạt động như thế nào?
PHP thường phải đọc source, tokenize, parse và compile file thành opcode trước khi thực thi. OPcache lưu opcode trong shared memory để các worker PHP-FPM có thể dùng lại. Lợi ích thực tế phụ thuộc vào tỷ lệ request lặp lại, số file được truy cập, thời gian sống của worker và mức độ thay đổi code. OPcache không thay thế HTTP cache, database cache hay CDN; nó chỉ tối ưu lớp thực thi PHP.
Trong PHP-FPM, mỗi pool có nhiều worker nhưng OPcache thường dùng vùng shared memory của SAPI. Vì vậy, kết quả kiểm tra từ `php -i` ở CLI có thể không phản ánh chính xác instance đang phục vụ Nginx. Đây là lý do cần có endpoint kiểm tra an toàn hoặc xem thông tin qua một request web được bảo vệ, thay vì chỉ chạy `php -i` trong terminal.
2. Kiểm tra PHP version, SAPI và file cấu hình
Trước khi sửa php.ini, hãy ghi nhận PHP binary, version, SAPI và file cấu hình. Máy chạy nhiều phiên bản PHP có thể có `/etc/php/8.2`, `/etc/php/8.3` hoặc nhiều service FPM; chỉnh nhầm file là nguyên nhân phổ biến khiến OPcache “đã bật nhưng không có tác dụng”.
php -v
php --ini
php -i | grep -Ei 'opcache|Loaded Configuration|Server API'
command -v php
ps aux | grep '[p]hp-fpm'
systemctl list-units --type=service | grep -E 'php.*fpm' Đối với Nginx, kiểm tra `fastcgi_pass` để biết website đang gửi request đến socket PHP-FPM nào. Nếu CLI dùng PHP 8.3 nhưng Nginx trỏ đến `php8.2-fpm.sock`, mọi kết luận từ CLI đều có thể sai với frontend. Bài [chạy nhiều phiên bản PHP với Nginx và PHP-FPM] có thể dùng làm tài liệu đối chiếu khi server có nhiều runtime.
3. Các directive OPcache quan trọng trong production
| Directive | Vai trò | Điểm cần cân nhắc |
|---|---|---|
| opcache.enable | Bật OPcache cho SAPI hỗ trợ | Phải kiểm tra ở PHP-FPM thực tế |
| opcache.memory_consumption | Dung lượng shared memory tính theo MB | Tăng khi cache gần đầy, nhưng phải dành RAM cho worker |
| opcache.interned_strings_buffer | Bộ nhớ cho interned strings | Dự án nhiều package có thể cần đo thêm |
| opcache.max_accelerated_files | Số file tối đa được cache | Phải bao phủ vendor và source thực tế |
| opcache.validate_timestamps | Có kiểm tra file thay đổi hay không | Tắt chỉ khi deploy có reset/restart cache rõ ràng |
| opcache.revalidate_freq | Khoảng thời gian giữa các lần kiểm tra timestamp | Giá trị thấp tiện development nhưng tăng I/O ở production |
PHP Manual hiện mô tả các mặc định như `opcache.enable=1`, `opcache.memory_consumption=128`, `opcache.interned_strings_buffer=8`, `opcache.max_accelerated_files=10000`, `opcache.validate_timestamps=1` và `opcache.revalidate_freq=2` [1]. Mặc định này chỉ là điểm xuất phát. Website nhiều file hoặc deploy atomic có thể cần profile khác sau khi đo hit rate và tình trạng cache.
4. Baseline cấu hình php.ini production
Một baseline an toàn nên ưu tiên tính quan sát và khả năng rollback. Ví dụ dưới đây không cố áp một dung lượng duy nhất; hãy thay đổi `memory_consumption` sau khi kiểm tra memory của máy và số lượng file PHP. Với máy ít RAM, tăng OPcache quá mức có thể cạnh tranh trực tiếp với PHP-FPM worker, MariaDB và hệ điều hành.
[opcache]
opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=128
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.max_wasted_percentage=10
opcache.validate_timestamps=1
opcache.revalidate_freq=60
opcache.save_comments=1`opcache.enable_cli=0` thường giúp tránh tiêu thụ shared memory không cần thiết trong các lệnh CLI ngắn, nhưng một số workflow chạy worker dài hoặc benchmark CLI có thể cần profile riêng. `opcache.save_comments=1` thường nên được giữ nếu framework, annotation hoặc thư viện cần docblock. Không tắt một directive chỉ vì thấy nó xuất hiện trong một bài tuning khác; hãy đối chiếu với phiên bản PHP và codebase.
5. Production deploy và vấn đề stale code
Khi `opcache.validate_timestamps=1`, PHP có thể kiểm tra timestamp theo chu kỳ `revalidate_freq`. Điều này thuận tiện khi chỉnh file trực tiếp nhưng không phải lúc nào cũng phù hợp với deploy atomic hoặc môi trường yêu cầu kiểm soát chặt. Khi `validate_timestamps=0`, code mới có thể chưa được dùng cho tới khi OPcache được reset hoặc worker được restart. Deploy thành công vì vậy phải bao gồm bước reload/restart PHP-FPM hoặc cơ chế reset cache tương đương.
# kiểm tra cấu hình trước khi reload
php-fpm8.3 -t 2>/dev/null || true
sudo nginx -t
# thay 8.3 bằng phiên bản thực tế
sudo systemctl reload php8.3-fpm
sudo systemctl is-active php8.3-fpm
# nếu cần restart hoàn toàn theo runbook
sudo systemctl restart php8.3-fpmReload có thể ít gián đoạn hơn restart, nhưng hành vi cụ thể phụ thuộc init system, package và pool. Sau deploy, gửi một request tới endpoint health của ứng dụng, kiểm tra log và xác nhận version/build hiện tại. Không nên dùng thao tác reset cache công khai cho người dùng nếu endpoint đó không có authentication và allowlist.
6. Kiểm tra trạng thái và hit rate
PHP cung cấp `opcache_get_status()` để trả về thông tin trạng thái instance cache trong bộ nhớ [2]. Có thể tạo script CLI hoặc endpoint nội bộ để đọc `memory_usage`, `opcache_statistics`, `num_cached_scripts`, `hits`, `misses` và `opcache_statistics['opcache_hit_rate']`. Endpoint web phải được bảo vệ, giới hạn IP hoặc chạy sau authentication; không để lộ đường dẫn source và thông số vận hành ra Internet.
<?php
header('Content-Type: application/json');
if (!function_exists('opcache_get_status')) {
http_response_code(503);
echo json_encode(['enabled' => false]);
exit;
}
$status = opcache_get_status(false);
$stats = $status['opcache_statistics'] ?? [];
$memory = $status['memory_usage'] ?? [];
echo json_encode([
'enabled' => $status['opcache_enabled'] ?? false,
'cached_scripts' => $stats['num_cached_scripts'] ?? null,
'hit_rate' => $stats['opcache_hit_rate'] ?? null,
'used_memory' => $memory['used_memory'] ?? null,
'free_memory' => $memory['free_memory'] ?? null,
], JSON_PRETTY_PRINT);Nếu script trả `opcache_enabled=false`, kiểm tra đúng SAPI và file ini trước khi tăng dung lượng. Nếu hit rate thấp, chưa chắc cache hỏng: traffic thấp, deploy vừa xảy ra hoặc code có nhiều file ít được gọi đều có thể làm tỷ lệ thấp. Nếu `free_memory` gần bằng không hoặc wasted memory tăng, cần xem log, số file, `max_accelerated_files` và kế hoạch restart cache thay vì chỉ tăng một con số.
7. Các lỗi thường gặp khi cấu hình OPcache
CLI báo enabled nhưng website vẫn không cache
CLI và FPM có thể đọc hai php.ini khác nhau. Dùng một endpoint kiểm tra nội bộ từ đúng virtual host, so sánh `PHP_VERSION`, `PHP_SAPI`, `php_ini_loaded_file()` và trạng thái OPcache. Sau khi xác định, chỉnh file cấu hình đúng SAPI rồi reload service tương ứng.
Deploy xong nhưng website vẫn chạy code cũ
Nguyên nhân thường là validate timestamp bị tắt, revalidate frequency chưa hết, worker chưa reload hoặc deploy thay file nhưng giữ inode/symlink không như dự kiến. Xác định chiến lược deploy trước: restart FPM sau release, dùng reload có kiểm soát hoặc bật timestamp check với khoảng thời gian hợp lý.
Tăng memory nhưng hết RAM
OPcache nằm ngoài memory của từng PHP worker nhưng vẫn dùng RAM hệ thống. Khi tăng `opcache.memory_consumption`, hãy xem tổng RAM dành cho FPM, database, web server và filesystem cache. Máy ít RAM nên ưu tiên đo RSS worker và đặt ngưỡng cảnh báo trước khi tuning thêm.
Không thấy đủ file trong cache
Nếu `max_accelerated_files` nhỏ hơn số file thực tế hoặc cache bị đầy, một phần script sẽ không được lưu. Kiểm tra số file source/vendor, tình trạng wasted memory và các giới hạn liên quan trong `opcache_get_status()`. Đừng chọn giá trị quá cao nếu server có nhiều website dùng chung RAM.
8. Checklist rollback và verify
- Lưu bản sao php.ini và ghi rõ phiên bản PHP-FPM trước khi chỉnh.
- Kiểm tra `php --ini`, SAPI và socket FPM đang phục vụ website.
- Chạy `nginx -t` và kiểm tra cấu hình FPM trước khi reload.
- Gửi request health sau deploy, xác nhận release/build mới và xem error log.
- Đọc trạng thái OPcache từ đúng SAPI; không công khai endpoint debug.
- Theo dõi memory, hit rate, cache đầy và OOM trong một khoảng thời gian đủ dài.
- Nếu lỗi xuất hiện, rollback php.ini hoặc release code rồi reload FPM theo runbook.
9. Kết luận
Cấu hình PHP OPcache production hiệu quả bắt đầu từ việc xác định đúng PHP-FPM đang chạy, sau đó chọn dung lượng và chính sách timestamp dựa trên codebase, RAM và quy trình deploy. Baseline có thể giúp bắt đầu nhanh, nhưng chỉ hit rate, memory usage, log và kiểm tra release mới cho biết cấu hình có phù hợp hay không. Khi kết hợp OPcache với Nginx, PHP-FPM và database, hãy coi toàn bộ chuỗi runtime là một hệ thống thay vì tối ưu một directive riêng lẻ.
Nguồn tham khảo
[1] PHP Manual — Runtime Configuration: OPcache; [2] PHP Manual — opcache_get_status.