بهینه سازی PHP-FPM و OPcache برای سایت

برای بهینه‌سازی PHP-FPM و اجرای PHP روی سرور سه چیز بیشترین اثر را دارد: تنظیم درست تعداد پروسه‌های PHP-FPM بر اساس حافظه، فعال و درست تنظیم‌شده بودن OPcache، و استفاده از یک کش شیء مثل Redis برای سایت‌های وردپرسی و فروشگاهی. هر کدام جداگانه قابل اندازه‌گیری‌اند و پیش از تغییر باید معلوم کنید گلوگاه سایت کجاست، وگرنه فقط عدد را بی‌دلیل عوض کرده‌اید.

این راهنما برای سروری است که Nginx یا Apache با PHP-FPM روی Ubuntu یا AlmaLinux دارد. مسیرها و شماره نسخه را با سرور خودتان تطبیق دهید. اگر هنوز وب‌سرور را نصب نکرده‌اید، نصب Nginx روی اوبونتو را ببینید.

اول اندازه بگیرید

پیش از هر تغییر، وضعیت فعلی را ثبت کنید تا بعد بتوانید مقایسه کنید:

php -v
php-fpm8.3 -t
sudo systemctl status php8.3-fpm
free -m

نسخه PHP را متناسب با سرور خودتان بنویسید. زمان پاسخ صفحه اصلی را هم چند بار اندازه بگیرید:

curl -o /dev/null -s -w "TTFB: %{time_starttransfer}s total: %{time_total}s\n" https://example.com/

دستورهای بیشتر بررسی منابع در دستورات مانیتورینگ سرور لینوکس آمده است. اگر کندی از دیتابیس است، تغییر تنظیمات PHP کمکی نمی‌کند و باید بهینه‌سازی دیتابیس را بررسی کنید.

بهینه‌سازی PHP-FPM: تعداد پروسه‌ها

فایل pool در Ubuntu معمولاً /etc/php/8.3/fpm/pool.d/www.conf است. مهم‌ترین تنظیم، حالت مدیریت پروسه‌هاست:

مقدار pmرفتار
staticتعداد ثابتی پروسه همیشه آماده است؛ برای سرور اختصاصی با حافظه کافی
dynamicبین حداقل و حداکثر تعداد، پروسه‌ها بسته به بار کم و زیاد می‌شوند؛ پیش‌فرض و رایج
ondemandپروسه فقط با درخواست ساخته می‌شود؛ مناسب سرورهای کم‌ترافیک و چند سایتی

برای حالت dynamic، این تنظیم‌ها اهمیت دارند:

pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 8
pm.max_requests = 500

عدد pm.max_children سقف تعداد پروسه‌هاست و باید بر اساس حافظه حساب شود. اول میانگین حافظه یک پروسه را بگیرید:

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

بعد حافظه‌ای را که برای PHP می‌توانید بدهید، بعد از کم کردن سهم دیتابیس و سیستم‌عامل، بر میانگین تقسیم کنید. مثلاً با ۲ گیگابایت حافظه آزاد و میانگین ۶۰ مگابایت، حدود ۳۰ پروسه. اگر max_children را بالاتر از ظرفیت حافظه بگذارید، سرور به swap می‌افتد و کندتر می‌شود. گزینه pm.max_requests هر پروسه را بعد از تعداد مشخصی درخواست دوباره می‌سازد و نشتی حافظه برنامه‌های ضعیف را مهار می‌کند.

برای اینکه بدانید ظرفیت پر شده است یا نه، وضعیت FPM را روشن کنید و لاگ را ببینید. پیام زیر در لاگ PHP-FPM یعنی سقف پر شده:

WARNING: [pool www] server reached pm.max_children setting (20), consider raising it

بعد از تغییر فایل، کانفیگ را تست و سرویس را reload کنید:

sudo php-fpm8.3 -t
sudo systemctl reload php8.3-fpm

OPcache: کامپایل نشدن دوباره کد در هر درخواست

PHP هر درخواست را از روی فایل کد اجرا می‌کند. OPcache نسخه کامپایل‌شده فایل‌ها را در حافظه نگه می‌دارد و زمان پاسخ را معمولاً به‌طور محسوسی کم می‌کند. روی بیشتر نصب‌ها همراه PHP هست. با این دستور ببینید فعال است:

php -i | grep opcache.enable

برای تنظیم، فایل /etc/php/8.3/fpm/conf.d/10-opcache.ini را ویرایش کنید:

opcache.enable=1
opcache.memory_consumption=192
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1
opcache.revalidate_freq=60

memory_consumption اندازه حافظه کش بر حسب مگابایت است و باید به اندازه‌ای باشد که همه فایل‌های PHP سایت در آن جا شوند. max_accelerated_files سقف تعداد فایل‌هاست و برای وردپرس با افزونه‌های زیاد باید بالاتر باشد. با validate_timestamps=1 تغییر فایل‌ها بعد از revalidate_freq ثانیه دیده می‌شود. روی سرور تولیدی که کدش فقط با استقرار عوض می‌شود، می‌توانید validate_timestamps را صفر کنید، ولی آن‌وقت باید بعد از هر به‌روزرسانی PHP-FPM را reload کنید وگرنه کد قدیمی اجرا می‌شود.

وضعیت OPcache را از طریق یک اسکریپت یا ابزار پایش ببینید؛ اگر نرخ برخورد (hit rate) پایین است یا حافظه پر شده، memory_consumption را بالا ببرید. مقدارها در بالا فقط نقطه شروع‌اند و به اندازه سایت بستگی دارند.

Redis برای کش شیء وردپرس

وردپرس برای هر صفحه ده‌ها کوئری به دیتابیس می‌فرستد. کش شیء نتیجه بسیاری از آن‌ها را در حافظه نگه می‌دارد. Redis معمولاً برای این کار انتخاب می‌شود:

sudo apt install redis-server php-redis
sudo systemctl enable --now redis-server

بعد با افزونه‌ای که از Redis پشتیبانی می‌کند، مثل Redis Object Cache، آن را به وردپرس وصل می‌کنید. به‌طور پیش‌فرض Redis فقط روی 127.0.0.1 گوش می‌دهد؛ آن را روی آدرس عمومی باز نکنید. این کش برای سایت‌های پرکوئری، مثل فروشگاه‌ها، اثر بیشتری دارد تا برای سایت ساده.

کش صفحه در وب‌سرور

بیشتر سایت‌های وردپرسی که بازدیدکننده ناشناس زیاد دارند، از کش صفحه بیشتر سود می‌برند تا از هر تنظیم PHP. اگر از Nginx استفاده می‌کنید، می‌توانید با FastCGI cache پاسخ را بدون رسیدن به PHP برگردانید، یا از یک افزونه کش صفحه استفاده کنید. کش صفحه برای کاربران واردشده و سبد خرید باید دور زده شود.

نسخه PHP را به‌روز نگه دارید

نسخه‌های جدید PHP معمولاً سریع‌تر هستند و وصله امنیتی می‌گیرند. قبل از ارتقا، سازگاری افزونه‌ها و قالب را روی یک نسخه آزمایشی بسنجید، چون ارتقای نسخه یکی از علت‌های رایج خطای 500 است؛ عیب‌یابی آن در خطای 500 Internal Server Error آمده است.

خطاهای رایج

نشانهعلت و راه حل
خطای 502 یا 504 در ترافیک بالاظرفیت PHP-FPM پر شده یا کوئری کند است؛ لاگ FPM و slow log را ببینید
سرور بعد از افزایش max_children کند شدحافظه کم آمده و سرور swap می‌کند؛ عدد را با حافظه تطبیق دهید
تغییر کد دیده نمی‌شودOPcache با validate_timestamps=0 کد قدیمی را نگه داشته؛ reload کنید
حافظه PHP-FPM مدام بالا می‌رودنشتی حافظه؛ pm.max_requests را کم کنید و افزونه مقصر را پیدا کنید

سوالات متداول

مقدار مناسب pm.max_children چند است؟

عدد ثابتی وجود ندارد و به حافظه سرور و میانگین مصرف هر پروسه بستگی دارد. حافظه قابل اختصاص به PHP را بر میانگین مصرف یک پروسه تقسیم کنید و کمی حاشیه بگذارید.

بعد از بهینه‌سازی PHP-FPM، OPcache هم لازم است؟

تقریباً همیشه مفید است و عیب مشخصی ندارد. فقط در محیط توسعه، که کد مدام عوض می‌شود، باید validate_timestamps روشن بماند.

Redis جای کش صفحه را می‌گیرد؟

نه. Redis کش شیء است و نتیجه کوئری‌ها را نگه می‌دارد؛ کش صفحه خروجی کامل HTML را. هر دو را می‌توان با هم به کار برد.

برای وردپرس Apache بهتر است یا Nginx؟

هر دو با PHP-FPM خوب کار می‌کنند. تفاوت اصلی در مدیریت فایل‌های .htaccess و تنظیمات کش است. تفاوت‌ها را در وب سرور لینوکس: نصب و پیکربندی پیشرفته می‌بینید.

منابع