كسر حواجز التوسع: كيف قمنا بتحسين استخدام Elasticsearch في Intercom

نشرت: 2022-09-22

Elasticsearch جزء لا غنى عنه من الاتصال الداخلي.

إنها تدعم ميزات الاتصال الداخلي الأساسية مثل Inbox و Inbox Views و API والمقالات وقائمة المستخدمين والتقارير و Resolution Bot وأنظمة التسجيل الداخلية الخاصة بنا. تحتوي مجموعات Elasticsearch لدينا على أكثر من 350 تيرابايت من بيانات العملاء وتخزن أكثر من 300 مليار مستند وتخدم أكثر من 60 ألف طلب في الثانية في الذروة.

مع زيادة استخدام Elasticsearch الخاص بـ Intercom ، نحتاج إلى ضمان نطاق أنظمتنا لدعم نمونا المستمر. مع الإطلاق الأخير لصندوق الوارد الخاص بنا من الجيل التالي ، أصبحت موثوقية Elasticsearch أكثر أهمية من أي وقت مضى.

قررنا معالجة مشكلة في إعداد Elasticsearch لدينا والتي شكلت خطر التوافر وهددت بالتوقف في المستقبل: التوزيع غير المتكافئ لحركة المرور / العمل بين العقد في مجموعات Elasticsearch الخاصة بنا.

العلامات المبكرة لعدم الكفاءة: عدم توازن الحمل

يتيح لك Elasticsearch القياس أفقيًا عن طريق زيادة عدد العقد التي تخزن البيانات (عقد البيانات). بدأنا نلاحظ عدم توازن الحمل بين عقد البيانات هذه: كان بعضها تحت ضغط أكبر (أو "أكثر سخونة") من البعض الآخر بسبب زيادة استخدام القرص أو وحدة المعالجة المركزية.

الشكل 1 عدم التوازن في استخدام وحدة المعالجة المركزية
(الشكل 1) عدم التوازن في استخدام وحدة المعالجة المركزية: عقدتان رائعتان مع استخدام وحدة المعالجة المركزية بنسبة 20٪ أعلى من المتوسط.

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

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

الشكل 4 إضافة العقد

(الشكل 2) لم تؤد إضافة عقدة بيانات إلى تقليل الحمل على المضيف A. ستؤدي إضافة عقدة أخرى إلى تقليل الحمل على المضيف A ، ولكن سيظل توزيع الحمل غير المتكافئ للمجموعة.

على النقيض من ذلك ، فإن وجود شظايا أصغر يساعد في تقليل الحمل على جميع عقد البيانات حيث تتدرج الكتلة - بما في ذلك العقد "الساخنة" (الشكل 3).

الشكل 3 العديد من القطع الصغيرة

(الشكل 3) وجود العديد من الأجزاء الصغيرة يساعد في تقليل الحمل على جميع عقد البيانات.

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

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

ساعدنا فهمنا العميق للمشكلة على إدراك أنه يمكن توزيع الحمل بشكل متساوٍ إذا كان لدينا:

  • المزيد من الأجزاء بالنسبة إلى عدد عقد البيانات. سيضمن ذلك حصول معظم العقد على عدد متساوٍ من الأجزاء.
  • شظايا أصغر بالنسبة إلى حجم عقد البيانات. إذا أعطيت بعض العقد بضع قطع إضافية ، فلن ينتج عن ذلك أي زيادة ذات مغزى في الحمل لهذه العقد.

حل الكب كيك: عدد أقل من العقد الأكبر

يمكن تعديل هذه النسبة من عدد القطع إلى عدد عقد البيانات ، وحجم القطع إلى حجم عقد البيانات ، من خلال الحصول على عدد أكبر من الأجزاء الأصغر. ولكن يمكن تعديله بسهولة أكبر عن طريق الانتقال إلى عقد بيانات أقل ولكن أكبر.

قررنا أن نبدأ مع كب كيك للتحقق من هذه الفرضية. لقد قمنا بترحيل عدد قليل من مجموعاتنا إلى مثيلات أكبر وأكثر قوة مع عدد أقل من العقد - مع الحفاظ على نفس السعة الإجمالية. على سبيل المثال ، قمنا بنقل مجموعة من 40 مثيلًا بحجم كبير إلى 10 حالات بحجم 16x كبير ، مما قلل من عدم توازن الحمل عن طريق توزيع القطع بشكل متساوٍ أكثر.

الشكل 4 توزيع أفضل للحمل عبر القرص ووحدة المعالجة المركزية

(الشكل 4) توزيع أفضل للحمل عبر القرص ووحدة المعالجة المركزية عن طريق الانتقال إلى عدد أقل من العقد الأكبر.

تحقق تقليل عدد العقد الأقل من صحة افتراضاتنا التي تفيد بأن تعديل عدد وعقد البيانات وحجمها يمكن أن يحسن توزيع الحمل. كان من الممكن أن نتوقف عند هذا الحد ، لكن كانت هناك بعض الجوانب السلبية لهذا النهج:

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

التحدي: عبور عتبة مؤشرات الكائنات العادية المضغوطة (OOPs)

لم يكن الانتقال إلى عدد أقل من العقد الأكبر أمرًا بسيطًا مثل مجرد تغيير حجم المثيل. كان الاختناق الذي واجهناه هو الاحتفاظ بإجمالي حجم الكومة المتاح (حجم الكومة في عقدة واحدة × العدد الإجمالي للعقد) أثناء الترحيل.

لقد قمنا بتقييد حجم الكومة في عقد البيانات الخاصة بنا إلى حوالي 30.5 جيجابايت ، كما هو مقترح من قبل Elastic ، للتأكد من بقائنا تحت الحد الأقصى حتى يتمكن JVM من استخدام OOPs المضغوطة. إذا قمنا بتقييد حجم الكومة إلى حوالي 30.5 جيجابايت بعد الانتقال إلى عدد أقل وأكبر من العقد ، فسنقلل سعة الكومة بشكل عام لأننا سنعمل مع عدد أقل من العقد.

"كانت الحالات التي كنا نرحل إليها ضخمة وأردنا تخصيص جزء كبير من ذاكرة الوصول العشوائي إلى الكومة بحيث يكون لدينا مساحة للمؤشرات ، مع بقاء كافٍ لذاكرة التخزين المؤقت لنظام الملفات"

لم نتمكن من العثور على الكثير من النصائح حول تأثير تجاوز هذه العتبة. كانت الحالات التي كنا نرحل إليها ضخمة وأردنا تخصيص جزء كبير من ذاكرة الوصول العشوائي إلى الكومة بحيث يكون لدينا مساحة للمؤشرات ، مع ترك مساحة كافية لذاكرة التخزين المؤقت لنظام الملفات. لقد جربنا عددًا قليلاً من العتبات من خلال تكرار حركة مرور الإنتاج لدينا لاختبار المجموعات ، واستقرنا على حوالي 33٪ إلى ~ 42٪ من ذاكرة الوصول العشوائي كحجم كومة للأجهزة التي تحتوي على أكثر من 200 جيجابايت من ذاكرة الوصول العشوائي.

أثر التغيير في حجم الكومة على مجموعات مختلفة بشكل مختلف. بينما لم تظهر بعض المجموعات أي تغيير في المقاييس مثل "JVM٪ heap in use" أو "Young GC Collection Time" ، كان الاتجاه العام هو الزيادة. بغض النظر ، كانت تجربة إيجابية بشكل عام ، وكانت مجموعاتنا تعمل منذ أكثر من 9 أشهر مع هذا التكوين - دون أي مشاكل.

الإصلاح طويل المدى: العديد من القطع الصغيرة

يتمثل الحل الأطول أجلاً في التحرك نحو الحصول على عدد أكبر من الأجزاء الأصغر حجمًا بالنسبة لعدد وحجم عقد البيانات. يمكننا الوصول إلى شظايا أصغر بطريقتين:

  • ترحيل الفهرس للحصول على مزيد من الأجزاء الأولية: يؤدي ذلك إلى توزيع البيانات الموجودة في الفهرس بين المزيد من الأجزاء.
  • تقسيم الفهرس إلى فهارس أصغر (أقسام): يؤدي هذا إلى توزيع البيانات الموجودة في الفهرس بين المزيد من الفهارس.

من المهم ملاحظة أننا لا نريد إنشاء مليون جزء صغير أو مئات الأقسام. يتطلب كل فهرس وجزء بعض موارد الذاكرة ووحدة المعالجة المركزية.

"ركزنا على تسهيل تجربة التكوينات دون المستوى الأمثل وإصلاحها داخل نظامنا ، بدلاً من التركيز على التهيئة" المثالية ""

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

لجعل أنظمتنا أكثر مرونة ، ركزنا على تسهيل تجربة التكوينات دون المستوى الأمثل وإصلاحها داخل نظامنا ، بدلاً من التركيز على التكوين "المثالي".

فهارس التقسيم

يمكن أن تؤثر زيادة عدد الأجزاء الأولية في بعض الأحيان على أداء الاستعلامات التي تجمع البيانات ، والتي واجهناها أثناء ترحيل المجموعة المسؤولة عن منتج تقارير Intercom. في المقابل ، يؤدي تقسيم الفهرس إلى فهارس متعددة إلى توزيع التحميل على المزيد من الأجزاء دون إضعاف أداء الاستعلام.

لا يشترط الاتصال الداخلي لتحديد موقع البيانات لعدة عملاء ، لذلك اخترنا التقسيم بناءً على المعرفات الفريدة للعملاء. ساعدنا ذلك في تقديم قيمة أسرع من خلال تبسيط منطق التقسيم وتقليل الإعداد المطلوب.

"لتقسيم البيانات بطريقة أقل تأثيرًا على العادات والأساليب الحالية لمهندسينا ، استثمرنا أولاً الكثير من الوقت في فهم كيفية استخدام مهندسينا Elasticsearch"

لتقسيم البيانات بطريقة أقل تأثيرًا على العادات والأساليب الحالية لمهندسينا ، استثمرنا أولاً الكثير من الوقت في فهم كيفية استخدام مهندسينا Elasticsearch. لقد قمنا بدمج نظام المراقبة الخاص بنا بعمق في مكتبة عميل Elasticsearch واكتسحنا قاعدة الرموز الخاصة بنا للتعرف على جميع الطرق المختلفة التي يتفاعل بها فريقنا مع Elasticsearch APIs.

كان وضع الاسترداد من الفشل هو إعادة محاولة الطلبات ، لذلك أجرينا التغييرات المطلوبة حيث كنا نقدم طلبات غير ثابتة. انتهى بنا المطاف بإضافة بعض linters لتثبيط استخدام واجهات برمجة التطبيقات مثل "update / delete_by_query" ، لأنها جعلت من السهل تقديم طلبات غير فاعلة.

لقد بنينا قدرتين تعملان معًا لتقديم وظائف كاملة:

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

"لقد قمنا بتحسين عملياتنا لتقليل نصف قطر الانفجار لأي حوادث ، دون المساومة على السرعة"

إجمالاً ، تبدو عملية ترحيل الفهرس إلى الأقسام كما يلي:

  1. نقوم بإنشاء أقسام جديدة وتشغيل الكتابة المزدوجة حتى تظل أقسامنا محدثة بالفهرس الأصلي.
  2. نقوم بإعادة ملء جميع البيانات. ستتم كتابة طلبات الردم هذه بشكل مزدوج في الأقسام الجديدة.
  3. عند اكتمال إعادة التعبئة ، نتحقق من أن كلا الفهرين القديم والجديد لهما نفس البيانات. إذا كان كل شيء يبدو جيدًا ، فإننا نستخدم علامات الميزات لبدء استخدام الأقسام لعدد قليل من العملاء ومراقبة النتائج.
  4. بمجرد أن نكون واثقين ، ننقل جميع عملائنا إلى الأقسام ، كل ذلك أثناء الكتابة المزدوجة لكل من الفهرس القديم والأقسام.
  5. عندما نتأكد من نجاح الترحيل ، نتوقف عن الكتابة المزدوجة ونحذف الفهرس القديم.

هذه الخطوات التي تبدو بسيطة تحتوي على الكثير من التعقيد. لقد قمنا بتحسين عملياتنا لتقليل نصف قطر الانفجار لأي حوادث ، دون المساومة على السرعة.

جني الفوائد

ساعدنا هذا العمل في تحسين توازن الحمل في مجموعات Elasticsearch الخاصة بنا. الأهم من ذلك ، يمكننا الآن تحسين توزيع الحمل في كل مرة يصبح فيها غير مقبول عن طريق ترحيل الفهارس إلى أقسام بها عدد أقل من الأجزاء الأولية ، وتحقيق أفضل ما في العالمين: عدد أقل وأصغر من القطع لكل فهرس.

بتطبيق هذه الدروس ، تمكنا من إطلاق العنان لمكاسب ومدخرات مهمة في الأداء.

  • لقد خفضنا تكاليف مجموعتين من مجموعاتنا بنسبة 40٪ و 25٪ على التوالي ، وحققنا وفورات كبيرة في التكاليف على مجموعات أخرى أيضًا.
  • لقد قللنا متوسط ​​استخدام وحدة المعالجة المركزية لمجموعة معينة بنسبة 25٪ وحسّننا متوسط ​​زمن انتقال الطلب بنسبة 100٪. لقد حققنا ذلك من خلال ترحيل مؤشر حركة مرور عالية إلى أقسام بها عدد أقل من الأجزاء الأولية لكل قسم مقارنة بالأصل.
  • تتيح لنا القدرة العامة على ترحيل الفهارس أيضًا تغيير مخطط الفهرس ، مما يسمح لمهندسي المنتجات ببناء تجارب أفضل لعملائنا ، أو إعادة فهرسة البيانات باستخدام إصدار Lucene الأحدث الذي يطلق العنان لقدرتنا على الترقية إلى Elasticsearch 8.

الشكل 5 تحسين عدم توازن الحمل واستخدام وحدة المعالجة المركزية

(الشكل 5) تحسن بنسبة 50٪ في عدم توازن الحمل وتحسين بنسبة 25٪ في استخدام وحدة المعالجة المركزية عن طريق ترحيل مؤشر حركة المرور المرتفع إلى الأقسام التي تحتوي على عدد أقل من الأجزاء الأولية لكل قسم.

الشكل 6 متوسط ​​وقت استجابة الطلب

(الشكل 6) تم تحسين متوسط ​​زمن انتقال الطلب بنسبة 100٪ في المتوسط ​​من خلال ترحيل مؤشر حركة المرور المرتفع إلى أقسام بها عدد أقل من الأجزاء الأولية لكل قسم.

ماذا بعد؟

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

هل أنت مهتم بالطريقة التي يعمل بها فريقنا الهندسي في Intercom؟ تعرف على المزيد وتحقق من أدوارنا المفتوحة هنا.

وظائف CTA - الهندسة (أفقي)