سایت را روی سرور گذاشتهاید، کد را عوض میکنید، صفحه را رفرش میکنید و هیچ اتفاقی نمیافتد. فایل را دوباره ذخیره میکنید، باز هم همان خروجی قدیمی. بعد یک بار سرویس PHP را ریاستارت میکنید و همهچیز درست میشود. این دقیقاً همان جایی است که باید سراغ OPcache بروید، نه سراغ کش مرورگر و نه سراغ CDN.
OPcache بایتکد کامپایلشدهٔ فایلهای PHP را در حافظهٔ اشتراکی نگه میدارد تا هر درخواست مجبور نباشد دوباره فایل را از دیسک بخواند و پارس کند. روی یک سایت وردپرسی معمولی، همین یک لایه بهتنهایی میتواند زمان اجرای اسکریپت را از حدود ۸۰ میلیثانیه به ۲۵ میلیثانیه برساند. عدد دقیق به تعداد افزونهها و اندازهٔ کد بستگی دارد، ولی جهت تغییر همیشه همین است.
چرا تغییر کد اعمال نمیشود
پارامتر کلیدی اینجاست: opcache.validate_timestamps. وقتی مقدارش 1 باشد، OPcache هر چند ثانیه یکبار (بر اساس opcache.revalidate_freq) زمان تغییر فایل را چک میکند و اگر فایل جدیدتر باشد، نسخهٔ کششده را دور میریزد. وقتی مقدارش 0 باشد، این بررسی کاملاً خاموش میشود و تا ریاستارت PHP یا پاک شدن کش، همان بایتکد قدیمی سرو میشود.
روی سرور پروداکشن، 0 گذاشتن وسوسهکننده است چون چند میلیثانیه صرفهجویی میکند. ولی هزینهاش این است که هر دیپلوی باید با یک مرحلهٔ صریح پاکسازی کش همراه باشد، وگرنه نیمی از کاربران نسخهٔ قدیمی سایت را میبینند و نیمی نسخهٔ جدید. اگر فرآیند دیپلوی شما خودکار و قابلاعتماد نیست، 1 را نگه دارید و opcache.revalidate_freq را روی 2 بگذارید. تفاوت کاراییاش در عمل ناچیز است؛ تفاوت دردسرش نیست.
اینجا اشتباه میکنند
خطای رایج این است که توسعهدهنده روی سرور توسعه هم validate_timestamps=0 میگذارد، بعد ساعتها دنبال باگی میگردد که وجود ندارد. نشانهاش این است: کد را با var_dump عوض میکنید، خروجی عوض نمیشود، ولی وقتی فایل را کپی میکنید به یک نام جدید و آن را صدا میزنید، درست کار میکند. آن لحظه یعنی کش، نه منطق برنامه، مقصر است. روی محیط توسعه همیشه validate_timestamps=1 و revalidate_freq=0 بگذارید تا هر تغییر بلافاصله دیده شود.
حافظهٔ OPcache را چقدر بگذاریم
مقدار پیشفرض opcache.memory_consumption در بسیاری از بیلدها ۱۲۸ مگابایت است و برای یک سایت وردپرسی با چند افزونه کافی نیست. وقتی این حافظه پر شود، OPcache شروع میکند به دور ریختن اسکریپتها و دوباره کامپایل کردنشان؛ نتیجهاش افت کارایی است در حالی که هیچ خطایی در لاگ نمیبینید.
برای دیدن وضعیت واقعی، اسکریپت opcache_get_status() را اجرا کنید یا از خروجی php -i | grep opcache شروع کنید. اگر opcache_get_status()['opcache_statistics']['oom_restarts'] بزرگتر از صفر است، یعنی حافظه کم آوردهاید. عدد 256 برای اکثر سایتها نقطهٔ شروع معقولی است؛ سایتهای سنگین با فریمورکهای بزرگ به ۵۱۲ هم میرسند.
دو پارامتر دیگر هم مهماند. opcache.max_accelerated_files سقف تعداد فایلهای کششده است و پیشفرضش ۱۰۰۰۰ است. یک نصب وردپرس با افزونههای سنگین بهراحتی از این عدد رد میشود. مقدار 20000 یا 30000 بگذارید. و opcache.interned_strings_buffer که پیشفرض ۸ مگابایت است؛ روی ۱۶ یا ۳۲ بگذارید تا رشتههای تکراری بین فایلها به اشتراک گذاشته شوند.
نمونهٔ پیکربندی
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1
opcache.revalidate_freq=2
opcache.save_comments=1
opcache.fast_shutdown=1
آن save_comments=1 را دستکم نگیرید. اگر روی 0 بگذارید، انوتیشنها و کامنتهای DocBlock از بایتکد حذف میشوند و هر چیزی که به آنها تکیه دارد از کار میافتد؛ از ORMهای مبتنی بر انوتیشن تا بعضی افزونههای وردپرس. صرفهجویی حافظهاش کوچک است، هزینهٔ خرابیاش بزرگ.
OPcache یا APCu یا Redis؟
اینها جای هم را نمیگیرند و اشتباه گرفتنشان وقت آدم را تلف میکند.
| لایه | چه چیزی را کش میکند | کجا به کار میآید |
|---|---|---|
| OPcache | بایتکد فایلهای PHP | همهجا، بدون استثنا |
| APCu | دادههای کاربردی در حافظهٔ همان سرور | کش آبجکت تکسروری |
| Redis / Memcached | دادههای کاربردی روی سرویس جدا | چند سرور، سشن مشترک |
اگر سایت روی یک سرور است، APCu سادهتر و سریعتر از Redis جواب میدهد چون رفتوبرگشت شبکه ندارد. اگر روی چند سرور پشت لودبالانسر هستید، APCu بیفایده است چون هر سرور کش خودش را دارد و کاربر بعد از هر درخواست ممکن است به سرور دیگری برود. آنجا Redis انتخاب درست است. برای راهاندازی روی یک هاست لینوکس تکسروری، ترکیب OPcache بههمراه APCu تقریباً همیشه کافی است.
وقتی OPcache را باید خاموش کنید
روی محیط توسعه، همانطور که گفتیم. همچنین وقتی از opcache.file_cache استفاده میکنید و فایلهای کش روی دیسک مشترک بین نسخههای مختلف PHP قرار دارند؛ این حالت میتواند بایتکد ناسازگار برگرداند و خطای عجیبی بدهد که هیچ ربطی به کد شما ندارد. اگر چنین خطایی دیدید، اول opcache.file_cache را خالی کنید.
و یک نکتهٔ عملی: بعد از هر دیپلوی، اگر validate_timestamps=0 دارید، پاکسازی کش را فراموش نکنید. سادهترین راه، فراخوانی opcache_reset() از یک اسکریپت CLI است، یا ریاستارت php-fpm با systemctl reload php8.2-fpm. دقت کنید که reload کافی است و نیازی به restart کامل نیست؛ reload اتصالات فعال را قطع نمیکند.
اگر بعد از این تنظیمات هنوز کندی میبینید، مسئله جای دیگری است. با خواندن لاگ خطای سرور شروع کنید و ببینید آیا خطای تکراریای وجود دارد که هر بار بخشی از کار را دوباره انجام میدهد. اگر هم سایت گاهی اوقات کلاً از دسترس خارج میشود، مسیر درست ریشهیابی قطعیهای متناوب است، نه دستزدن به تنظیمات کش.
پرسشهای پرتکرار
چرا بعد از تغییر فایل PHP تغییرات اعمال نمیشود؟
چون OPcache نسخهٔ کامپایلشدهٔ قبلی را در حافظه نگه داشته و بررسی زمان تغییر فایل خاموش است. مقدار opcache.validate_timestamps را چک کنید؛ اگر 0 است، یا آن را 1 کنید یا بعد از هر تغییر کش را با opcache_reset() پاک کنید.
مقدار مناسب opcache.memory_consumption چقدر است؟
برای بیشتر سایتهای وردپرسی ۲۵۶ مگابایت نقطهٔ شروع خوبی است. اگر در خروجی opcache_get_status() مقدار oom_restarts بزرگتر از صفر بود، حافظه کم است و باید بالاتر ببرید. سایتهای سنگین با فریمورکهای بزرگ تا ۵۱۲ مگابایت هم میروند.
آیا OPcache روی هاست اشتراکی هم فعال است؟
در بیشتر هاستهای لینوکس OPcache بهصورت پیشفرض فعال است، ولی مقادیرش را معمولاً نمیتوانید از داخل پنل تغییر دهید چون تنظیمات در سطح سرور اعمال میشود. اگر به تنظیم دقیق نیاز دارید، باید سراغ سروری بروید که کنترل کامل روی php.ini دارد.
تفاوت OPcache با کش وردپرس چیست؟
OPcache سطح پایینتر و مستقل از وردپرس کار میکند و فقط بایتکد فایلهای PHP را کش میکند. کش وردپرس خروجی کوئریها و قطعات HTML را نگه میدارد. این دو رقیب هم نیستند؛ هر دو باید فعال باشند و هر کدام لایهٔ متفاوتی را پوشش میدهند.
اگر همین حالا روی سرور خودتان دسترسی به php.ini دارید، اول مقدار max_accelerated_files را چک کنید. این پارامتر بیشتر از آنچه فکر میکنید روی سایتهای وردپرسی اثر دارد و کمترین توجه را میگیرد.
دیدگاهها ۰
هنوز دیدگاهی ثبت نشده؛ اولین نفر باشید!