تحسين الاستعلامات إلى قاعدة البيانات هو شيء أساسي حينما يتعلق الأمر بتطوير المواقع الإلكترونية والبرمجة بشكل عام، خصوصا إذا كان المشروع الذي تعمل عليه يستقبل الزيارات بنسبة عالية، هنا يجب علينا تقليل الاستعلامات على قاعدة البيانات لكيلا يتم استنزاف موارد الخادم.
في حالة العكس، وإذا كان الكود غير محسن والاستعلامات تتم بشكل مكثف، يمكن أن يؤدي هذا إلى بطء تصفح الموقع أو توقفه نهائيا عن الاستجابة.
تحسين الاستعلامات على قاعدة البيانات
في ووردبريس لدينا عدة وسائل للتعامل مع قواعد البيانات لكن في غالب الحالات نستخدم WP_Query
.
WP_Query هو عبارة عن كلاس يسمح لنا بجلب بيانات الصفحاتـ المقالات وأنواع المقالات المخصصة. افتراضيا، يتم تنفيذ 5 استعلامات تتضمن على سبيل المثال التخزين المؤقت لبيانات الميتا، عداد التصفح … الخ
دائما عند قيامك باستعلام عن طريق WP_Query خد بعين الاعتبار البارامترات التالية:
$args = [
'no_found_rows' => true,
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
'posts_per_page' => 5
];
$the_query = new WP_Query( $args );
no_found_rows
يستعمل لعمل تصفح للبيانات العائدة من من WP_Query. في حالة عدم استخدامك للتصفح في الإستعلام الذي تقوم به، ببساطة قم بإعطائه القيمة true.
update_post_term_cache
هذا البارمتر اعطيه القيمة false
لكيلا يتم تحديث التخزين المؤقت للتصنيفات والوسوم والتصنيفات المخصصة، هكذا توفر عليك استعلام آخر (فقط استخدمه إذا كنت لا تحتاج ترتيب المواضيع حسب التصنيف (taxonomy)!).
update_post_term_cache
هو نفس ما ذكرناه سابقا لكن هذه المرة يتعلق بالتخزين المؤقت الخاص بالخصائص الإضافية ( post meta)، ففي حالة عدم إدراج خصائص إضافية في الاستعلام يمكنك إعطائها القيمة false
.
posts_per_page
هذا البرامتر يمكن من خلاله تحديد عدد المواضيع التي سيتم جلبها من قاعدة البيانات، ولجب كل المواضيع هناك من يضع 1- وهذا قد لا يكون مناسب إذا كان الموقع الذي تعمل عليه يتوفر على آلاف المقالات فمن الأفضل تحديد عدد المقالات الواجب جلبها لتخفيف العبء على الخادم، على سبيل المثال 'posts_per_page' => 500
.
من الأشياء التي يمكن عملها أيضا تحديد معلومات المقالة التي نحتاجها في الاستعلام، فعلى سبيل المثال إذا كنا نحتاج فقط المعرف الوحيد والعنوان، الكود سيكون على هذا الشكل:
add_filter( 'posts_fields', 'posts_fields_custom_variable', 10, 2 );
function posts_fields_custom_variable( $fields, $query ) {
global $wpdb;
// Only if our custom variable is not false.
if ( $query->get( 'limit_fields' ) ) {
$fields = "$wpdb->posts.ID, $wpdb->posts.post_title";
}
return $fields;
}
$args = [
'post_type' => 'post',
'no_found_rows' => true,
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
'posts_per_page' => 5,
// Our custom variable.
'limit_fields' => true,
];
$the_query = new WP_Query( $args );
إذا كنا سنحتاج المعرف الوحيد فقط نستعمل البارمتر fields على هذا الشكل
$args = [
'fields' => 'ids',
];
في الحلقة Loop الأساسية للووردبريس، يتم استعمال الوظيفة update_post_thumbnail_cache()
للحصول على معلومات الصور المصغرة لكل مقالات ال Loop مرة واحدة فقط. تخزن تلك المعلومات في الكاش وتستخدم بعدها لعرض الصور المصغرة عندما يتم استدعاؤها، وبهذا يتم تجنب عمل استعلام إضافي لكل صورة مصغرة في ال Loop.
لكن للأسف هذه الوظيفة لا تستخدم افتراضيا في الاستعلامات المخصصة عبر WP_Query، ويمكننا إضافتها على هذا الشكل
<?php
$args = array(
'post_type' => 'post',
'post_per_page' => 5,
'no_found_rows' => true,
'update_post_term_cache' => false,
'suppress_filters' => true,
);
$the_query= new WP_Query( $args );
if( $the_query->have_posts() ) {
update_post_thumbnail_cache( $the_query);
echo '<ul>';
while( $the_query->have_posts() ) {
$the_query->the_post();
?>
<li>
<h3><?php the_title(); ?></h3>
<?php the_post_thumbnail( 'thumbnail' ); ?>
</li>
<?php
}
wp_reset_postdata();
echo '</ul>';
}
لكن بدل وضع update_post_thumbnail_cache()
داخل كل استعلام مخصص، يمكننا إضافة بارامتر مخصص نعطيه القيمة true في كل الاستعلامات التي ستحتوي على صور مصغرة.
add_filter( 'the_posts', 'sb_prime_post_thumbnails_cache', 10, 2 );
function sb_prime_post_thumbnails_cache( $posts, $query ) {
if( ! $query->is_main_query() && $query->get( 'sb_upadate_post_thumbnail_cache' ) ) {
update_post_thumbnail_cache( $query );
}
return $posts;
}
نستخدمه هكذا
$args = [
// Our custom parameter
sb_upadate_post_thumbnail_cache' =>; true,
];
$latest_news = new WP_Query( $args );
علينا أن نعلم أيضا أن الوظيفة get_posts()
تستخدم داخليا WP_Query
وتحدد بعض البارامترات بقيمة مناسبة ك 'no_found_rows' => true
، وكذلك تضع 'suppress_filters' => true
وهذا يستبعد نتائج الاستعلام من نظام التخزين المؤقت الخاص بووردبريس، لهذا لا تنسى تمريره بالقيمة false في حالة استعمالك ل get_posts()
ونفس الأمر مع wp_get_recent_posts()
و get_children()
.
ولكي تكون استعلاماتنا أسرع يجب علينا تجنب قدر الإمكان البحث في جداول متعددة، على سبيل المثال استعلامات حسب تصنيفات (taxonomy) متعددة، استعلامات حسب خصائص إضافة (post meta) متعددة وكذلك الإبتعاد عن البارامترات من نوع __in و not_in كسبيل المثال category__in
، category__not_in
، post__not_in
، tag__not_in
… الخ
وفي حالة ضرورة استعمالها من الواجب عمل تخزين مؤقت للاستعلامات.
التخزين المؤقت للإستعلامات في ووردبريس
ووردبريس يوفر لنا واجهتين برمجيتين تمكناننا من عمل تخزين مؤقت لاستعلاماتنا، الأولى WP Object Cache والأخرى Transients API.
الفرق بينها هو أن الأولى غير مستمرة (non-persistent) حيث يتم تخزين الاستعلام في الذاكرة العشوائية خلال مدة الاستعلام فقط، إذا أردت أن يتم عمل تخزين مستمر ( persistent caching ) عليك استخدام أحد الحلول ك W3 Total Cache، Memcached Object Cache، Redis Object Cache …
وهذا أفضل مثال للاستخدام WP Object Cache في WP_Query
$result = wp_cache_get( 'my_result' );
if ( false === $result ) {
$args = array(
'post_type' => 'post',
'tax_query' => array(
'relation' => 'OR',
array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => array( 'quotes' ),
),
array(
'relation' => 'AND',
array(
'taxonomy' => 'post_format',
'field' => 'slug',
'terms' => array( 'post-format-quote' ),
),
array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => array( 'wisdom' ),
),
),
),
);
$query = new WP_Query( $args );
wp_cache_set( 'my_result', $query );
}
أما Transients API فتقوم بتخزين نتيجة الإستعلام في قاعدة البيانات، بالتحديد في جدول wp_options
عوض تخزينه في الذاكرة، وبهذا يمكننا جلب نتيجة الاستعلام جاهزة في كل مرة وبالتالي تحسين كبير في الآداء وتخفيف العبء على الخادم.
مثال لاستخدام Transients API في WP_Query
$result = get_transient( 'my_result' );
if ( false === $result ) {
$args = array(
'post_type' => 'post',
'tax_query' => array(
'relation' => 'OR',
array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => array( 'quotes' ),
),
array(
'relation' => 'AND',
array(
'taxonomy' => 'post_format',
'field' => 'slug',
'terms' => array( 'post-format-quote' ),
),
array(
'taxonomy' => 'category',
'field' => 'slug',
'terms' => array( 'wisdom' ),
),
),
),
);
$query = new WP_Query( $args );
set_transient( 'my_result', $query, HOUR_IN_SECONDS );
}
في هذا المثال قمنا بتحديد مدة صلاحية التخزين المؤقت في ساعة من الزمن، ووردبريس يوفر ستة ثوابت لتحديد المدة الزمنية لكن يمكنك استخدام نماذج أخرى من عندك إذا أردت
MINUTE_IN_SECONDS = 60 (seconds)
HOUR_IN_SECONDS = 60 * MINUTE_IN_SECONDS
DAY_IN_SECONDS = 24 * HOUR_IN_SECONDS
WEEK_IN_SECONDS = 7 * DAY_IN_SECONDS
MONTH_IN_SECONDS = 30 * DAY_IN_SECONDS
YEAR_IN_SECONDS = 365 * DAY_IN_SECONDS
بالإضافة إلى عمل تخزين مؤقت لاستعلامات قاعدة البيانات، يمكننا أيضا تخزين الاستعلامات الخارجية، نعم، إذا كان موقعك يستقبل زيارات كثيرة ويعمل استعلامات على خدمات خارجة، فمن الأفضل تخزين تلك الاستعلامات ولو لمدة قصيرة.
على سبيل المثال، هناك زر فايسبوك يعرض عدد الإعجابات لكل تدوينة وفي كل زيارة يتم الاتصال ب API فايسبوك لجلب عدد الإعجابات، في هذه الحالة يمكننا مثلا تخزين النتيجة العائدة من فايسبوك في قاعدة البيانات لمدة 5 دقائق على سبيل المثال أو مدة أطول إذا كنت تفضل ذلك، وهذا سيكون له مردود جد إجابي على سرعة تحميل الموقع إذا كان يستقبل عدد كبير من الزيارات.
الكود في هذه الحالة سيكون كالتالي:
$fb_count = get_transient('facebook_count');
if ( false === $fb_count ) {
$return = wp_remote_get( 'http://graph.facebook.com/?id=http://www.mysite.com', array( 'timeout' => 10 ) );
$json = json_decode($return["body"], true);
$fb_count = isset($json["shares"]) ? intval($json["shares"]) : 0;
set_transient('facebook_count', $fb_count, 5 * MINUTE_IN_SECONDS);
}
باستعمال الخطوات المذكورة في هذه التدوينة يمكننا تحسين اداء القالب أو الإضافة التي نعمل عليها بشكل جد ملحوظ، نحن نتكلم عن تحسين الأداء بأجزاء من الثانية، لن يلاحظ إذا كانت الزيارات قليلة لكن إذا كانت الزيارات بالآلاف أجزاء الثانية تتضاعف أيضا بالآلاف …
شكرا لك على الوصول إلى نهاية التدوينة 🙂
عيسى قال
مقال ودرس أكثر من ممتاز،
استفدت الكثير من المعلومات القيمة الواردة فيه.
سعيد البقالي قال
شكرا لك على التشجيع وعلى مرورك العطر أخي عيسى 🙂
أمين قال
مقال جد رائع شكرا على المشاركة.
سعيد البقالي قال
العفو أمين، شكرا لك على الزيارة والتعليق 🙂
mouad harmouche قال
مقال رائع جدا اخي
سعيد البقالي قال
يفرحني أنه أعجبك أخي معاذ 😃
حسين قال
السلام عليكم و رحمة الله و بركاته
الاخ العزيز سعيد اخي كفيت و وفيت بالمعلومة القيمة بس المشكلة نحن عادنا ما وصلنا هذه المراحل حتي ما ندري وين نطرح او نخزن علي الاكواد التي شرحتها ههههههههههه ولكن سيأتي يومه انشاء الله و شكرا يا صديقي الله يسعدك و يجزيك بالخير
سعيد البقالي قال
وعليكم السلام أخي حسين، نسيت أن أذكر في التدوينة أن هذا الموضوع موجه بشكل أساسي للمطوين، إن شاء الله ستكون هناك شرحات لشتى المستويات 🙂
تحياتي لك
حسين قال
حيا الله بالاخ الغالي اخي لا بأس لا تخصص الدروس خلي المجال يكون مفتوح للجميع لان كل واحد يعرف مجاله و هو يختار بنفسه ما يناسبه في الموقع و المثل يقول تعب اليوم هو سلاح الغد و شكرا علي الرسالة
والسلام
عمر قال
الله يعطيك العافيه على المعلومات القيمة .
هل تقصد خانة
وهذا أفضل مثال للإستخدام WP Object Cache في WP_Query
اقوم بوضعها داخل ملف الفنكشن للقالب ووردبريس وسوف يتم التخزين وكيفية تحديد الوقت للكاش للاستعلامات ؟
اذا تكرمت تضع الكود كامل للفائدة .
سعيد البقالي قال
الله يعافيك أخي عمر
المثال يظهر كيفة تطبيق WP Object Cache على استعلام مخصص بواسطة [code lang=”php” inline=”true”]WP_Query[/code]، يعني أن الكود يتغير حسب الإستعلام الذي تريد عمله ولا يمكن إعطاء كود معيّن لووضه في ملف [code lang=”php” inline=”true”]functions.php[/code] …
تحياتي.
نور قال
مقال جد غني بالمعلومات و مفيد
شكرا جزيلا على التدوينة
اقدر مجهوداتك
Happy قال
مقال جد رائع شكرا على المعلومات القيمة ,
المزيد من التوفيق ان شاء الله .