دسته بندی:

تست سرعت هارد دیسک و SSD در لینوکس با fio، dd و hdparm

تست سرعت هارد دیسک و SSD در لینوکس با fio، dd و hdparm

حدود ۱۲ دقیقه مطالعه

برای سنجش واقعی سرعت دیسک، خروجی یک دستور ساده مثل dd کافی نیست؛ باید سه چیز را جداگانه اندازه بگیرید: تعداد عملیات در ثانیه (IOPS)، توان عبوری (Throughput) و تاخیر پاسخ (Latency). ابزار استاندارد این کار fio است و نتیجه هر تست فقط به همان الگویی که تعریف کرده‌اید مربوط می‌شود.

در ادامه روش درست تست دیسک روی لینوکس را می‌بینید؛ روشی که هم روی سرور فیزیکی جواب می‌دهد و هم روی سرور مجازی. سراغ dd و hdparm هم می‌رویم، چون هر کدام کاربرد محدود اما درستی دارند و در بیشتر آموزش‌ها اشتباه به کار می‌روند.

چرا تست قدیمی با dd گمراه‌کننده است

سال‌ها این حلقه معروف دست‌به‌دست می‌شد: هزار بار خواندن یک بلوک ۴ کیلوبایتی از /dev/sda با dd و اندازه‌گیری زمان کل با time؛ ادعا این بود که اگر چند ثانیه طول بکشد یعنی SSD و اگر بیشتر شود یعنی دیسک مکانیکی؛ منظور از دیسک مکانیکی همان هارد دیسک HDD و ساختار چرخان آن است. این معیار امروز دیگر معتبر نیست، به دلایل مشخص:

  • در آن حلقه به ازای هر تکرار یک فرایند تازه dd ساخته می‌شود؛ بخش بزرگی از زمانی که اندازه می‌گیرید هزینه ساخت فرایند است، نه کار دیسک.
  • بدون iflag=direct پاسخ‌ها از حافظه نهان صفحه (Page Cache) هسته برمی‌گردد و شما در عمل سرعت رم را می‌سنجید، نه دیسک را.
  • عمق صف همیشه یک است. SSD و به‌ویژه NVMe وقتی توان واقعی‌شان را نشان می‌دهند که درخواست‌های هم‌زمان در صف باشد.
  • متغیر $RANDOM در Bash بازه محدودی دارد، پس آدرس‌هایی که می‌خوانید آن‌قدرها هم تصادفی و پراکنده نیستند.
  • دسته‌بندی امروزی فقط «SSD هست یا نیست» نیست؛ میان یک SSD معمولی SATA و یک دیسک NVMe هم تفاوت جدی وجود دارد و پاسخ «بله SSD است» چیزی درباره آن نمی‌گوید.

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

پس به‌جای دنبال کردن یک عدد، روش را عوض کنید. اگر هم به‌دنبال سخت‌افزاری هستید که لایه اشتراکی در میان نباشد، نوع دیسک یکی از معیارهای اصلی هنگام انتخاب سرویس سرور اختصاصی است و باید کنار نسل پردازنده و حجم رم دیده شود.

سه عددی که باید جدا اندازه بگیرید

IOPS

تعداد عملیات ورودی/خروجی در ثانیه، معمولا با بلوک کوچک (۴ کیلوبایت) و الگوی تصادفی. این عدد سرنوشت دیتابیس، میل سرور و سایت پویا را تعیین می‌کند.

Throughput

حجم داده جابه‌جاشده در ثانیه، معمولا با بلوک بزرگ (۱ مگابایت) و الگوی ترتیبی. این عدد برای بکاپ‌گیری، انتقال فایل و سرور دانلود اهمیت دارد.

Latency

زمان پاسخ هر عملیات. میانگین آن تقریبا بی‌فایده است؛ چیزی که کاربر حس می‌کند صدک‌های بالا مثل ۹۵ و ۹۹ است. یک دیسک می‌تواند میانگین خوبی داشته باشد ولی هر از گاهی وقفه‌های محسوس بسازد.

رابطه این سه ساده است: IOPS ضرب در اندازه بلوک تقریبا همان Throughput می‌شود. یعنی نمی‌توانید هر سه را هم‌زمان بیشینه کنید و هر تست فقط یک گوشه از توان دیسک را نشان می‌دهد.

چه چیزهایی سرعت دیسک را تعیین می‌کند

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

  • نوع رسانه. در دیسک مکانیکی، سرعت چرخش (RPM) و زمان جست‌وجوی هد، تاخیر دسترسی را می‌سازد؛ به همین دلیل IOPS تصادفی HDD در حد چند ده تا چند صد باقی می‌ماند هرچند خواندن ترتیبی‌اش قابل قبول باشد. SSD قطعه متحرک ندارد، پس تاخیر دسترسی تصادفی‌اش مرتبه‌ها کمتر است.
  • رابط اتصال. یک SSD روی SATA به سقف رابط SATA III یعنی ۶ گیگابیت بر ثانیه محدود است و عملا از حدود ۵۵۰ مگابایت بر ثانیه بالاتر نمی‌رود. دیسک NVMe مستقیم روی PCIe می‌نشیند و این سقف را ندارد؛ ضمن اینکه صف‌های عمیق‌تری پشتیبانی می‌کند و همین بیشترین تفاوت را در بار هم‌زمان می‌سازد.
  • کنترلر و حافظه نهان خود دیسک. بسیاری از SSDها بخشی از حافظه را به‌صورت کش سریع به کار می‌گیرند؛ تا وقتی این کش پر نشده عدد نوشتن بالاست و بعد از آن افت می‌کند. برای همین تست کوتاه چند ثانیه‌ای عدد خوش‌بینانه می‌دهد و تست ۶۰ ثانیه‌ای واقعیت را نشان می‌دهد.
  • میزان فضای خالی. روی SSD وقتی دیسک تقریبا پر شود، عملکرد نوشتن پایین می‌آید. فعال بودن TRIM هم در طول عمر عملکرد اثر دارد.
  • لایه RAID و کنترلر آن. روی سرور فیزیکی، سطح RAID و حالت کش کنترلر (Write-Back در برابر Write-Through) گاهی بیش از خود دیسک روی تاخیر نوشتن اثر می‌گذارد.
  • لایه مجازی‌سازی. روی سرور مجازی، سقف IOPS تعریف‌شده و بار همسایه‌ها هم بخشی از عدد نهایی شماست.

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

تست سریع با dd؛ شکل درستش

اگر فقط یک عدد تقریبی از خواندن و نوشتن ترتیبی می‌خواهید، dd کافی است. سه شرط دارد: فایل تست را جایی بسازید که واقعا روی دیسک باشد، ورودی/خروجی مستقیم را فعال کنید، و بلوک‌های متعدد بنویسید نه یک بلوک غول‌پیکر.

تست نوشتن ترتیبی:

dd if=/dev/zero of=/var/tmp/ddtest bs=1M count=1024 oflag=direct

اگر فایل‌سیستم شما oflag=direct را نپذیرفت (روی برخی فایل‌سیستم‌ها و اشتراک‌های شبکه رخ می‌دهد)، به‌جای آن از conv=fdatasync استفاده کنید تا دست‌کم پیش از پایان اندازه‌گیری، داده روی دیسک نشسته باشد:

dd if=/dev/zero of=/var/tmp/ddtest bs=1M count=1024 conv=fdatasync

پیش از تست خواندن، حافظه نهان را خالی کنید تا فایل تازه‌نوشته‌شده از رم پاسخ ندهد. اجرای sync پیش از آن لازم است، چون drop_caches صفحه‌های کثیف (داده‌ای که هنوز روی دیسک ننشسته) را آزاد نمی‌کند؛ با sync آن داده‌ها نوشته می‌شوند و بعد قابل دور انداختن‌اند. خود drop_caches عملیاتی غیرمخرب است و داده‌ای را از بین نمی‌برد:

sync && echo 3 > /proc/sys/vm/drop_caches

این دستور باید با کاربر root اجرا شود. اگر با کاربر معمولی و sudo کار می‌کنید، تغییر مسیر خروجی پیش از بالا رفتن دسترسی انجام می‌شود و دستور با خطای Permission denied رد می‌شود؛ شکل درستش این است:

sudo sh -c 'sync && echo 3 > /proc/sys/vm/drop_caches'

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

و حالا خواندن:

dd if=/var/tmp/ddtest of=/dev/null bs=1M count=1024 iflag=direct

سه اشتباه رایج در نسخه‌هایی که در وب می‌بینید: نوشتن فایل تست در /tmp که روی خیلی از توزیع‌ها یک فایل‌سیستم موقت در رم (tmpfs) است و عدد رم به شما می‌دهد؛ استفاده از bs=1G count=1 که یعنی فقط یک عملیات نوشتن و نتیجه‌ای غیرقابل اتکا؛ و افزودن iflag=dsync به دستور خواندن، که dd آن را می‌پذیرد ولی روی خواندن هیچ اثری ندارد و کش را دور نمی‌زند؛ برای دور زدن کش خواندن باید iflag=direct را به کار برد. در پایان هم فایل را پاک کنید: rm -f /var/tmp/ddtest

حتی با این اصلاح‌ها، dd فقط الگوی ترتیبی و تک‌رشته‌ای را می‌سنجد و درباره IOPS تصادفی چیزی نمی‌گوید.

hdparm؛ فقط خواندن، فقط ترتیبی

hdparm ابزار قدیمی و سبکی است که یک عدد خواندن ترتیبی می‌دهد. نصب آن (دستورهای نصب در این مقاله با فرض کاربر root نوشته شده‌اند؛ اگر با کاربر معمولی کار می‌کنید sudo را ابتدای آن‌ها بگذارید):

apt update && apt install -y hdparm

روی AlmaLinux، Rocky Linux و RHEL:

dnf install -y hdparm

اجرای تست (به‌جای sda نام دیسک خودتان را بگذارید). این دستور به دسترسی root نیاز دارد؛ با کاربر معمولی خطای Permission denied می‌گیرید:

sudo hdparm -tT /dev/sda

نام دیسک همیشه sdX نیست: دیسک NVMe با نام /dev/nvme0n1 و دیسک مجازی virtio با نام /dev/vda دیده می‌شود. فهرست دیسک‌ها را با این دستور بگیرید:

lsblk -d -o NAME,ROTA,SIZE,MODEL

در خروجی hdparm دو عدد می‌بینید و معنایشان یکسان نیست. مقدار cached reads که با سوییچ -T گرفته می‌شود از حافظه نهان هسته می‌آید و عملا پهنای باند رم را نشان می‌دهد، نه دیسک را؛ به آن استناد نکنید. مقدار buffered disk reads که با -t گرفته می‌شود واقعا از دیسک خوانده می‌شود، اما فقط ترتیبی و با عمق صف یک. پس hdparm نه IOPS به شما می‌دهد، نه چیزی درباره نوشتن، و روی دیسک مجازی و NVMe هم عدد نماینده‌ای نیست. برای همه این‌ها باید سراغ fio بروید.

یک هشدار: خود hdparm -tT فقط خواندنی و بی‌خطر است، اما hdparm سوییچ‌های مخربی مثل پاک‌سازی امن و تغییر پارامترهای دیسک هم دارد. دستور را کامل و دقیق تایپ کنید.

نصب fio

روی دبیان و اوبونتو:

apt update && apt install -y fio

روی AlmaLinux، Rocky Linux و RHEL:

dnf install -y fio

اگر بسته پیدا نشد، اول مخزن EPEL را فعال کنید با dnf install -y epel-release. اگر در آموزشی apt-get install دیدید نگران نباشید؛ هنوز کار می‌کند، اما فرمان توصیه‌شده امروز روی دبیان و اوبونتو همان apt است.

اگر هنوز روی CentOS 7 یا CentOS 8 هستید، هر دو به پایان چرخه پشتیبانی رسیده‌اند (CentOS 8 در پایان ۲۰۲۱ و CentOS 7 در میانه ۲۰۲۴)، دیگر وصله امنیتی نمی‌گیرند و مخازنشان جابه‌جا شده است. همین تست را بهانه کنید و مهاجرت به AlmaLinux یا Rocky Linux را در برنامه بگذارید.

یک هشدار پیش از اجرا

هرگز تست نوشتن را روی دستگاه خام اجرا نکنید. اگر مقدار --filename را روی چیزی مثل /dev/sda بگذارید و الگو از نوع نوشتن باشد، fio بدون پرسش روی پارتیشن‌ها می‌نویسد و داده‌ها از بین می‌رود. همیشه یک فایل موقت داخل فایل‌سیستم بسازید، مثل /var/tmp/fiotest.

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

تست‌های پایه با fio

خواندن تصادفی با عمق صف بالا:

fio --name=randread --filename=/var/tmp/fiotest --size=4G --bs=4k --rw=randread --ioengine=libaio --direct=1 --iodepth=32 --numjobs=1 --runtime=60 --time_based --group_reporting

نوشتن تصادفی: همان دستور با --rw=randwrite و نامی متفاوت در --name.

خواندن و نوشتن ترتیبی: همان دستور با --bs=1M --iodepth=8 و به‌ترتیب --rw=read یا --rw=write.

سنجش تاخیر خالص: همان دستور با --bs=4k --rw=randread --iodepth=1. عمق صف یک، کف تاخیر دیسک را نشان می‌دهد.

در پایان فایل موقت را پاک کنید: rm -f /var/tmp/fiotest

دو نکته حیاتی: بدون --direct=1 نتیجه شما سرعت حافظه نهان است نه دیسک؛ و مقدار --size باید به‌قدر کافی بزرگ باشد که کش‌ها نتوانند کل داده را نگه دارند. اگر موتور libaio در دسترس نبود، از --ioengine=psync استفاده کنید و بدانید در آن حالت عمق صف موثر پایین می‌ماند. روی هسته‌های جدید لینوکس موتور --ioengine=io_uring هم در دسترس است؛ فقط برای مقایسه، همه اجراها را با یک موتور بگیرید.

اجرای fio با فایل پیکربندی

وقتی می‌خواهید همین تست‌ها را مرتب و یکسان تکرار کنید، به‌جای دستور طولانی یک فایل پیکربندی بنویسید. محتوای زیر را در فایلی به نام fio_test.fio ذخیره کنید:

[global]
ioengine=libaio
direct=1
filename=/var/tmp/fiotest
size=4G
runtime=60
time_based
group_reporting

[randread-4k]
rw=randread
bs=4k
iodepth=32

[seqread-1m]
stonewall
rw=read
bs=1M
iodepth=8

و اجرا:

fio fio_test.fio

چند نکته درباره همین فایل که در نمونه‌های رایج غلط نوشته می‌شود. نام موتور libaio است نه libai؛ با املای غلط، fio با خطای موتور ناشناخته متوقف می‌شود. خط direct=1 اگر نباشد همه اعداد از حافظه نهان می‌آید. مقدار size باید از حجم رم سرور بزرگ‌تر باشد، پس size=1g روی سروری با ۸ گیگابایت رم بی‌معناست. الگوی rw=readwrite خواندن و نوشتن را با هم قاطی می‌کند و در خروجی نمی‌فهمید کدام عدد مال کدام است؛ بهتر است هر الگو را در بخش جداگانه بگذارید. کلمه stonewall باعث می‌شود بخش دوم بعد از پایان بخش اول اجرا شود، نه هم‌زمان با آن. و در آخر، filename را با مسیر کامل بنویسید تا فایل تست جای غیرمنتظره‌ای ساخته نشود.

خروجی fio را چطور بخوانیم

در گزارش پایانی دنبال سه بخش بگردید: مقدار IOPS=، مقدار BW= برای توان عبوری، و جدول clat percentiles برای تاخیر. در جدول درصدها، ردیف‌های 95.00th و 99.00th را ببینید نه فقط avg را. اگر فاصله میانگین با صدک ۹۹ زیاد باشد، دیسک ناپایدار است حتی اگر عدد IOPS خوب به نظر برسد.

روی سرور مجازی نتیجه ثابت نیست

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

پس هر تست را دست‌کم سه بار و در ساعت‌های مختلف شبانه‌روز تکرار کنید و به بازه نتایج نگاه کنید، نه به بهترین اجرا. اگر پروژه‌تان به ورودی/خروجی تصادفی سنگین وابسته است، سرویسی مثل سرور ابری آلمان که روی دیسک NVMe ارائه می‌شود نقطه شروع بهتری است.

نکته دیگر اینکه ستون ROTA در خروجی lsblk (همان فایل /sys/block/sda/queue/rotational) روی سرور مجازی قابل اتکا نیست؛ دیسک مجازی معمولا صفر گزارش می‌کند، حتی وقتی پشت آن آرایه‌ای از دیسک مکانیکی باشد. تنها راه فهمیدن رفتار واقعی، همان تست بار است.

نتیجه را برای کدام بار تفسیر می‌کنید

عدد خوب و بد مطلق وجود ندارد؛ همه چیز به الگوی کاری شما بستگی دارد:

  • دیتابیس: InnoDB در MySQL با صفحه ۱۶ کیلوبایتی و PostgreSQL با صفحه ۸ کیلوبایتی کار می‌کند و هر دو برای تایید تراکنش به fsync متکی هستند. پس تست معنادار برای شما نوشتن تصادفی با بلوک کوچک و تاخیر پایین است، نه مگابایت بر ثانیه. برای شبیه‌سازی نزدیک‌تر، گزینه --fsync=1 را به دستور نوشتن اضافه کنید و ببینید IOPS چقدر افت می‌کند.
  • فایل‌سرور و سرور دانلود: اینجا خواندن ترتیبی و پهنای باند شبکه تعیین‌کننده است. اگر Throughput دیسک از ظرفیت پورت شبکه بیشتر است، ارتقای دیسک هیچ تغییری در تجربه کاربر ایجاد نمی‌کند.
  • وب‌سرور معمولی: بیشتر خواندن‌ها از کش رم پاسخ می‌گیرد؛ تاخیر نوشتن لاگ و کش مهم‌تر از IOPS اوج است.

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

اشتباه‌های رایج

  • جا انداختن --direct=1 و گزارش سرعت رم به‌جای دیسک.
  • انتخاب --size کوچک‌تر از حجم رم سرور.
  • ساختن فایل تست در /tmp که ممکن است tmpfs و در رم باشد.
  • استناد به عدد cached reads در خروجی hdparm به‌عنوان سرعت دیسک.
  • یک بار اجرا کردن تست و تصمیم گرفتن بر اساس همان یک عدد.
  • اجرای تست سنگین روی سرور فعال؛ خود fio سرویس‌های در حال اجرا را کند می‌کند.
  • مقایسه خروجی fio با عددی که ابزار دیگری گرفته است. مقایسه فقط با پارامترهای یکسان معنا دارد.
  • تست روی دیسکی که تقریبا پر شده؛ فضای خالی کم روی SSD عملکرد نوشتن را پایین می‌آورد.

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

آموزش‌های مرتبط