:: دورة الإحتراف في إدارة مخاطر المشاريع|دورات إدارة المشاريع مركزitr (آخر رد :ايمان محمد)       :: دورة الإحتراف في إدارة المشاريع وتطوير المهارات الإدارية والقيادية|دورات إدارة المشار (آخر رد :ايمان محمد)       :: دورة التحضير لشهادة مدير مشاريع معتمد وإكتساب المهارات|دورات إدارة المشاريع مركزitr (آخر رد :ايمان محمد)       :: دورة إستراتيجيات مكافحة غسل الأموال|دورات البنوك و المالية مركزitr (آخر رد :ايمان محمد)       :: دورة وسائل الدفع وآليات الضمان فى التجارة الخارجية وشروط البيوع الدولية incoterms 201 (آخر رد :ايمان محمد)       :: دورة تطبيقات الحوكمة فى القطاع المصرفى Governance|دورات البنوك و المالية مركزITR (آخر رد :ايمان محمد)       :: دورة التحكيم القضائي|دورات القانون والعقود مركزitr (آخر رد :ايمان محمد)       :: دورات تسويق مبيعات خدمة العملاء الاستثمار العقاري وفن البيع والتسويق (آخر رد :AHMEDBBMF)       :: دورة القانون الإداري وتطبيقاته فى منظومات الإدارة الحديثة|دورات القانون والعقود مركزi (آخر رد :ايمان محمد)       :: البرنامج التدريبى دور الذكاء الاصطناعي في إدارة المؤسسات (آخر رد :AHMEDBBMF)      
اختر لونك:
وَقُلِ اعْمَلُوا فَسَيَرَى اللَّهُ عَمَلَكُمْ وَرَسُولُهُ وَالْمُؤْمِنُونَ ۖ وَسَتُرَدُّونَ إِلَىٰ عَالِمِ الْغَيْبِ وَالشَّهَادَةِ فَيُنَبِّئُكُم بِمَا كُنتُمْ تَعْمَلُونَ [ التوبة : ( 105 )] كلمة الإدارة

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

يُمنع كتابة مواضيع السحر والشعوذة والروحانيات والابراج بكافة الأشكال والمخالف سيعاقب بحظر مؤقت وإذا تكرر سيكون حظر دائم تنبيه هام جداً



أضف رد جديد
 
LinkBack أدوات الموضوع انواع عرض الموضوع

قديم 23-02-2019, 03:43 PM   #1
تاريخ التسجيل: Feb 2019
المشاركات: 1,010
التقييم: 10
تاريخ التسجيل: Feb 2019
المشاركات: 1,010
التقييم: 10
افتراضي كيف تتجنب مشكل N+1 في Laravel باستخدام التحميل النّشط Eager Loading

كيف تتجنب مشكل N+1 في Laravel باستخدام التحميل النّشط Eager Loading

قبل أن ندخل في صلب الموضوع، دعني أطلعك على بعض مشاكل تصميم البرمجيات التي قد تواجهك. قبل بضعة أيام اشتكى أحد العملاء من بطء كبير جدًا في تحميل بعض صفحات موقعه. لذلك قرّرت أن أتفحّص تلك الصفحة وقد صدمني ما وجدته. فقد أظهر قسم الاستعلامات(query section) بأنّه تمّ تنفيذ أكثر من 16500 استعلام على تلك الصفحة لوحدها!
وجدت الشيفرة البرمجية المسبّبة لتلك المشكلة.

laravel3.png

لقد كانت ثلاث حلقات من نوع foreach تستعلم عن صفةattribute معينة وعن الصّفات المتعلقة بها. كانت هذه الحلقات تعمل بشكل جيّد حتى أصبح هناك ما يقارب من 5500 عُنصر في قاعدة البيانات. إليك ما كان يحصل:

$main_object = MainObject::all();

foreach($main_object as $object) {
echo $object->some_property;

foreach($object->related_object as $related) {
echo $related->some_property;
echo $related->another_property;
}

foreach($object->another_related as $another) {
echo $another->some_property;
echo $another->another_property;
}
}
إذا كانت $main_object = MainObject::all(); تقوم بإرجاع 5500 نتيجة، فإنّ أوّل حلقة foreach سوف تُنفَّذ 5500 مرة وكذلك الحال بالنّسبة للحلقتين الثانية والثالثة. عند استعمال ما يُسمى بمخطّط الكائن العلائقيّة ORM ، يقوم أغلب المطورين بكتابة استعلامات قواعد بيانات غير فعّالة، ويُصعّب استخدام الـORM مُهمّة اكتشاف هذه الأخطاء. تُسمّى هذه المشكلة بمشكلة N+1 وأعتقد بأنّ المُطوّر السّابق لم ينتبه لذلك. ولنتفادى هذه المشكلة سنقوم باستعمال التحميل النّشط(eager loading).

ما هو التحميل النّشط(eager loading)؟
يمكنك القول بأنّ التّحميل النّشط هو طريقة لفعل كلّ شيء عند الطّلب. وهو أيضًا عكس التحميل الخامل(lazy loading) بحيث يتمّ تنفيذ المهام عند الحاجة إليها. يساعد التحميل النّشط على تفادي الوقوع في مشاكل الأداء. حتى تتضح الصورة بشكل أفضل تخيّل معي الحالة التّالية:

Figure1_stores_model.png

يوجد لدينا نموذج علاقة كائنيّة مُعزّزة enhanced entity-relationship model يحتوي على ثلاث وحدات/كائنات مترابطة. يمكننا قراءة هذا النموذج كالتالي: كل عضو(member) يمكنه أن يملك أكثر من متجر(store) ولكن المتجر الواحد ينتمي إلى عضو واحد فقط. كل متجر يمكن أن يحتوي على أكثر من مُنتَج(product) ولكن المنتَج الواحد ينتمي إلى متجر واحد فقط.
الخطوة التّالية هي إنشاء نماذج eloquent لهذه الوحدات/الكائنات.
عضو(member):

<?php namespace App;

use Illuminate\Database\Eloquent\Model;

class Member extends Model {

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['username', 'email', 'first_name', 'last_name'];

public function stores() {
return $this->hasMany('App\\Store');
}
}
متجر(store):

<?php namespace App;

use Illuminate\Database\Eloquent\Model;

class Store extends Model {

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['name', 'slug', 'site', 'member_id'];

public function member() {
return $this->belongsTo('App\\Member');
}

public function products() {
return $this->hasMany('App\\Product');
}
}
مُنتَج(product):

<?php namespace App;

use Illuminate\Database\Eloquent\Model;

class Product extends Model {

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['name', 'short_desc', 'long_desc', 'price', 'store_id', 'member_id'];

public function store() {
return $this->belongsTo('App\\Store');
}
}
تخيّل بأنّك تقوم ببناء تطبيق ما وأنّ هذا التطبيق يسمح للمستخدمين بإنشاء متجر خاص بهم. بكل تأكيد وكما هو الحال في جميع المتاجر، فسيكون لدى المستخدمين القدرة على إنشاء أكثر من مُنتج. وما نريده أيضًا هو إنشاء صفحة لعرض جميع المتاجر وأفضل المنتجات الموجودة في كلّ متجر. شيء يشبه التالي:

Figure2_storelist_mockup.png

قد ينتهي بك المطاف بشيء كهذا في المتحكم(controller) الخاص بك:

<?php namespace App\Http\Controllers;

use App\Repositories\StoreRepository;

class StoresController extends Controller {

protected $stores;

function __construct(StoreRepository $stores) {
$this->stores = $stores;
}


public function index() {
$stores = $this->stores->all();

return \View::make('stores.index')->with('stores', $stores);
}
}
في الـView تريد أن تظهر تلك البيانات:

@foreach($stores as $store)
<h1>{{ $store->name }}</h1>
<span>Owner: {{ $store->member->first_name . ' ' . $store->member->last_name }}</span><br>

<h2>Products:</h2>
@foreach($store->products as $product)
<h3>{{$product->name}}</h3>
<span>{{$product->short_desc}}</span><br/><br/>
<span>Price: {{$product->price}}</span>
<br/>
<?php Debugbar::info('Product displayed'); ?>
@endforeach
<br/>
========================
<br/>
@endforeach
وتكون النتيجة كالتالي:

Figure3_n_plus_one_problem.png

في هذا المثال، قُمت بإعطاء قاعدة البيانات 5 أعضاء و3 متاجر و4 منتجات. الاستعلام الأول هو لجلب جميع المتاجر من قاعدة البيانات وهذا هو الجزء “+1” من المشكلة N+1. وفي هذا المثال تحديدًا، فإنّ الحرف “N” يُمثّل عدد المتاجر التي سيقوم الاستعلام الأول بإرجاعها، وسيتم تنفيذ استعلام select *from بنفس العدد على جداول المنتجات والأعضاء. ولأنّ هناك 3 متاجر، فهذا يعني بأنّنا سنقوم بإجراء 3 استعلامات على جدول الأعضاء و3 استعلامات أخرى على جدول المنتجات، وبالتالي يصبح عدد الاستعلامات التي تمّ تنفيذها بالكامل هو 3+3+1.
تخيّل معي الآن ماذا كان سيحصل لو كان هناك 5 آلاف أو 10 آلاف متجر؟ سيكون هناك من 10 آلاف إلى 20 ألف استعلام في كلّ مرّة يزور أحدهم تلك الصفحة. وماذا لو كان هناك 10 آلاف أو 100 ألف زائر خلال 24 ساعة فقط؟ سيكون ذلك بمثابة كابوسٍ مرعبٍ بلا شك. يظهر الآن وبكلّ وضوح بأنّ هذه الطّريقة غير مجدية وقاتلة للأداء(performance killer). لن يهم أي قاعدة بيانات تستخدم أو قوّة الخادوم فستكون هناك دائمًا نقطة ما لن يستطيع العتاد القوي التعامل معها. يمكنك تحسين الأداء عن طريق التخزين المؤقت(caching) للاستعلامات؛ باستخدام Redis على سبيل المثال. سيعمل ذلك جيّدًا ولكن بشكل مؤقّت. ستقوم بتلك الطّريقة فقط بتأخير النّهاية الحتميّة التي ستكلّفك الكثير من المال والوقت وقد تخسر أيضًا الكثير من المستخدمين.
وهنا يأتي دور التّحميل النّشط. فاستعمال التحميل النّشط في Laravel يُعدّ أمرًا في غاية السهولة. فعن طريق استخدام تابع with تقوم بتحديد العلاقات التي تريد أن يتمّ تحميلها تحميلاً نشطًا:

$stores = Store::with('member','products')->get();
باستعمال التحميل النّشط، قمنا بتقليص عدد الاستعلامات من 7 إلى 3 فقط، كما توضح الصورة التالية:

Figure4_n_plus_one_problem_eager.png

وحتى لو كان هناك 10 آلاف مُدخل للمتجر، فإنّ عدد الاستعلامات لن يتغير وسيبقى 3 فقط. وكما ترى، فإنّ استخدام التّحميل النّشط بالشّكل المناسب قد يُحسّن من الأداء بصورة كبيرة. وحتى نرى تحسّنًا فعليًا، فإنّه يجب أيضًا أن يكون هناك مؤشّر/فهرس(index) على حقول المُعرّفات(id fields) في جداول الأعضاء والمنتجات. فمع وجود الكثير من السجلات(records) فإنّ تنفيذ in( ‘1’, ‘2’, … ) على حقول غير مفهرسة(non-indexed) قد يأخذ بعض الوقت.

بعد هذه المقدمة السّريعة عن التحميل النّشط، لنرى كيف يمكننا استعمال العلاقات(relations) مع المستودعات(repositories).

تمديد فئة المستودع
سأريك الآن طريقة واحدة لكيفية استعمال العلاقات (relations) في فئات المستودع. هذا مثال لنتيجة نهائية:

function __construct(StoreRepository $stores) {
$this->stores = $stores;
}


public function index() {
$stores = $this->stores->with('member', 'products')->all();

....
}
فكما ترى في الأعلى، هناك تابع with ليمكننا من سلسلة نموذج العلاقات. هذا التابع سيكون شبيها بالتّابع with الخاصة بمُنشئ الاستعلامات في Laravel(Laravel’s Query Builder).

public function with($relations) {
if (is_string($relations)) $relations = func_get_args();

$this->with = $relations;

return $this;
}
نحتاج الآن لربط كل علاقة(relation) إلى النموذج:

protected function eagerLoadRelations() {
if(!is_null($this->with)) {
foreach ($this->with as $relation) {
$this->model->with($relation);
}
}

return $this;
}
ما تبّقى علينا الآن هو تحديث تابع المستودع all() (وأي توابع أخرى تريدها) لتستخدم التحميل النّشط:

public function all($columns = array('*')) {
$this->applyCriteria();
$this->newQuery()->eagerLoadRelations();
return $this->model->get($columns);
}
وكما ذكرت مسبقًا، يمكنك إضافة علاقات متعدّدة داخل وسيلة with(). وهذا مثال على StoresController:

<?php namespace App\Http\Controllers;

use App\Repositories\StoreRepository;

class StoresController extends Controller {

protected $stores;

function __construct(StoreRepository $stores) {
$this->stores = $stores;
}

public function index() {
$stores = $this->stores->with('member', 'products')->all();

return \View::make('stores.index')->with('stores', $stores);
}
}
يمكنك في الـView أن تعرض أيّ بيانات بالطريقة التي تريدها. لأغراض التّجريب سيكون التالي كافيًا:

@foreach($stores as $store)
<h1>{{ $store->name }}</h1>
<span>Owner: {{ $store->member->first_name . ' ' . $store->member->last_name }}</span><br>

<h2>Products:</h2>
@foreach($store->products as $product)
<h3>{{$product->name}}</h3>
<span>{{$product->short_desc}}</span><br/><br/>
<span>Price: {{$product->price}}</span>
<br/>
<?php Debugbar::info('Product displayed'); ?>
@endforeach
<br/>
========================
<br/>
@endforeach
يوجد الآن 3 استعلامات فقط كما هو متوقع:

الفارس غير متواجد حالياً   اقتباس
قديم 26-07-2019, 03:24 PM   #2
الصورة الرمزية هديل الحرف
تاريخ التسجيل: Jul 2019
العمر: 30
المشاركات: 504
التقييم: 10
افتراضي

بارك الله فيك
إستمر ولك التوفيق بـإذن الله
تقديري وإحترامي
هديل الحرف غير متواجد حالياً   اقتباس
أضف رد جديد

الكلمات الدلالية (Tags)
منتديات رحيل, رحيل, شبكة رحيل, r7il, r7il.com


الذين يشاهدون محتوى الموضوع الآن : 1 ( الأعضاء 0 والزوار 1)
 

تعليمات المشاركة
لا تستطيع إضافة مواضيع جديدة
لا تستطيع الرد على المواضيع
لا تستطيع إرفاق ملفات
لا تستطيع تعديل مشاركاتك

BB code is متاحة
كود [IMG] متاحة
كود HTML معطلة
Trackbacks are متاحة
Pingbacks are متاحة
Refbacks are متاحة


المواضيع المتشابهه
الموضوع كاتب الموضوع المنتدى مشاركات آخر مشاركة
تحميل المنشورات ديناميكيا في ووردبريس باستخدام تقنية ajax مهرة النجدية قسم تطوير المواقع ومحركات البحث والسيو Seo والووردبريس WordPress 1 27-07-2019 11:08 PM
علاقات Eloquent والتحميل الحثيث في Laravel 5 الفارس قسم تطوير المواقع ومحركات البحث والسيو Seo والووردبريس WordPress 1 27-07-2019 10:44 AM
laravel5 todo إنشاء تطبيق Todo List بسيط باستخدام Laravel 5 - الجزء الثاني الفارس قسم تطوير المواقع ومحركات البحث والسيو Seo والووردبريس WordPress 1 27-07-2019 10:41 AM
تثبيت Laravel 5 وإعداده على Windows وUbuntu الفارس قسم تطوير المواقع ومحركات البحث والسيو Seo والووردبريس WordPress 1 27-07-2019 10:40 AM
طرق كسب المال عبر الإنترنت باستخدام ووردبريس raheel قسم تطوير المواقع ومحركات البحث والسيو Seo والووردبريس WordPress 0 27-03-2019 07:04 PM


الساعة الآن 08:32 PM

 


Content Relevant URLs by vBSEO ©2010, Crawlability, Inc.