توزيع البيانات في Apache Ignite

مرحبا! هذا المنشور هو نسخة مختصرة قليلاً من محاضرتي المسمى في اجتماع مجتمع Apache Ignite . يمكنك مشاهدة نسخة الفيديو الكاملة مع الأسئلة والأجوبة هنا ، وتنزيل الشرائح هنا . في التقرير ، حاولت أن أوضح من خلال الأمثلة كيفية توزيع البيانات في Apache Ignite.

لماذا تحتاج لتوزيع أي شيء


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

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

يمكن صياغة مشكلة توزيع البيانات بين عقد الطوبولوجيا كمجموعة من المتطلبات ، والتي يجب أن يفي بها توزيعنا:

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

تحقيق أول متطلبين سهل إلى حد ما.

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

صورة

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

ولكن ماذا يحدث إذا أدخلنا العقدة الرابعة في الطوبولوجيا؟

صورة

تغيرت وظيفتنا ، والآن نأخذ الباقي من القسمة على 4 ، وليس على 3. وإذا تغيرت الوظيفة ، فقد تغير التوزيع ، وكثيرًا.

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

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

هناك طريقتان شائعتان لحل مشكلة توزيع البيانات ، مع مراعاة المتطلبات المذكورة ، كما يلي:

  • التجزئة المتسقة
  • أكبر خوارزمية الوزن العشوائي (HRW) ، والمعروفة أيضًا باسم تجزئة Rendezvous.

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

دعونا نلقي نظرة على هذه الخوارزميات بمزيد من التفصيل.

تجزئة متناسقة


الحيلة الكامنة وراء خوارزمية التجزئة المتسقة هي تعيين كل من العقد والكائنات المخزنة على نفس مساحة المعرف. وهذا يجعل كياناتنا وعناصرنا وعقدنا المختلفة قابلة للمقارنة.

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

نحن نمثل مساحة معرفنا في شكل دائرة ، أي نفترض ببساطة أن الحد الأقصى لقيمة المعرف يتبع مباشرة الحد الأدنى لقيمة المعرف.

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

الحركة التخيلية في اتجاه عقارب الساعة معادلة وظيفيا للبحث الثنائي في مصفوفة مرتبة من رموز العقدة.

صورة

في الرسم البياني ، يعكس كل قطاع من لون معين مساحة المعرف التي تكون عقدة معينة مسؤولة عنها.

إذا أضفنا عقدة جديدة ،

صورة

فسيتم تقسيم أحد القطاعات إلى قسمين وسيتولى المفاتيح المقابلة تمامًا.

في هذا المثال ، استحوذت العقدة 3 على جزء من مفاتيح العقدة 1.

كما ترى ، يعطي هذا النهج توزيعًا غير متكافئ للكائنات عبر العقد ، لأنه يعتمد بشكل كبير على معرفات العقد نفسها. كيف يمكن تحسين هذا النهج؟

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

صورة

في هذا المثال ، تحتوي كل عقدة على 4 رموز مميزة.

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

أما بالنسبة لمتطلبات الحد الأدنى من التغيير في التوزيع عند تغيير الهيكل ، فهو راضٍ لأن الترتيب المتبادل للعقد على الدائرة لم يتغير. أولئك. لن تؤدي إزالة عقدة من الطوبولوجيا إلى تغيير علاقة الترتيب بين العقد المتبقية.

تجزئة رانديفو


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

التجزئة مرة أخرى تساعدنا في ذلك. ولكن الآن ، من أجل تحديد وزن العقدة N لكائن معين O ، نمزج معرف الكائن مع معرف العقدة ونأخذ التجزئة من هذا المزيج. بعد القيام بهذه العملية لكل عقدة ، نحصل على مجموعة من الأوزان نقوم من خلالها بفرز العقد.

العقدة التي تبين أنها الأولى وستكون مسؤولة عن تخزين الكائن.

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

صورة

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

أضف عقدة أخرى إلى الطوبولوجيا.

صورة

لقد وضعتها عمداً على القطر لأخذ جميع الخيارات الممكنة في الاعتبار. هنا ، دخلت العقدة 3 ، الموضحة باللون الأخضر ، في الهيكل. لذلك ، تغير توزيع الوزن للعقد لكل من المفاتيح. يشير الأحمر إلى العقد التي غيرت موقعها في القائمة لمفتاح معين ، لأن كانت أوزان هذه العقد أقل من وزن العقدة المضافة. ومع ذلك ، أثر هذا التغيير على مفتاح واحد فقط ، K3.

دعونا نستمد عقدة غادرة من طوبولوجيا.

صورة

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

يبدو توزيع اللقاءات جيدًا ولا يتطلب حيلًا إضافية مقارنة بالتجزئة المتسقة مثل الرموز.

في حال أردنا دعم النسخ المتماثل ، ستكون العقدة التالية في القائمة هي النسخة المتماثلة الأولى للكائن ، وستكون العقدة التالية هي النسخة المتماثلة الثانية ، إلخ.

كيف يتم استخدام التجزئة في Apache Ignite


تعتبر وظيفة التقارب مسؤولة عن توزيع البيانات في Apache Ignite (انظر واجهة AffinityFunction ). التطبيق الافتراضي هو تجزئة التقاء (راجع فئة RendezvousAffinityFunction ).

أول شيء يجب الانتباه إليه هو أن Apache Ignite لا يقوم بتعيين الكائنات المخزنة مباشرة إلى عقد الهيكل. بدلاً من ذلك ، تم تقديم مفهوم إضافي - التقسيم.

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

وبالتالي ، يمكننا عرض الكائنات على الأقسام باستخدام التقسيم المعياري الفعال ، واستخدام التجزئة التقسيم لعرض الأقسام على العقد.

صورة

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

تحسب كل عقدة هذا التوزيع بشكل مستقل ، ولكن في كل العقد التي لها نفس بيانات الإدخال ، سيكون هذا التوزيع متطابقًا.

يمكن أن يحتوي التقسيم على عدة نسخ ، نسميها نسخًا احتياطية. القسم الأساسي يسمى القسم الأساسي.

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

يتم تقسيم ذاكرة التخزين المؤقت في Ignite وتكرارها.

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

صورة

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

في ذاكرة التخزين المؤقت المنسوخة ، يكون عدد النسخ الاحتياطية دائمًا مساوياً لعدد عقد الطوبولوجيا ناقص 1. أي ، تحتوي كل عقدة دائمًا على نسخ من جميع الأقسام.

صورة

تكون ذاكرة التخزين المؤقت هذه هي الأنسب للعمل مع البيانات التي نادرًا ما يتم تغييرها (على سبيل المثال ، الأدلة) وتوفر أكبر قدر من التوفر ، مثل يمكن أن نفقد عقد N-1 (في هذه الحالة 3) دون فقدان البيانات. أيضًا في هذا الخيار ، سنحصل على أقصى أداء للقراءة إذا سمحنا بقراءة البيانات من الأقسام الأساسية والنسخ الاحتياطية.

تجميع البيانات في Apache Ignite


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

إذا تم توزيع الكائنات بواسطة أقسام من نفس دالة التقارب ، فمن المنطقي أن تقع الكائنات التي لها نفس مفتاح التقارب في نفس القسم ، وبالتالي ، إلى نفس العقدة. في Ignite ، يسمى هذا التجميع.

بشكل افتراضي ، يكون مفتاح التقارب هو المفتاح الأساسي للكائن. ولكن في Ignite ، يمكنك استخدام أي حقل آخر لكائن كمفتاح تقارب.

يقلل التجميع بشكل كبير من كمية البيانات المرسلة بين العقد لإجراء العمليات الحسابية أو استعلامات SQL ، مما يؤدي بطبيعة الحال إلى تقليل الوقت المستغرق في هذه المهام. تأمل هذا المفهوم بالقدوة.

دع نموذج بياناتنا يتكون من كيانين: الأمر (الأمر) وموضع الأمر (الطلب). أمر واحد يمكن أن يتوافق مع العديد من العناصر. تكون معرّفات الترتيب والعنصر مستقلة ، ولكن العنصر يحتوي على مفتاح خارجي يشير إلى الترتيب المقابل.

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

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

صورة

في الرسم البياني ، يتم تمثيل الطلبات بالمربعات والمواقع في الدوائر. يشير اللون إلى أن العنصر ينتمي إلى الطلب.

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

ماذا لو أخبرنا Ignite أنه يجب وضع عناصر الطلب في نفس العقد مثل الطلبات نفسها ، أي اجمع بيانات؟

كمفتاح التقارب للموضع ، سنأخذ المفتاح الخارجي OrderId وسيتم استخدام هذا الحقل عند حساب القسم الذي ينتمي إليه السجل. علاوة على ذلك ، داخل القسم ، يمكننا دائمًا العثور على كائننا من خلال المفتاح الأساسي.

صورة

الآن ، إذا كان كلا ذاكرة التخزين المؤقت (Order و OrderItem) يستخدمان نفس وظيفة التقارب مع نفس المعلمات ، فستكون بياناتنا قريبة ولن نحتاج إلى التنقل عبر الشبكة للحصول على عناصر الطلب.

تكوين التقارب في Apache Ignite


في التطبيق الحالي ، يعد كائن دالة التقارب معلمة تكوين ذاكرة التخزين المؤقت.

تأخذ دالة التقارب نفسها الحجج التالية عند الإنشاء:

  • عدد الأقسام ؛
  • عدد النسخ الاحتياطية (في الواقع ، هذه أيضًا هي معلمة تكوين ذاكرة التخزين المؤقت) ؛
  • مرشح النسخ الاحتياطي
  • استبعاد العلم الجيران.

لا يمكن تغيير هذه الإعدادات.

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

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

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

كمثال ، افترض أن عقدنا المادية - الخوادم - تقع في مركز البيانات في رفوف مختلفة. عادةً ما يكون لكل رف قوته المستقلة الخاصة ...

صورة

... وإذا فقدنا الرف ، فإننا نفقد البيانات.

صورة

في هذا المثال ، فقدنا نصف الأقسام.

ولكن إذا قمنا بتعيين مرشح النسخ الاحتياطي الصحيح ، فإن التوزيع سيتغير بهذه الطريقة ...

صورة

... إذا فقد الحامل ، فلن يكون هناك فقدان للبيانات وستظل متاحة.

صورة

تؤدي علامة ExceNeighbours وظيفة مماثلة ، وهي في الواقع اختصار لحالة واحدة محددة.

غالبًا ما تعمل عدة عقد Ignite على نفس المضيف الفعلي. هذه الحالة تشبه إلى حد كبير المثال الموجود مع الرفوف في مركز البيانات ، الآن فقط نحن نكافح فقدان البيانات مع فقدان المضيف ، وليس الرفوف.

صورة

والباقي هو نفسه. يمكنك تنفيذ هذا السلوك باستخدام عامل تصفية النسخ الاحتياطي. هذا العلم هو إرث تاريخي ويمكن إزالته في الإصدار الرئيسي التالي من Ignite.

يبدو أنني تحدثت عن وظيفة التقارب وتوزيع البيانات كل ما يحتاج مطور يستخدم Apache Ignite إلى معرفته.

في الختام ، دعونا نلقي نظرة على مثال لتوزيع 16 قسمًا طبولوجيا 3 عقد. من أجل البساطة والوضوح ، نعتقد أن الأقسام لا تحتوي على نسخ احتياطية.

لقد خضت للتو وكتبت اختبارًا صغيرًا أعطاني التوزيع الحقيقي:

صورة

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

الآن أضف عقدة جديدة إلى الطبولوجيا.

صورة

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

نزيل من الطوبولوجيا العقدة التي كانت موجودة فيها في المرحلة الأولية:

صورة

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

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

All Articles