تشخیص علت لود بالای سرور؛ راهنمای عملی و گام‌به‌گام

در این مقاله یاد می‌گیرید که لود بالای سرور را ریشه‌یابی کنید: خواندن load average، یافتن فرایند مقصر و تفکیک مشکل CPU از I/O با مثال‌های واقعی.

۶ دقیقه به‌روزرسانی ۶ شهریور ۱۴۰۵

لود بالای سرور؛ مسئله‌ای که دیر یا زود با آن روبه‌رو می‌شوید

اگر سرور لینوکسی مدیریت می‌کنید، تقریباً قطعی است که روزی با پیام‌هایی مثل «سرور خیلی کند شده» یا «سایت بالا نمی‌آید» مواجه می‌شوید. اولین چیزی که به ذهن می‌رسد، لود بالای سرور است. اما مشکل اینجاست که «لود» یک عدد مبهم است؛ عددی که اگر ندانید چطور بخوانیدش، ممکن است ساعت‌ها در مسیر اشتباه دنبال مقصر بگردید.

هدف این مقاله این است که دقیقاً همین را روشن کند: چطور load average را درست تفسیر کنید، چطور فرایند مقصر را پیدا کنید، و مهم‌تر از همه، چطور تشخیص دهید که مشکل از CPU است یا از I/O (ورودی/خروجی دیسک). این تفکیک، نیمی از راه حل است. چون درمان یک سرور CPU-bound با درمان یک سرور I/O-bound کاملاً فرق دارد.

خواندن load average؛ عددی که اکثراً اشتباه تفسیر می‌شود

وقتی دستور uptime یا top را اجرا می‌کنید، خروجی‌ای شبیه این می‌بینید:

load average: 4.52, 3.87, 3.21

این سه عدد به ترتیب میانگین بار در ۱ دقیقه، ۵ دقیقه و ۱۵ دقیقه اخیر هستند. اما «بار» دقیقاً یعنی چه؟ در لینوکس، load average تعداد فرایندهایی است که در صف اجرا منتظرند (runnable) به اضافه تعداد فرایندهایی که در انتظار I/O هستند (uninterruptible sleep). این نکته خیلی مهم است: لود فقط مربوط به CPU نیست؛ فرایندی که منتظر خواندن از دیسک است هم در این عدد حساب می‌شود.

عدد «مناسب» چقدر است؟

یک قانون سرانگشتی ساده: load average را با تعداد هسته‌های CPU مقایسه کنید. اگر لود برابر تعداد هسته‌ها باشد، یعنی CPU تقریباً کاملاً مشغول است. اگر لود از تعداد هسته‌ها بیشتر شود، صف انتظار تشکیل شده و پاسخ‌دهی سرور افت می‌کند.

مثلاً روی یک سرور ۴ هسته‌ای، لود ۴ یعنی CPU پر است اما هنوز صف انتظار جدی نداریم. لود ۸ یعنی دو برابر ظرفیت، و کاربران تأخیر محسوسی را حس خواهند کرد. اما این قانون برای I/O-bound صادق نیست؛ یک سرور با ۴ هسته و لود ۶ ممکن است CPU تقریباً بیکار داشته باشد و مشکل از دیسک باشد.

اشتباه رایج: خیلی‌ها فکر می‌کنند لود ۱ روی یک سرور ۱ هسته‌ای یعنی «کاملاً عادی». در حالی که اگر آن ۱ واحد از یک فرایند I/O-bound باشد، سرور ممکن است به شدت کند باشد. همیشه به ستون wa (I/O wait) در top هم نگاه کنید.

گام اول: پیدا کردن فرایند مقصر با top و ps

وقتی لود بالا را تأیید کردید، اولین ابزار top است. اما فقط نگاه کردن به ستون %CPU کافی نیست. ترتیب پیشنهادی من این است:

  1. دستور top را اجرا کنید و کلید P را بزنید تا بر اساس مصرف CPU مرتب شود.
  2. کلید M را بزنید تا بر اساس مصرف حافظه مرتب شود (گاهی مشکل RAM خودش را به شکل لود بالا نشان می‌دهد).
  3. به ستون STATE دقت کنید: اگر فرایندی در حالت D (uninterruptible sleep) باشد، یعنی منتظر I/O است.

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

ps -eo pid,ppid,user,stat,%cpu,%mem,cmd --sort=-%cpu | head -20

این دستور ۲۰ فرایند پرمصرف را بر اساس CPU نشان می‌دهد. اگر خروجی را می‌خواهید بر اساس I/O ببینید، pidstat -d ابزار بهتری است (بسته به sysstat):

pidstat -d 2 5

این دستور هر ۲ ثانیه، ۵ بار، میزان خواندن و نوشتن هر فرایند را بر حسب KB/s نشان می‌دهد. اگر فرایندی مثل mysqld یا php-fpm را می‌بینید که مدام در حال خواندن از دیسک است، مشکل از I/O است نه CPU.

تفکیک CPU از I/O؛ مهم‌ترین مهارت در تشخیص لود بالای سرور

این بخش قلب مقاله است. اگر این تفکیک را درست انجام دهید، نیمی از راه را رفته‌اید. سه سناریو را بررسی می‌کنیم.

سناریوی ۱: CPU-bound (مشکل از پردازنده)

علائم: در top، ستون %CPU فرایندی نزدیک ۱۰۰٪ یا بیشتر است (برای فرایندهای چندنخی). ستون wa پایین است (زیر ۵٪). load average بالا و معمولاً نزدیک یا بیشتر از تعداد هسته‌هاست.

مثال واقعی: یک اسکریپت PHP که حلقه‌ای بی‌نهایت دارد یا یک کوئری MySQL که از ایندکس استفاده نمی‌کند و کل جدول را اسکن می‌کند. در این حالت، راه حل بهینه‌سازی کد یا افزودن CPU است، نه ارتقای دیسک.

سناریوی ۲: I/O-bound (مشکل از دیسک)

علائم: در top، ستون wa بالاست (مثلاً بالای ۲۰٪). فرایندها در حالت D هستند. load average بالاست اما %CPU فرایندها پایین است (زیر ۳۰٪).

برای تأیید، از iostat استفاده کنید:

iostat -x 2 3

به ستون %util نگاه کنید. اگر نزدیک ۱۰۰٪ باشد، دیسک اشباع شده است. ستون await هم میانگین زمان پاسخ دیسک را بر حسب میلی‌ثانیه نشان می‌دهد؛ مقدار بالای ۲۰ms برای SSD و بالای ۱۰۰ms برای HDD یعنی دیسک دارد جان می‌کند.

نکته عیب‌یابی: اگر %util بالاست اما await پایین است، احتمالاً مشکل از تعداد زیاد I/Oهای کوچک است نه ظرفیت دیسک. در این حالت، بررسی کنید آیا سرویسی مثل logrotate یا یک cron job سنگین در حال اجراست.

سناریوی ۳: ترکیبی (هر دو)

گاهی مشکل ترکیبی است؛ مثلاً یک فرایند PHP-FPM هم CPU مصرف می‌کند و هم لاگ‌های سنگین می‌نویسد. در این حالت، ابتدا مشکل I/O را حل کنید (چون معمولاً ارزان‌تر است)، سپس دوباره لود را بررسی کنید.

ابزارهای پیشرفته‌تر برای ریشه‌یابی دقیق

اگر ابزارهای پایه کافی نبودند، این ابزارها را امتحان کنید:

atop؛ نمای کامل‌تر از top

atop به شما امکان می‌دهد تاریخچه را هم ببینید. با کلید d به نمای دیسک بروید و با c به نمای CPU. نکته جالب: atop مصرف CPU را بر اساس «هسته» نشان می‌دهد و تشخیص اینکه یک فرایند تک‌نخی یک هسته را اشباع کرده راحت‌تر است.

strace؛ ردیابی فراخوانی‌های سیستمی

اگر فرایند مقصر را پیدا کرده‌اید اما نمی‌دانید چرا اینقدر I/O مصرف می‌کند، strace را روی آن attach کنید:

strace -p PID -f -e trace=file,read,write -o /tmp/strace.log

بعد از چند ثانیه، Ctrl+C بزنید و فایل لاگ را بررسی کنید. اگر می‌بینید که مدام یک فایل خاص را باز و بسته می‌کند، مشکل از طراحی برنامه است.

هشدار: strace را روی فرایندهای تولیدی (production) با احتیاط استفاده کنید؛ می‌تواند سرعت فرایند را به شدت کاهش دهد. بهتر است ابتدا روی یک محیط تست امتحان کنید.

راه‌حل‌های عملی برای هر سناریو

بعد از تشخیص، نوبت اقدام است. این‌ها رایج‌ترین راه‌حل‌ها هستند:

اگر مشکل CPU است

  • کد برنامه را بهینه کنید: کش کردن نتایج کوئری‌ها، استفاده از opcache برای PHP، و ایندکس‌گذاری صحیح در MySQL.
  • محدودیت منابع را اعمال کنید: با systemd می‌توانید سقف CPU را تعیین کنید. مثلاً برای سرویسی به نام myapp:
[Service]
CPUQuota=50%

این یعنی سرویس حداکثر از نیمی از یک هسته استفاده کند.

اگر مشکل I/O است

  • میزان I/O را کاهش دهید: log rotation را تنظیم کنید، کوئری‌هایی که مرتباً دیسک را می‌خوانند کش کنید.
  • از I/O scheduling استفاده کنید: برای دیسک‌های HDD، scheduler را روی deadline یا mq-deadline بگذارید تا تأخیر کاهش یابد.
  • اگر سرور مجازی (VPS) دارید و دیسک اشتراکی است، بررسی کنید که نرخ I/O تضمین‌شده (IOPS) شما چقدر است. در این موارد، ارتقای پلن یا مهاجرت به دیسک NVMe می‌تواند معجزه کند.

در این زمینه، اگر سرور شما روی زیرساخت ابری میزبانی می‌شود، ServerNet گزینه‌هایی با دیسک NVMe و IOPS اختصاصی ارائه می‌دهد که برای بارهای I/O-intensive مناسب است؛ اما همیشه قبل از ارتقا، مطمئن شوید که مشکل واقعاً از I/O است نه کد برنامه.

جمع‌بندی؛ چک‌لیست نهایی برای تشخیص لود بالای سرور

برای اینکه سریع به نتیجه برسید، این ترتیب را حفظ کنید:

  1. uptime را اجرا کنید و load average را با تعداد هسته‌ها مقایسه کنید.
  2. top را باز کنید و به ستون‌های %CPU، wa و STATE دقت کنید.
  3. اگر wa بالاست، با iostat -x اشباع بودن دیسک را تأیید کنید.
  4. فرایند مقصر را با ps یا pidstat -d پیدا کنید.
  5. در صورت نیاز، با strace علت رفتار فرایند را ریشه‌یابی کنید.
  6. راه‌حل متناسب با سناریو (CPU یا I/O) را اعمال کنید و دوباره لود را زیر نظر بگیرید.

تشخیص لود بالای سرور یک مهارت تدریجی است. هر بار که این چک‌لیست را اجرا کنید، سریع‌تر می‌شوید و کمتر به حدس‌وزن متکی خواهید بود. نکته آخر: همیشه قبل از هر اقدامی، از تنظیمات فعلی سرور backup بگیرید و تغییرات را یک‌به‌یک اعمال کنید تا اگر مشکلی پیش آمد، بتوانید برگردید.

آیا این مطلب برایتان مفید بود؟