تحميل البيانات المسبق والتعامل مع مشاكل استعلام N+1

عند العمل مع تطبيق Discourse rails، سواء كنت تبني إضافة أو تقوم بطلب سحب إلى discourse/discourse، ستواجه مشاكل استعلام N+1 في عدد من السياقات. يشرح هذا الموضوع كيفية التعامل مع تحميل البيانات المسبق لمعالجة مثل هذه المشاكل والحفاظ على أداء Discourse.

مشاكل استعلام N+1 في Rails

أولاً، إذا لم تكن على دراية بمشاكل استعلام N+1، وكيفية معالجتها في Rails، فراجع هذا القسم من الأدلة.

المواضيع

قضايا N+1 التي يجب الانتباه إليها عند تحميل موضوع هي عند تحميل الجداول المرتبطة بمنشور. الآن، يتم إعداد البيانات المسلسلة لموضوع فردي، ويتم تحميلها مسبقًا، في فئة TopicView. تحتوي هذه الفئة على خطاف on_preload يسمح لك بتحميل الجداول المرتبطة مسبقًا قبل تشغيل استعلامها الأساسي. هناك مثال جيد لاستخدام TopicView.on_preload في إضافة ActivityPub

يستخدم هذا الاستخدام للخطاف تحميل الحقول المخصصة مسبقًا، والتي سنغطيها أدناه، وتحميل الارتباطات الإضافية للمنشورات في الموضوع. ستلاحظ أنه يستخدم ActiveRecord::Associations::Preloader للقيام بذلك. يمكنك القراءة عن هذه الفئة في المستندات:

قوائم المواضيع

قضايا N+1 التي يجب الانتباه إليها عند تحميل قائمة مواضيع هي عند تحميل الجداول المرتبطة بموضوع. على غرار فئة TopicView للمواضيع الفردية، هناك فئة TopicList لقوائم المواضيع. ومثل TopicView، تحتوي TopicList أيضًا على طريقة on_preload يمكنك استخدامها للربط باستعلام قائمة المواضيع الرئيسي لتحميل الجداول المرتبطة قبل أن يصل الاستعلام إلى قاعدة البيانات. هناك مثال جيد لذلك في إضافة Discourse Assign:

register_topic_preloader_associations

تحتوي فئة TopicList على طريقة واجهة برمجة تطبيقات المكون الإضافي للخادم register_topic_preloader_associations، والتي تطبق بشكل أساسي نفس النمط الذي رأيناه في إضافة ActivityPub، باستخدام ``ActiveRecord::Associations::Preloader`. ستقوم بهذا العمل نيابة عنك إذا مررت لها مصفوفة من الارتباطات.

register_topic_list_preload_user_ids

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

  • مستخدم الموضوع
  • آخر مستخدم للمنشور
  • مستخدم الموضوع المميز
  • المستخدمون المسموح لهم بالموضوع

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

الفئات

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

site_all_categories_cache_query modifier

تحتوي طريقة ذاكرة التخزين المؤقت لجميع الفئات all_categories_cache على معدّل لواجهة برمجة تطبيقات المكون الإضافي للخادم يسمح لك بتعديل استعلام الفئات الكبير: site_all_categories_cache_query. تستخدم هذا المعدّل كما يلي في ملف plugin.rb الخاص بك:

register_modifier(:site_all_categories_cache_query) do |query|
   query.where("categories.name LIKE 'Cool%'")
end

البحث

تحميل البحث المسبق أيضًا لا يوصف تقنيًا على هذا النحو في الكود، ولكنه يقوم أيضًا بشيء مماثل باستعلاماته، أي التحميل المسبق.

register_search_topic_eager_load

تسمح طريقة واجهة برمجة تطبيقات المكون الإضافي للخادم register_search_topic_eager_load لك بتحميل الجداول الإضافية مسبقًا في البحث. على سبيل المثال:

register_search_topic_eager_load do |opts|
    %i(example_table)
end

الحقول المخصصة

يتم تشغيل الحقول المخصصة بواسطة القلق HasCustomFields. يتم تضمين هذا القلق في النماذج التي تحتوي على حقول مخصصة مرتبطة، أي Post، Topic، Category و Group. يحتوي نموذج HasCustomFields على نظام تحميل مسبق خاص به يمكن استخدامه بطرق مختلفة. أفضل طريقة لاستخدام التحميل المسبق لـ HasCustomFields هي من خلال طرق واجهة برمجة تطبيقات المكون الإضافي للخادم، والتي يجب أن تكون واضحة نسبيًا بالنظر إلى كل ما قلته حتى الآن.

register_preloaded_category_custom_fields

add_preloaded_group_custom_field

add_preloaded_topic_list_custom_field

يمكنك أيضًا استخدام بعض الطرق ذات المستوى الأدنى في HasCustomFields للقيام بالتحميل المسبق الخاص بك عند الضرورة، كما رأينا في مثال إضافة ActivityPub. في هذا المثال، يتم استخدام القلق HasCustomFields preload_custom_fields لتحميل حقول المنشورات المخصصة مسبقًا في TopicView.on_preload.

TopicView.on_preload do |topic_view|
  ##
  Post.preload_custom_fields(topic_view.posts, activity_pub_post_custom_field_names)
  ##
end
4 إعجابات