PRODمنصة BaaS الأوروبية السياديةافتح لوحة المعلومات →

الأداء · 10 دقيقة للقراءة

لماذا لا يغير جامع البيانات المهملة زمن الوصول p99

Affane Daylami · Fondateur · 2 يوليو 2026

العودة إلى بلوق

يقيس p99 أبطأ طلب من بين مائة - الطلب الذي يكسر اتفاقية مستوى الخدمة (SLA) الخاصة بك بينما يظل المتوسط مثاليًا. في الواجهة الخلفية ذات حركة المرور العالية، غالبًا ما يكون لهذه المجموعة من الطلبات البطيئة سبب واحد: جامع البيانات المهملة، الذي يقاطع البرنامج بأكمله لتحرير الذاكرة. الصدأ ليس لديه جامع القمامة. ها هي الآلية، مع مصادر مؤرخة تابعة لجهات خارجية بدلاً من شخصية تسويقية.

تم إنشاء هذا النص الإنجليزي تلقائيًا من النص الأصلي الفرنسي ولم تتم مراجعته بعد.
تمت ترجمة هذه الصفحة تلقائيًا. النسخة الإنجليزية موثوقة.

تعتمد هذه المقالة فقط على مصادر مؤرخة تابعة لجهات خارجية - ولا تعتمد أبدًا على تشفير Aurabase المخترع. لم يتم تضمين زمن الوصول p99 الخاص بالواجهة الخلفية لدينا. نكتب نواة تطبيقنا بلغة Rust، بدون أداة تجميع البيانات المهملة: وهي حقيقة يمكن التحقق منها مباشرة في الكود ومساحة العمل Cargo وخدمات axum. ومع ذلك، لم ننشر بعد منهجية مرجعية p99 قابلة للتكرار لإثبات ذلك بالأرقام. يشرح هذا النص آلية، وليس نتيجة مقاسة.

الأساسيات

  • يقيس p99 أبطأ استعلام من بين مائة - المكان الذي يكون فيه توقف أداة تجميع البيانات المهملة (GC) هو الأكثر ضررًا، وليس في المتوسط (Dean & Barroso، "The Tail at Scale،" Google، 2013).
  • يقوم GC بمقاطعة البرنامج بأكمله ("إيقاف العالم") لتحرير الذاكرة غير المستخدمة. لا يحتوي الصدأ على GC: يتم تحرير الذاكرة في اللحظة المحددة عندما تخرج القيمة عن النطاق، ويتم التحقق منها بواسطة المترجم.
  • قام Discord بتوثيق خدمة ذاكرة التخزين المؤقت في عام 2020 حيث قام Go بتشغيل دورة جمع البيانات المهملة كل دقيقتين على الأقل، حيث تسببت كل دورة في ارتفاع كبير في زمن الوصول (مدونة Discord Engineering).
  • يستغرق تقليل الإيقاف المؤقت لـ GC سنوات من الهندسة، حتى في Google: فقد انتقل مُجمِّع GB من 300-400 مللي ثانية إلى 500 ميكروثانية بين عامي 2015 و2018، دون أن يصل إلى الصفر على الإطلاق (go.dev).
  • تمت كتابة جوهر الواجهة الخلفية لـ Aurabase بلغة Rust، بدون أداة تجميع البيانات المهملة - وتم التحقق منها في الكود. لم يتم نشر أي أرقام زمن وصول Aurabase p99 حتى الآن: تظل هذه آلية وليست قياسًا.
#
الكمون الذيل

لماذا p99 ليس متوسطًا؟

المتوسط يخفي الأساسيات. إذا تم الرد على 99 طلبًا من أصل 100 خلال 5 مللي ثانية واستغرق طلب واحد فقط 500 مللي ثانية، فسيظل المتوسط ​​منخفضًا. لكن مستخدمًا واحدًا من كل مائة يواجه الانتظار لفترة أطول مائة مرة. يقيس p99 هذا الطلب بالضبط: أبطأ جزء من المائة، وهو الطلب الذي ينتهك اتفاقية مستوى الخدمة الخاصة بك بينما تظل لوحة التحكم الخاصة بمتوسط ​​زمن الاستجابة باللون الأخضر.

في جوجل، قام جيفري دين ولويز أندريه باروسو بإضفاء الطابع الرسمي على هذه المشكلة في "The Tail at Scale" (Communications of the ACM، المجلد 56، 2013). ملاحظتهم، والتي غالبًا ما يتم الاستشهاد بها منذ ذلك الحين: "قد تهيمن حلقات الكمون العالي المؤقتة التي لا تعد مهمة في الأنظمة متوسطة الحجم على الأداء العام للخدمة على نطاق واسع". باختصار: حلقات عرضية من الكمون، لا تكاد تذكر على نطاق صغير، تنتهي في نهاية المطاف بالسيطرة على الأداء المتصور للنظام الموزع.

الواجهة الخلفية التي تعالج آلاف الطلبات في الثانية لا بد أن ترسل، عند نقطة أو أخرى، طلبًا يقع أثناء توقف GC مؤقتًا. على نطاق واسع، هذه ليست حالة غير شائعة. وهذا يقين إحصائي.

#
ميكانيكا جي سي

ما الذي يفعله جامع القمامة، ولماذا يوقف كل شيء مؤقتًا؟

يقوم جامع البيانات المهملة (GC) بتتبع الكائنات الحية للبرنامج بشكل مستمر - تلك التي لا يزال يتم الرجوع إليها في مكان ما - ويحرر ذاكرة الكائنات التي أصبح يتعذر الوصول إليها. يسمى هذا التتبع tracing: يمر GC عبر الرسم البياني المرجعي، ويحدد ما لا يزال مستخدمًا، ثم يمسح الباقي.

المشكلة: يؤدي اجتياز هذا الرسم البياني أثناء استمرار البرنامج في إنشاء مراجع جديدة إلى نتائج غير متناسقة. الإجابة التاريخية، التي لا تزال تستخدم كملاذ أخير من قبل العديد من GCs الحديثة، هي stop-the-world - يتوقف البرنامج بأكمله مؤقتًا أثناء وضع العلامات والمسح الضوئي. كلما كانت الكومة أكبر، كلما كانت فترة التوقف المؤقت أطول: تعتمد مدتها على حجم البيانات المباشرة، وليس على عبء العمل الحالي.

تستخدم معظم GCs الحديثة استراتيجية الأجيال: فهي تفترض أن غالبية الأشياء تموت في سن مبكرة. ولذلك يتم فحص التخصيصات الأخيرة كثيرًا، ولكن بسرعة، في منطقة ذاكرة صغيرة. تنتقل الكائنات التي تنجو من عدة دورات إلى منطقة أكبر، ويتم فحصها بشكل غير متكرر - ولكن عندما تحتاج هذه المنطقة إلى التنظيف، فإن فترة الإيقاف المؤقت المرتبطة بها تنمو مع حجمها. إن هذا الفاصل "الرئيسي"، وليس الفواصل "الثانوية" الصغيرة، هو الذي يهيمن على p99 لخدمة ذات حركة مرور عالية وتخصيص مرتفع.

معلومات

تعمل وحدات التحكم العامة المتزامنة والأجيال الحديثة على تقليل تكرار ومدة فترات الراحة هذه من خلال العمل بالتوازي مع البرنامج. لكن جميعها تقريبًا تحتفظ بآلية احتياطية لإيقاف العالم في الحالات الحدودية - ويستغرق تقليلها سنوات من الهندسة. يقدم القسم 04 مثالاً كميًا ومصادرًا.

#
حالة حقيقية

Discord، 2020: يصبح انقطاع GC حادثًا إنتاجيًا

في فبراير 2020، نشر المهندس جيسي هوارث منشورًا أصبح مرجعًا في الصناعة: "لماذا يتحول Discord من Go إلى Rust" (مدونة Discord Engineering). تدير الخدمة ذات الصلة، حالات القراءة، حالة قراءة الرسائل لملايين المستخدمين - عشرات الملايين من الإدخالات لكل ذاكرة تخزين مؤقت - مع مئات الآلاف من التحديثات في الثانية.

التشخيص مباشر، كما هو مذكور في المقالة: "سيفرض Go عملية جمع البيانات المهملة كل دقيقتين على الأقل". بمعنى آخر، يقوم Go بتشغيل دورة جمع البيانات المهملة كل دقيقتين على الأقل على هذه الخدمة - وتنتج كل دورة ارتفاعًا كبيرًا في زمن الوصول مرئيًا في الرسوم البيانية للفريق.

قام الفريق أولاً بتقليل حجم ذاكرة التخزين المؤقت لتنعيم المسامير. ظلت التسوية غير مواتية: توقفات أقل لـ GC مؤقتًا، ولكن المزيد من طلبات ذاكرة التخزين المؤقت المفقودة التي تقع على قاعدة البيانات - وبالتالي انخفاض إجمالي p99 في مكان آخر. كان الإصلاح الأساسي هو إعادة كتابة الخدمة في Rust، دون الحاجة إلى مراقبة أداة تجميع البيانات المهملة.

أثار المنشور نقاشًا تقنيًا حيويًا: أكثر من 1580 نقطة و642 تعليقًا على Hacker News في نفس يوم نشره (4 فبراير 2020) - في إشارة إلى أن المشكلة تتجاوز بكثير قضية Discord.

#
اذهب إلى التاريخ

ثلاث سنوات من الهندسة في Google لإسقاط التوقف المؤقت من 400 مللي ثانية إلى 500 ميكروثانية

يوضح جامع البيانات المهملة Go حجم الجهد المطلوب لترويض توقف GC مؤقتًا - حتى مع موارد فريق متخصص في Google. قام ريك هدسون، المدير الفني لـ Go GC، بتوثيق هذه القصة في منشورين رسميين على مدونة Go.

قبل أغسطس 2015300-400 مللي ثانيةجامع Go التاريخي، قبل إعادة التصميم
أغسطس 2015 · اذهب 1.530-40 مللي ثانيةأول جامع منافس، الهدف <10 مللي ثانية
2016 · اذهب 1.6< 10 مللي ثانية (SLO مع الاستمرار)تم تحقيق الهدف الأولي في الإنتاج
مارس 2017 · اذهب 1.8تحت ميلي ثانيةإزالة فحص المكدس الخاص بإيقاف العالم
أغسطس 2017 · اذهب 1.9100-200 ميكروثانية (علامة)معيار غير رسمي جديد ذكره الفريق
2018 · الإعلان عن SLO500 ميكرو ثانية لكل دورةهدف الخدمة تم صياغته رسميًا بواسطة ريك هدسون

المصدر: "الذهاب: رحلة جامع القمامة في Go"، go.dev، 12 يوليو 2018؛ و"Go GC: إعطاء الأولوية لزمن الاستجابة المنخفض والبساطة"، go.dev، 31 أغسطس 2015.

لقد أدت ثلاث سنوات من العمل المتفاني إلى خفض الاستراحة النموذجية بعامل الألف. لكن التوقف المؤقت لم يختف أبدًا: إنه هدف خدمة (SLO)، وليس ضمانًا مطلقًا للصفر. يجب أن يجتاز تتبع GC، من خلال البناء، رسمًا بيانيًا للكائنات الحية من وقت لآخر. المتغير الوحيد القابل للتعديل هو تكرار هذه الرحلة ومدتها، وليس وجودها.

وهذا الاختيار للأولوية ليس محايدا. يستهدف Go في المقام الأول خدمات الشبكة والواجهات الخلفية للويب، حيث يؤدي التوقف المؤقت لعدة مئات من المللي ثانية إلى انقطاع تجربة المستخدم مباشرةً - ومن هنا يأتي الجهد الهائل المستثمر في زمن الوصول بدلاً من إنتاجية GC الأولية. وقد ورثت أوقات التشغيل المُدارة الأخرى مقايضات مختلفة، والتي تشكلت من خلال حالات الاستخدام التاريخية الخاصة بها، قبل أن تعوض عن ذلك باستخدام أدوات التجميع ذات الإيقاف المؤقت المنخفض الخاصة بها. تظل النقطة المشتركة كما هي: جميعها تبدأ من تتبع GC، وبالتالي من آلية الإيقاف المؤقت التي سيتم تقليلها إلى الحد الأدنى - ولا يتم إلغاؤها أبدًا عن طريق البناء.

#
ميكانيكا الصدأ

لماذا لا يعاني الصدأ من هذه المشكلة عن طريق البناء

الصدأ لا يقلل من توقف GC مؤقتًا: فهو يلغي الآلية التي تسببها. يتتبع المترجم، عند التجميع، من يملك كل قيمة ذاكرة - وهذا هوownership. عندما يخرج مالك القيمة عن النطاق، يقوم Rust تلقائيًا بإدراج الاستدعاء الذي يحرر تلك الذاكرة، في نفس المكان في الكود الثنائي. تسمى هذه الآلية RAII (تهيئة الحصول على الموارد): الإصدار حتمي، ولم تتم جدولته بواسطة أداة تجميع البيانات المهملة التي تعمل في الخلفية.

يلخص Alexandru Nedelcu، مؤلف مدونة تقنية معترف بها في نظام Scala/Rust البيئي، المقايضة في مقال حديث: "المقايضة التي تقوم بها Rust هي سهولة الاستخدام، وتفضيل الأداء مع زمن استجابة وأمان يمكن التنبؤ بهما" (alexn.org، 21 يوليو 2026). يستبدل الصدأ بعضًا من بساطة الكتابة بزمن انتقال يمكن التنبؤ به.

تلخص المقالة نفسها سبب عدم كفاية GCs الحديثة دائمًا: "تحاول GCs الحديثة القيام بعملها بشكل تدريجي ومتزامن، دون التأثير على البرنامج. لكن قدرتها محدودة، وتعود إلى دورة GC التي توقف العالم والتي تجمد البرنامج بأكمله، وبالتالي تؤثر على زمن الوصول".

إليك الآلية في حوالي عشرة أسطر - مثال عام، وليس مقتطفًا من كود Aurabase:

example.rsrust
struct Connection {
    id: u32,
}

impl Drop for Connection {
    fn drop(&mut self) {
        println!("connexion {} fermée", self.id);
    }
}

fn handle_request() {
    let conn = Connection { id: 42 };
    // ...يعالج الطلب...
} // conn يخرج عن النطاق هنا: يتم تنفيذ drop()
   // في اللحظة المحددة، التي تم فحصها بواسطة المترجم -
   // ليس من خلال دورة جمع القمامة.

فارق بسيط مهم: ليس كل شيء مجاني. تضيف أنواع العد المرجعي (Rc, Arc) تكلفة صغيرة لكل نسخة وإصدار. وتظل هذه التكلفة محلية وحتمية. لا يوجد أبدًا توقف مؤقت يؤدي إلى تجميد البرنامج بأكمله أثناء مروره عبر كومة الذاكرة.

توضيح مفيد للواجهة الخلفية غير المتزامنة: وقت تشغيل Rust async (tokio، الذي تستخدمه جميع خدمات Aurabase) ليس له علاقة بأداة تجميع البيانات المهملة. يقوم بجدولة المهام التعاونية على مجموعة من مؤشرات الترابط، ولكن لا يتكرر أبدًا من خلال رسم بياني للكائن المباشر لتحرير الذاكرة. يعد الارتباك أمرًا شائعًا بسبب الأنظمة البيئية حيث تتم إدارة وقت التشغيل غير المتزامن وGC بواسطة نفس الجهاز الظاهري.

#
الآثار المعمارية

ما الذي يتغير هذا بالنسبة للواجهة الخلفية لحركة المرور العالية

في الواجهة الخلفية التي تخدم آلاف الطلبات المتزامنة، يؤدي غياب GC إلى إزالة متغير من المعادلة p99. لم تعد هناك حاجة إلى تحديد حجم كومة الذاكرة، أو ضبط أجيال المجمع، أو مراقبة دورة يمكن أن تقع في أسوأ وقت. يعتمد زمن استجابة الطلب الفردي على عمله، وليس على بعض الأحداث العالمية التي لا يمكن التنبؤ بها في مكان آخر من البرنامج.

يطبق جوهر الواجهة الخلفية لـ Aurabase هذا المبدأ: جميع الخدمات (aura-gateway, aura-auth, aura-db, aura-realtime, aura-storage, aura-functions, aura-ai...) مكتوبة بلغة Rust، منظمة في مساحة عمل Cargo واحدة . يمكن التحقق من ذلك مباشرة في المستودع:

Cargo.tomltoml
[workspace]
resolver = "2"

members = [
    "services/aura-gateway",
    "services/aura-auth",
    "services/aura-db",
    # ...9 خدمات أخرى + مكتبات مشتركة
]

[workspace.dependencies]
axum = { version = "0.8", features = ["ws", "multipart", "macros"] }

مقتطف فعلي من Cargo.toml، إصدار مساحة العمل 2021، محلل الإصدار v2 - تم التحقق منه في مستودع Aurabase.

ما لا تثبته هذه الحقيقة، في هذه المرحلة: رقم زمن الوصول p99 الذي تم قياسه لـ Aurabase. لم ننشر بعد منهجية مرجعية قابلة للتكرار للواجهة الخلفية الخاصة بنا - وهذا عمل قيد التقدم، وليس نتيجة متاحة اليوم. إن عدم وجود أداة تجميع البيانات المهملة هي آلية تم التحقق منها في الكود. هذا وحده ليس دليلاً على الكمون p99 المقاس. ضع هذا التمييز في الاعتبار في مواجهة أي حجة تسويقية حول هذا الموضوع، بما في ذلك حجةنا - راجع المقارنة الفنية الخاصة بنا Aurabase vs Supabase للحصول على تفاصيل البنية.

يتطلب قياس p99 بشكل صحيح نظامًا خاصًا به: ظروف حمل تمثيلية، ونسب مئوية محسوبة على نافذة منزلقة واسعة بما فيه الكفاية، وبيئة اختبار قريبة من الإنتاج. إن نشر رقم بدون هذه المنهجية يشبه نشر رقم تسويقي. وهذا بالضبط ما نرفض القيام به في هذا المقال.

#
فارق بسيط

ما لا يحل غياب GC

No-GC ليست عصا سحرية

تؤدي إزالة أداة تجميع البيانات المهملة إلى التخلص من مصدر واحد فقط من مصادر الكمون الخلفي - وليس جميعها. لا يزال بإمكان الواجهة الخلفية لـ Rust إظهار p99 متدهورًا بسبب انتظار الشبكة، أو تجمع اتصال Postgres المشبع، أو قفل قاعدة البيانات المتنازع عليها، أو استعلام SQL مفهرس بشكل سيئ، أو استدعاء API بطيء لجهة خارجية. تعمل الآلية الموضحة في هذه المقالة على إزالة السبب الهيكلي. ولا يوفر الحصانة ضد الآخرين.

في Aurabase على سبيل المثال، تتواصل كل خدمة مع Postgres عبر مجمع اتصال (sqlx) ومع الخدمات الأخرى عبر NATS JetStream. إن مجموعة صغيرة الحجم، أو اشتراك NATS بطيء الاستخدام، أو استعلام SQL بدون فهرس مناسب، يؤدي كل منها إلى ارتفاع زمن الاستجابة الخاص به - بغض النظر عن عدم وجود أداة تجميع البيانات المهملة.

الاستنتاج العملي: يعد غياب GC سببًا معماريًا جيدًا لاختيار واجهة خلفية Rust لنظام مدرك لـ p99. ولا يعد هذا في حد ذاته ضمانًا لوقت الاستجابة - لا في Aurabase ولا في أي مكان آخر. تظل الطريقة المهمة كما هي: القياس، ونشر المنهجية، ثم تصحيح ما تكشفه القياسات. إذا كنت تقوم بالترحيل من الواجهة الخلفية باستخدام GC، فإن دليل الترحيل Supabase إلى Aurabase يوضح بالتفصيل ما الذي يتغير وما الذي يظل على حاله.

#
الأسئلة المتداولة

الأسئلة الشائعة: جامع البيانات المهملة وزمن الوصول p99

هل يضمن غياب جامع القمامة انخفاض مستوى p99؟+
لا، فهو يزيل المصدر الهيكلي للتوقف المؤقت غير المتوقع، ولكن هناك عوامل أخرى - الشبكة، وتجمع الاتصالات، وأقفال قاعدة البيانات - تؤثر أيضًا على زمن الاستجابة. راجع قسم "ما لا يصلحه GC" أعلاه.
لماذا لم يقوم Discord بضبط GC Go بشكل مختلف؟+
جرب الفريق العديد من التعديلات، بما في ذلك تقليل حجم ذاكرة التخزين المؤقت، الموثقة في منشورهم لعام 2020. ظلت المقايضة غير مواتية: توقف مؤقت أقل لـ GC مقابل المزيد من حالات فقدان ذاكرة التخزين المؤقت. أدت إعادة الكتابة في Rust إلى إزالة التسوية بدلاً من تحريكها.
هل قامت GCs الحديثة مثل Go بحل المشكلة؟+
لقد قاموا بتقليلها بشكل كبير - انتقل GC الخاص بـ Go من 300-400 مللي ثانية إلى 500 ميكروثانية بين عامي 2015 و2018 (go.dev) - ولكن لم يتم إلغاؤه. يحتفظ تتبع GC، من خلال البناء، بآلية الإيقاف المؤقت لحالات الحافة.
هل أصدرت Aurabase معيار زمن الوصول p99؟+
ليس بعد. الحقيقة التي تم التحقق منها في الكود هي عدم وجود أداة تجميع البيانات المهملة (Rust، Workspace Cargo، axum Services). لا يزال يتعين نشر رقم زمن الاستجابة p99 المُقاس ومنهجيته القابلة للتكرار - فنحن نفضل عدم وجود رقم على رقم غير مصدر.

هل أنت جاهز للنشر؟

الواجهة الخلفية الخاصة بك في خمس دقائق.

لا حاجة لبطاقة ائتمان · 500 ميجابايت مجانًا · 50000 MAU