دسته بندی:

آموزش نصب Memcached در لینوکس: پیکربندی، امنیت و رفع خطا

هر بار که یک صفحه از سایت باز می‌شود، همان چند ده کوئری تکراری دوباره به دیتابیس فرستاده می‌شود؛ نتیجه، بالا رفتن لود سرور و کند شدن سایت است. Memcached (مم‌کش) دقیقاً برای همین ساخته شده است: یک سیستم کش بسیار قوی و پیشرفته که اشیاء و نتیجه کوئری‌های دیتابیس را در حافظه و رم سرور ذخیره می‌کند تا دفعه بعد به‌جای اجرای دوباره کوئری، پاسخ مستقیم از رم خوانده شود. این کار کمک فراوانی به کاهش لود سرور و سریع‌تر شدن لود وب‌سایت شما می‌کند.

در ادامه، نصب Memcached روی توزیع‌های امروزی لینوکس، پیکربندی حافظه و پورت، نصب افزونه PHP، ایمن‌سازی سرویس و خطاهای رایج را قدم‌به‌قدم می‌بینیم.

Memcached دقیقاً چه کاری انجام می‌دهد

Memcached یک سرویس مستقل (daemon) است که روی پورت ۱۱۲۱۱ گوش می‌دهد و یک انبار ساده «کلید – مقدار» در رم فراهم می‌کند. برنامه شما پیش از رفتن سراغ دیتابیس از Memcached می‌پرسد «این کلید را داری؟». اگر داشته باشد (hit) کوئری اصلاً اجرا نمی‌شود و اگر نداشته باشد (miss) کوئری اجرا شده و نتیجه آن برای دفعات بعد در کش نوشته می‌شود.

Memcached بیشترین تاثیر را در وب‌سایت‌هایی با دیتابیس‌های بزرگ و تعداد کوئری زیاد دارد. اگر دیتابیس سایت شما بزرگ و سنگین است (برای مثال از نوع MySQL) استفاده از Memcached را توصیه می‌کنیم. همچنین بیشترین بازدهی Memcached زمانی به دست می‌آید که روی یک سرور جداگانه مخصوص کش اجرا شود؛ در این حالت همه وب‌سرورها به یک کش مشترک وصل می‌شوند و به‌جای چند کش تکراری و ناهماهنگ، یک منبع واحد دارید.

قبل از نصب: آیا Memcached ابزار درست برای شماست

  • داده‌های Memcached فقط در رم هستند. با هر ری‌استارت سرویس یا ریبوت سرور، کل کش خالی می‌شود. Memcached هرگز نباید محل نگهداری داده‌ای باشد که نسخه اصلی آن جای دیگری وجود ندارد.
  • اگر به ساختارهای داده (list، hash، sorted set)، ذخیره‌سازی روی دیسک یا replication نیاز دارید، Redis گزینه مناسب‌تری است. Memcached عمداً ساده نگه داشته شده: کلید، مقدار، زمان انقضا و حذف خودکار قدیمی‌ترین‌ها (LRU). در عوض چندنخی (multi-threaded) است و روی بار سنگین ساده و پیش‌بینی‌پذیر رفتار می‌کند.
  • اگر مشکل شما کش کردن خروجی نهایی HTML است و نه کوئری‌های تکراری، ابزار درست یک reverse proxy cache مانند Varnish است. این دو رقیب هم نیستند و معمولاً کنار هم استفاده می‌شوند.
  • اگر فقط یک سرور دارید و می‌خواهید داده‌ها را روی همان ماشین کش کنید، APCu سبک‌تر و سریع‌تر از Memcached است چون اصلاً از شبکه عبور نمی‌کند. توجه کنید که افزونه قدیمی APC از PHP 7 به بعد وجود ندارد و جای آن را OPcache (برای کش opcode) و APCu (برای کش داده) گرفته‌اند.
  • روی هاست اشتراکی معمولاً امکان اجرای این سرویس را ندارید؛ Memcached به دسترسی root روی سرور مجازی یا اختصاصی نیاز دارد.
  • رم را از دیتابیس نگیرید. اگر سرور رم محدودی دارد، اختصاص دادن حجم زیاد به Memcached می‌تواند buffer pool دیتابیس را کوچک کند و نتیجه معکوس بدهد.

نصب Memcached روی لینوکس

AlmaLinux، Rocky Linux، RHEL و CentOS Stream (نسخه ۸ و بالاتر)

dnf install memcached
systemctl enable --now memcached

Ubuntu و Debian

apt update
apt install memcached
systemctl enable --now memcached

دستورهای قدیمی و معادل امروزی آن‌ها

نسخه اول این آموزش برای CentOS/RHEL 6 و نسخه‌های قدیمی Fedora نوشته شده بود. آن دستورها هنوز در همان سیستم‌ها کار می‌کنند، اما همه آن توزیع‌ها سال‌هاست به پایان پشتیبانی رسیده‌اند و نباید سرور تازه‌ای با آن‌ها راه‌اندازی شود:

# خانواده RHEL نسخه 7 و قدیمی‌تر (در نسخه 8 به بعد yum فقط نام مستعار dnf است)
yum install memcached

# فقط CentOS/RHEL 5 که کاملاً منسوخ است
yum --enablerepo=remi install memcached

همچنین دستورهای service memcached start و /etc/init.d/memcached start جای خود را در تمام توزیع‌های امروزی به systemd داده‌اند:

systemctl start memcached
systemctl stop memcached
systemctl restart memcached
systemctl status memcached

پیکربندی: حافظه، پورت و اینترفیس

محل فایل پیکربندی بسته به توزیع فرق می‌کند:

  • خانواده RHEL (AlmaLinux، Rocky، CentOS): /etc/sysconfig/memcached
  • Debian و Ubuntu: /etc/memcached.conf

نمونه محتوای /etc/sysconfig/memcached:

PORT="11211"
USER="memcached"
MAXCONN="1024"
CACHESIZE="512"
OPTIONS="-l 127.0.0.1 -U 0"

در Debian و Ubuntu همان تنظیمات به شکل سوییچ خط فرمان در /etc/memcached.conf نوشته می‌شوند. این فایل از قبل وجود دارد و مقدارهای پیش‌فرض خودش را دارد؛ خط‌های زیر را در همان فایل ویرایش یا اضافه کنید و کل فایل را با این چند خط جایگزین نکنید:

-m 512
-p 11211
-u memcache
-l 127.0.0.1
-U 0

معنی گزینه‌های مهم: CACHESIZE یا -m حجم حافظه اختصاصی کش بر حسب مگابایت است، -l تعیین می‌کند سرویس روی کدام آدرس گوش بدهد و -U 0 پروتکل UDP را کاملاً خاموش می‌کند. دقت کنید که نام کاربر سرویس در Debian و Ubuntu برابر memcache و در خانواده RHEL برابر memcached است؛ اشتباه نوشتن آن یکی از رایج‌ترین دلایل بالا نیامدن سرویس است.

بعد از هر تغییر باید سرویس را ری‌استارت کنید و به یاد داشته باشید که با این کار کل کش خالی می‌شود:

systemctl restart memcached

نصب افزونه PHP برای اتصال به Memcached

نصب خود سرویس کافی نیست؛ PHP هم باید کلاینت آن را داشته باشد. اگر این ماژول را روی سرور نصب نکنید، هنگام استفاده با پیغام خطا (معمولاً Class "Memcached" not found) روبه‌رو خواهید شد.

دو افزونه متفاوت با نام‌های بسیار نزدیک وجود دارد و اشتباه گرفتن آن‌ها رایج‌ترین سردرگمی این مسیر است:

  • memcache (بدون d): افزونه قدیمی‌تر با کلاس Memcache. بسته php-pecl-memcache همین است و در نسخه قدیمی این آموزش معرفی شده بود.
  • memcached (با d): افزونه مبتنی بر کتابخانه libmemcached با کلاس Memcached که پروتکل باینری، SASL و multi-get را پشتیبانی می‌کند. امروز این گزینه پیشنهادی است.

نصب افزونه پیشنهادی:

# Debian و Ubuntu
apt install php php-memcached

# خانواده RHEL — ابتدا مخزن EPEL را فعال کنید
dnf install epel-release
dnf install php php-pecl-memcached

در خانواده RHEL بسته php-pecl-memcached در مخزن پیش‌فرض (AppStream) نیست و از مخازن جانبی می‌آید: EPEL آن را دارد و مخزن Remi هم معمولاً نسخه تازه‌تری ارائه می‌کند. اگر روی سرور شما از پیش Remi فعال است، همان را نگه دارید و مخزن دوم را اضافه نکنید تا بسته‌های PHP بین دو مخزن قاطی نشوند. اگر بسته آماده در دسترس نبود می‌توانید افزونه را با PECL کامپایل کنید که به php-devel و هدرهای libmemcached نیاز دارد.

پس از نصب، سرویس‌ها را ری‌استارت کنید. از سه دستور زیر فقط آن‌هایی را اجرا کنید که روی سرور شما واقعاً نصب‌اند:

systemctl restart php-fpm      # در اوبونتو معمولاً php8.2-fpm یا نسخه نصب‌شده شما
systemctl restart httpd        # خانواده RHEL
systemctl restart apache2      # دبیان و اوبونتو

دستورهای قدیمی /etc/init.d/httpd restart و service httpd restart همین کار را می‌کردند و امروز معادل systemd آن‌ها استفاده می‌شود. نکته مهم: اگر PHP را با PHP-FPM اجرا می‌کنید، ری‌استارت وب‌سرور به‌تنهایی کافی نیست و حتماً باید سرویس FPM هم ری‌استارت شود. برای اطمینان از فعال بودن افزونه:

php -m | grep memcached

اجرای Memcached روی سرور جداگانه و باز کردن پورت ۱۱۲۱۱

در صورتی که از Memcached روی سروری جداگانه استفاده می‌کنید، دو کار لازم است: سرویس باید روی آدرس شبکه داخلی گوش بدهد و پورت ۱۱۲۱۱ باید در فایروال، آن هم فقط برای IP وب‌سرورها، باز شود.

ابتدا در فایل پیکربندی مقدار -l را به آدرس شبکه خصوصی سرور کش تغییر دهید (مثلاً -l 10.0.0.10) و سرویس را ری‌استارت کنید. سپس بسته به فایروال سرور:

# firewalld (خانواده RHEL نسخه 7 به بعد)
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.20/32" port port="11211" protocol="tcp" accept'
firewall-cmd --reload

# ufw (اوبونتو)
ufw allow from 10.0.0.20 to any port 11211 proto tcp

اگر ufw تا امروز روی سرور فعال نبوده، پیش از ufw enable حتماً اول قانون SSH را اضافه کنید (ufw allow 22/tcp یا پورت سفارشی خودتان)؛ در غیر این صورت با فعال شدن فایروال ارتباط SSH خودتان قطع می‌شود.

اگر هنوز روی iptables کلاسیک هستید (در خانواده RHEL نیازمند بسته iptables-services)، همان روش قدیمی کار می‌کند؛ فایل قوانین را ویرایش و خط INPUT را اضافه کنید. مسیر زیر مخصوص خانواده RHEL است؛ در دبیان و اوبونتو چنین فایلی وجود ندارد و قوانین با iptables-persistent در /etc/iptables/rules.v4 نگهداری می‌شوند:

nano -w /etc/sysconfig/iptables

# افزودن این خط در بخش INPUT:
-A INPUT -m state --state NEW -m tcp -p tcp -s 10.0.0.20 --dport 11211 -j ACCEPT

# سپس اعمال قوانین:
systemctl restart iptables

# معادل‌های قدیمی همین دستور روی سیستم‌های بدون systemd:
# service iptables restart
# /etc/init.d/iptables restart

دو نکته که نادیده گرفتن‌شان یا قانون را بی‌اثر می‌کند یا شما را از سرور بیرون می‌اندازد: این خط باید بالاتر از هر خط REJECT یا DROP پایانی زنجیره INPUT قرار بگیرد، وگرنه هرگز اجرا نمی‌شود؛ و چون یک خطای نگارشی در این فایل می‌تواند هنگام ری‌استارت کل قوانین را خراب کند و دسترسی SSH را ببندد، پیش از ویرایش یک نسخه پشتیبان بگیرید (cp /etc/sysconfig/iptables /root/iptables.bak) و ترجیحاً از کنسول یا KVM ارائه‌دهنده هم دسترسی داشته باشید.

در نسخه قدیمی این مطلب همین قانون بدون بخش -s نوشته شده بود، یعنی پورت ۱۱۲۱۱ روی کل اینترنت باز می‌شد. این کار را انجام ندهید؛ دلیلش در بخش بعد آمده است.

امنیت: مهم‌ترین بخش این آموزش

Memcached به‌صورت پیش‌فرض هیچ احراز هویتی ندارد. هر کسی که بتواند به پورت ۱۱۲۱۱ وصل شود می‌تواند کل محتوای کش را بخواند، تغییر دهد یا پاک کند. علاوه بر آن، پروتکل UDP این سرویس در گذشته به‌طور گسترده برای حملات تقویتی DDoS سوءاستفاده شد و به همین دلیل از نسخه ۱٫۵٫۶ به بعد، UDP به‌صورت پیش‌فرض غیرفعال است.

  • اگر Memcached و وب‌سرور روی یک ماشین هستند، -l 127.0.0.1 بگذارید و کار تمام است؛ نیازی به باز کردن هیچ پورتی در فایروال نیست.
  • اگر جدا هستند، فقط روی IP شبکه خصوصی گوش بدهید و دسترسی فایروال را به IP مشخص وب‌سرورها محدود کنید.
  • هرگز روی IP عمومی و بدون محدودیت فایروال گوش ندهید.
  • UDP را با -U 0 خاموش نگه دارید.
  • اگر شبکه خصوصی مطمئنی در اختیار ندارید، ترافیک را از داخل VPN یا تونل عبور دهید یا از احراز هویت SASL استفاده کنید (نیازمند نسخه‌ای از memcached است که با پشتیبانی SASL بیلد شده باشد).

چطور مطمئن شویم واقعاً کار می‌کند

memcached --version
systemctl status memcached
ss -lntp | grep 11211
printf "statsnquitn" | nc 127.0.0.1 11211

روش قدیمی تست اتصال از راه دور echo stats | nc memcache_host_name_or_ip 11211 بود که هنوز پاسخ می‌گیرد، اما چون Memcached اتصال را خودش نمی‌بندد ترمینال ظاهراً هنگ می‌کند؛ افزودن quit این مشکل را حل می‌کند. اگر nc نصب نبود، با dnf install nmap-ncat یا apt install netcat-openbsd آن را نصب کنید.

در خروجی stats به این مقادیر نگاه کنید: get_hits و get_misses نسبت موفقیت کش را نشان می‌دهند، curr_items تعداد کلیدهای موجود است، bytes و limit_maxbytes مصرف حافظه را می‌گویند و evictions تعداد کلیدهایی است که پیش از انقضا برای باز کردن جا حذف شده‌اند. اگر evictions مدام بالا می‌رود، حافظه اختصاص‌یافته کم است.

تست نهایی از داخل PHP:

<?php
$m = new Memcached();
$m->addServer('127.0.0.1', 11211);
$m->set('os_test', 'ok', 60);
var_dump($m->get('os_test'));

خروجی باید string(2) "ok" باشد. اگر bool(false) گرفتید، متد getResultMessage() علت دقیق را نشان می‌دهد.

خطاهای رایج و علت آن‌ها

  • Class “Memcached” not found: افزونه نصب نشده یا برای SAPI درست فعال نیست. ممکن است در CLI فعال باشد و در PHP-FPM نه؛ یک بار با php -m و یک بار با phpinfo() از مسیر وب بررسی کنید.
  • Connection refused: یا سرویس بالا نیست، یا روی 127.0.0.1 گوش می‌دهد و شما از سرور دیگری وصل می‌شوید.
  • اتصال بی‌پاسخ و timeout: تقریباً همیشه فایروال یا Security Group است. با ss -lntp روی سرور کش مطمئن شوید سرویس روی IP درست گوش می‌دهد، سپس از وب‌سرور تست کنید.
  • سرویس بعد از تغییر کانفیگ بالا نمی‌آید: با journalctl -u memcached -n 50 لاگ را ببینید. دو علت رایج: نام کاربر اشتباه (memcache در برابر memcached) و مقدار CACHESIZE بزرگ‌تر از رم آزاد سرور.
  • کش هر چند وقت یک‌بار خالی می‌شود: یا سرویس ری‌استارت شده یا evictions به‌خاطر کمبود حافظه رخ داده است؛ خروجی stats پاسخ را می‌دهد.
  • سایت محتوای قدیمی نشان می‌دهد: این مشکل Memcached نیست. Memcached نمی‌داند داده شما کی کهنه شده و فقط به TTL و دستور حذفی که برنامه یا افزونه می‌فرستد عمل می‌کند؛ منطق invalidation را در همان‌جا بررسی کنید.

جمع‌بندی

نصب Memcached روی لینوکس امروز به دو دستور خلاصه می‌شود، اما کار واقعی بعد از نصب شروع می‌شود: تعیین حجم درست حافظه، نصب افزونه سمت PHP، محدود کردن سرویس به شبکه داخلی و بستن پورت ۱۱۲۱۱ روی اینترنت. اگر سایت شما کوئری‌های تکراری زیادی به یک دیتابیس بزرگ می‌زند، همین چند قدم می‌تواند لود سرور را به‌طور محسوسی پایین بیاورد؛ و اگر ترافیک شما کم است یا داده‌ها باید ماندگار باشند، بهتر است سراغ ابزار دیگری بروید.