خلاصة سريعة. يُبلّغ
rdc machine query --storage-healthفي Rediacc عن رقم تجزئة لكل مستودع. على أحد أجهزة الإنتاج، يُبلّغ عن GitLab بنحو 19,650 امتداداً لكل جيجابايت. الغريزة تقول: قم بإزالة التجزئة. أنا قِستُ بدلاً من ذلك.
- مستودع مجزَّأ 16 مرة أكثر من جاره قرأ بسرعة 149 MB/s مقابل 143 MB/s تسلسلياً، وكان أسرع في القراءات العشوائية لـ 4K (719 مقابل 957 ميكروثانية).
- الجهاز يعتمد على ذاكرة فلاش. التجزئة تؤثر على الأقراص الدوّارة بسبب وقت تحريك الرأس. على محرك SSD لا توجد آلية تقريباً تجعلها ضارة.
- تشغيل
btrfs filesystem defragmentهنا سيلغي مشاركة نحو 250 GB من التفريعات والقطع الأثرية المرتبطة بـ reflink في تجمّع يبلغ فيه الفراغ الحر 4.4 GB فقط. هذا هو الخطر الحقيقي، والاختبار يقول إنه لا فائدة توازنه.
يوجد تقرير صحة التخزين للإجابة على سؤال واحد: أين تذهب مساحة القرص لديّ. يُظهر حجم كل مستودع، ومقدار البيانات التي يشاركها مع تفريعاته، ورقم تجزئة. هذا الرقم الأخير قد يبدو مرعباً. على الجهاز الذي أديره، يُبلّغ مستودع GitLab عن 268,771 امتداداً عبر 14.6 GB. وهذا يعادل نحو 19,650 امتداداً لكل جيجابايت، والأداة تصنّفه “مرتفعاً”.
الردّ الفعلي الذي يتبع ذلك تلقائي: تجزئة مرتفعة، إذن أزل التجزئة. كتبتُ هذا الردّ في سكريبتات shell على أقراص دوّارة لخمسة عشر عاماً. قبل أن أضيف زر defrag إلى Rediacc، أردتُ أن أعرف ما الذي يكلّفه هذا الرقم فعلاً على الأجهزة التي نعمل عليها. فأجريتُ الاختبار على الجهاز المباشر.
ما الذي يحسبه الرقم فعلاً
مستودع Rediacc هو ملف صورة LUKS واحد يعيش على تجمّع btrfs. يأتي رقم التجزئة من تشغيل filefrag على ملف الصورة هذا. يحسب امتدادات الحاوية المشفّرة، لا الملفات التي يقرأها تطبيقك من داخلها.
هذا مهم بسبب طريقة تكديس البيانات. من الأسفل إلى الأعلى: محرك SSD فيزيائي، ثم نظام الملفات ext4 الجذري للمضيف، ثم ملف تجمّع مدعوم بـ loop، ثم loop0، ثم تجمّع btrfs، ثم صورة LUKS، ثم جهاز device-mapper crypt، ثم ext4 الداخلي الذي تراه حاوياتك. btrfs يعمل بنظام copy-on-write. كل كتابة عشوائية داخل مستودع تكتب امتداداً جديداً في الصورة. قواعد البيانات وطبقات overlay للحاويات تكتب عشوائياً طوال اليوم، لذا تتراكم الامتدادات في الصورة بحكم التصميم.
الملفات داخل الحجم قصة مختلفة. فحصتُ مستودع GitLab: ملف gitaly الثنائي في 10 امتدادات، وملف git pack في 17. نظام الملفات الداخلي غير مجزّأ. رقم 19,650 لكل جيجابايت يصف حاوية copy-on-write، وهو بالضبط ما تتوقع أن تبدو عليه، ولا يخبرك بشيء عن سرعة القراءة.
الاختبار
اخترتُ مستودعَين في طرفَي سلّم التجزئة وقرأتُ منهما بـ direct IO، الذي يتجاوز ذاكرة التخزين المؤقتة ويفرض قراءة فيزيائية.
| المستودع | متوسط الامتداد | الامتدادات لكل GB | القراءة التسلسلية |
|---|---|---|---|
| GitLab | 54 KB | ~19,650 | 149 MB/s |
| عرض Stack Overflow | 880 KB | ~1,190 | 143 MB/s |
فارق 16x في التجزئة لم يُنتج أي خسارة في الإنتاجية. الملف الأكثر تجزئة كان أسرع هامشياً. ثم النمط الذي يقلق الناس فعلاً، القراءات العشوائية الصغيرة، شكل حركة قواعد البيانات:
| المستودع | زمن استجابة 4K عشوائي | IOPS |
|---|---|---|
| GitLab (مجزَّأ) | 719 us | 1,390 |
| عرض Stack Overflow (أقل تجزئة) | 957 us | 1,045 |
الملف الأكثر تجزئة مرة أخرى أسرع. الفارق الصغير يتتبع حجم الملف والتخزين المؤقت للخادم الخلفي، لا تخطيط الامتدادات. على الفلاش، القراءة العشوائية هي بحث واحد وقراءة واحدة بغض النظر عن موقع الامتدادات المحيطة. لا يوجد رأس يتحرك.
الإنتاجية متواضعة بالأرقام المطلقة، نحو 145 MB/s و1,000 IOPS، لأن الجهاز قرص افتراضي على مضيف مشترك ومسار البيانات عميق. هذا السقف تحدده طبقات المحاكاة الافتراضية والتشفير، فوق btrfs وتحته. إزالة تجزئة الصورة لا يمكنها رفعه.
الشيء الوحيد الذي تكلّفه التجزئة
تستلزم الأمانة ذكر الجانب الآخر. للتجزئة تكلفة واحدة قابلة للقياس هنا، وليست سرعة القراءة. إنها الوقت اللازم لتعداد خريطة الامتدادات:
filefragعلى GitLab (268,771 امتداداً): 3.19 sfilefragعلى عرض Stack Overflow (152,364 امتداداً): 0.74 s
العمليات التي تمرّ على كل امتداد تدفع هذا الثمن. يشمل ذلك فحص صحة التخزين نفسه، ومزامنة النسخ الاحتياطية، وأدوات الدلتا. إنها ثوانٍ تتناسب تقريباً مع عدد الامتدادات، وتمسّ المهام الخلفية لا تطبيقك. إن أصبح وقت تعداد الامتدادات عائقاً حقيقياً يوماً ما، فهذه مشكلة محددة بحلول محددة. إنها ليست سبباً لإعادة كتابة البيانات الحية.
لماذا لا يوفر Rediacc أمر defragment
لم يحافظ btrfs filesystem defragment على reflinks منذ نواة 3.9 تقريباً. صفحة الدليل تقول ذلك صراحة: إزالة التجزئة تكسر روابط reflink لبيانات copy-on-write ويمكن أن تسبب زيادة ملحوظة في استخدام المساحة. إعادة كتابة ملف بشكل متجاور تنسخ كل امتداد مشترك إلى امتداد خاص.
على هذا الجهاز تقريباً كل شيء مشترك. التفريعات تشارك بيانات الأصل عبر reflinks، ومؤقت النسخ الاحتياطية يضيف قطع أثرية للقراءة فقط تشارك أيضاً. التجمّع ممتلئ بنسبة 99% مع 4.4 GB فراغاً. GitLab مشترك بنسبة 97%، إذن إزالة تجزئته ستحاول نسخ نحو 14 GB إلى 4.4 GB وستفشل في منتصف الطريق. عرض Stack Overflow 137 GB مع 26 MB من البيانات الفريدة، إذن إزالة تجزئته ستحاول تجسيد 137 GB غير موجودة فيزيائياً. عبر جميع المستودعات نحو 250 GB مرتبطة بـ reflink. مسار defrag هو قنبلة مساحة، لا ضبطٌ للأداء.
حتى لو ناسب، لن يدوم. هذه الصور تُعيد التجزئة خلال دقائق تحت نفس حمل الكتابة العشوائي. ستلغي مشاركة تفريعاتك، مؤقتاً، مقابل سرعة قراءة الاختبار يقول إنك تملكها بالفعل.
ما يستحق القراءة بدلاً من التجزئة
العمود الجدير بانتباهك في نفس التقرير هو Divergence (الاختلاف). إنه النسبة المئوية من صورة المستودع التي تخصّه وحده بدلاً من مشاركتها مع التفريعات والقطع الأثرية. التفريع الجديد يقع قرب 0%، لأنه يشارك تقريباً كل شيء. المستودع الذي كُتب بكثافة منذ تفريعه يتسلق نحو 100%.
Divergence يجيب على السؤال الذي لا تستطيع التجزئة الإجابة عليه: كم من القرص الحقيقي القابل للاسترداد يستهلك هذا المستودع. حين يكون التجمّع ضيقاً، المستودع ذو الاختلاف المنخفض هدف تنظيف ضعيف، لأن بياناته مشتركة وحذفه يُحرّر القليل. البيانات تعيش حيث يكون الاختلاف مرتفعاً.
الخلاصة
رقم التجزئة حقيقي، وعلى صورة copy-on-write تحت كتابات عشوائية سيبدو دائماً مرتفعاً. على الفلاش إنه معلوماتي. قِستُ فارقاً 16x ولم أجد أي خسارة في القراءة، وملفاً أكثر تجزئة بنمط عشوائي أسرع، وتكلفة واحدة صغيرة في وقت الفحص الخلفي. الأداة التي ستُصلح الرقم ستلغي مشاركة ربع تيرابايت من التفريعات في تجمّع لا مكان فيه لها.
لذا يُبلّغ Rediacc عن التجزئة ويشرحها، ولا يوفر زراً للتصرف بناء عليها. الإجابة الهندسية الأمينة كانت قياس الافتراض بدلاً من أتمتة الردّ الفعلي.