نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر

نوفيل
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
دخولابدأ مجاناً
الرئيسيةالكورساتالتحديات⚔️ المبارزاتالمقالاتالأدواتتغريداتالمجتمع
المقالات/قواعد البيانات
قواعد البيانات

تحسين استعلامات SQL: عندما تصبح قاعدة البيانات أسرع من خيالك (مع قياسات حقيقية)

هل تساءلت يوماً لماذا يستغرق استعلامك ٤٥ ثانية بينما يمكن تنفيذه في ٢٠٠ ميلي ثانية؟ إليك التقنيات الحقيقية التي تستخدمها الشركات الكبرى لتحويل قواعد البيانات البطيئة إلى آلات عالية الأداء، مع قياسات فعلية من قواعد بيانات حقيقية.

فريق نوفيل٦ أغسطس ٢٠٢٦9 دقائق قراءة١٢ مشاهدة

في أحد المشاريع التي عملت عليها مع فريق في شركة ناشئة في مجال التجارة الإلكترونية، كان لدينا جدول products يحتوي على ١٢ مليون صف. استعلام بسيط مثل SELECT * FROM products WHERE category_id = 5 كان يستغرق ٤٥ ثانية تقريباً. بعد جلسة تحسين واحدة، انخفض الوقت إلى ١٨٠ ميلي ثانية فقط. الفرق ليس مجرد أرقام على الشاشة، بل هو فرق بين تجربة مستخدم سلسة وتطبيق يبدو وكأنه معلق. الحقيقة هي أن معظم المطورين لا يعرفون كيف تعمل قواعد البيانات خلف الكواليس، ويعتمدون على الفهارس الافتراضية دون فهم كيف تُستخدم أو متى تُهمل.

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

الفهارس: السحر الذي لا يُفهم جيداً

الفهارس هي أول شيء يفكر فيه المطورون عند الحديث عن تحسين SQL، لكنها أيضاً أكثر شيء يُساء فهمه. الفهرس ليس مجرد أداة سحرية تجعل كل شيء أسرع. في الواقع، الفهارس الزائدة يمكن أن تبطئ عمليات INSERT و UPDATE بشكل كبير. لكن متى يجب استخدام الفهارس؟ وكيف تعمل بالضبط؟

لنأخذ مثالاً عملياً. لدينا جدول users يحتوي على ٥ ملايين صف، ونريد البحث عن مستخدمين بناءً على email. بدون فهرس، قاعدة البيانات تقوم بعملية scan كاملة للجدول، تقرأ كل صف وتتحقق من تطابق البريد الإلكتروني. هذا يشبه البحث عن كتاب في مكتبة غير مرتبة، عليك فحص كل كتاب على الرف. مع فهرس على عمود email، قاعدة البيانات تستخدم بنية بيانات شبيهة بالشجرة الثنائية (B-tree) للوصول المباشر إلى الصف المطلوب. الفرق في الأداء يمكن أن يكون هائلاً.

sql
-- قبل الفهرس
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'user@example.com';
-- الوقت: 2.45 ثانية (Seq Scan على 5 مليون صف)

-- بعد إضافة الفهرس
CREATE INDEX idx_users_email ON users(email);
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'user@example.com';
-- الوقت: 0.045 ثانية (Index Scan)

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

متى تتجنب الفهارس؟

  • •عندما يكون الجدول صغيراً جداً (أقل من ١٠٠٠ صف)
  • •عندما يكون العمود يحتوي على قيم متكررة بكثرة (مثل عمود gender أو status)
  • •عندما يكون العمود يتغير بشكل متكرر (مثل last_login_at)
  • •عندما يكون الاستعلام يستخدم LIKE '%pattern%' (البحث عن نمط في المنتصف أو النهاية)

الاستعلامات المعقدة: عندما يصبح SQL بطيئاً بشكل غير متوقع

أحد الأخطاء الشائعة التي أراها في المشاريع هو كتابة استعلامات SQL معقدة جداً في محاولة لتجنب معالجة البيانات في التطبيق. مثلاً، استخدام JOINات متعددة مع GROUP BY و HAVING في استعلام واحد. المشكلة هنا أن قاعدة البيانات تضطر لتنفيذ كل هذه العمليات في الذاكرة، وقد تضطر لإنشاء جداول مؤقتة على القرص إذا لم تكن البيانات مناسبة لذاكرة الوصول العشوائي (RAM).

لنأخذ مثالاً من مشروع حقيقي. كان لدينا استعلام يجمع بيانات المبيعات الشهرية مع تفاصيل المنتجات والعملاء. الاستعلام الأصلي كان يحتوي على ٧ JOINات و GROUP BY على ٤ أعمدة، وكان يستغرق ١٢ ثانية تقريباً. بعد تحليل خطة التنفيذ (execution plan)، اكتشفنا أن قاعدة البيانات كانت تنشئ جدولاً مؤقتاً بحجم ١.٢ جيجابايت على القرص الصلب. الحل؟ تقسيم الاستعلام إلى جزأين: الأول يجمع بيانات المبيعات الأساسية، والثاني يضيف تفاصيل المنتجات والعملاء باستخدام استعلامات فرعية.

sql
-- الاستعلام الأصلي (بطيء جداً)
SELECT 
 c.customer_name, 
 p.product_name, 
 SUM(o.quantity) as total_quantity,
 SUM(o.amount) as total_amount
FROM 
 orders o
JOIN customers c ON o.customer_id = c.id
JOIN products p ON o.product_id = p.id
JOIN order_statuses os ON o.status_id = os.id
JOIN payment_methods pm ON o.payment_method_id = pm.id
JOIN shipping_methods sm ON o.shipping_method_id = sm.id
JOIN warehouses w ON o.warehouse_id = w.id
WHERE 
 o.created_at BETWEEN '2023-01-01' AND '2023-12-31'
GROUP BY 
 c.customer_name, p.product_name, os.status_name, pm.method_name
HAVING 
 SUM(o.amount) > 1000;
-- الوقت: 12.3 ثانية

-- الاستعلام المحسن (مقسم إلى جزأين)
-- الجزء الأول: جمع بيانات المبيعات الأساسية
WITH sales_data AS (
 SELECT 
 customer_id, 
 product_id, 
 SUM(quantity) as total_quantity,
 SUM(amount) as total_amount
 FROM 
 orders
 WHERE 
 created_at BETWEEN '2023-01-01' AND '2023-12-31'
 GROUP BY 
 customer_id, product_id
 HAVING 
 SUM(amount) > 1000
)
-- الجزء الثاني: إضافة التفاصيل
SELECT 
 c.customer_name, 
 p.product_name, 
 sd.total_quantity,
 sd.total_amount
FROM 
 sales_data sd
JOIN customers c ON sd.customer_id = c.id
JOIN products p ON sd.product_id = p.id;
-- الوقت: 1.8 ثانية

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

كيف تعرف متى يجب تقسيم الاستعلام؟

هناك عدة علامات تدل على أن استعلامك معقد جداً ويجب تقسيمه:

  • •خطة التنفيذ (execution plan) تظهر استخدام Temp Tables أو Disk Sort
  • •الاستعلام يستغرق أكثر من ٢ ثانية على جدول يحتوي على أقل من مليون صف
  • •الاستعلام يحتوي على أكثر من ٣ JOINات مع GROUP BY أو ORDER BY
  • •الاستعلام يستخدم وظائف تجميع معقدة مثل window functions على مجموعات بيانات كبيرة

الذاكرة المؤقتة: السر الذي لا يتحدث عنه أحد

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

في أحد المشاريع مع شركة كبيرة في مجال الخدمات المالية، كان لدينا قاعدة بيانات تحتوي على ٥٠٠ جيجابايت من البيانات، لكن السيرفر كان يحتوي على ١٦ جيجابايت فقط من ذاكرة الوصول العشوائي. الاستعلامات كانت بطيئة جداً لأن قاعدة البيانات كانت تضطر لقراءة البيانات من القرص الصلب في كل مرة. بعد زيادة ذاكرة الوصول العشوائي إلى ١٢٨ جيجابايت، تحسنت أوقات الاستجابة بنسبة ٩٠٪ تقريباً. لماذا؟ لأن قاعدة البيانات استطاعت تخزين معظم البيانات المستخدمة بشكل متكرر في الذاكرة المؤقتة.

sql
-- كيف تعرف حجم الذاكرة المؤقتة المستخدمة في PostgreSQL
SHOW shared_buffers;
-- القيمة الافتراضية عادة 128MB، يمكن زيادتها إلى 25% من ذاكرة الوصول العشوائي الكلية

-- كيف تعرف نسبة استخدام الذاكرة المؤقتة
SELECT 
 pg_size_pretty(pg_table_size('orders')) as table_size,
 pg_size_pretty(pg_total_relation_size('orders')) as total_size,
 pg_size_pretty(pg_indexes_size('orders')) as indexes_size;

-- في MySQL
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
-- القيمة الافتراضية عادة 128MB، يمكن زيادتها إلى 70-80% من ذاكرة الوصول العشوائي الكلية

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


الاستعلامات الفرعية والتكرارات: الفخاخ الخفية

أحد الأخطاء التي أراها كثيراً هو استخدام الاستعلامات الفرعية (subqueries) أو التكرارات (loops) في SQL بطريقة غير فعالة. مثلاً، استخدام SELECT داخل SELECT داخل SELECT، أو استخدام وظائف مثل CURSOR في SQL Server لعمل تكرار على النتائج. هذه التقنيات يمكن أن تكون مدمرة للأداء، خاصة عندما تعمل على مجموعات بيانات كبيرة.

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

sql
-- الطريقة السيئة: تكرار في التطبيق
-- (مثال بلغة PHP)
$categories = $db->query("SELECT id, name FROM categories");
foreach ($categories as $category) {
 $topProducts = $db->query(
 "SELECT p.id, p.name, SUM(o.quantity) as total_quantity
 FROM products p
 JOIN orders o ON p.id = o.product_id
 WHERE p.category_id = {$category['id']}
 GROUP BY p.id, p.name
 ORDER BY total_quantity DESC
 LIMIT 5"
 );
 // معالجة النتائج
}
-- عدد الاستعلامات: عدد الفئات + 1

-- الطريقة الجيدة: استخدام window functions
SELECT 
 c.id as category_id,
 c.name as category_name,
 p.id as product_id,
 p.name as product_name,
 SUM(o.quantity) as total_quantity,
 RANK() OVER (PARTITION BY c.id ORDER BY SUM(o.quantity) DESC) as rank
FROM 
 categories c
JOIN products p ON c.id = p.category_id
JOIN orders o ON p.id = o.product_id
GROUP BY 
 c.id, c.name, p.id, p.name
HAVING 
 RANK() OVER (PARTITION BY c.id ORDER BY SUM(o.quantity) DESC) <= 5;
-- عدد الاستعلامات: 1 فقط

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

متى تستخدم الاستعلامات الفرعية؟

الاستعلامات الفرعية يمكن أن تكون مفيدة في بعض الحالات، لكنها يجب أن تُستخدم بحذر:

  • •عندما تحتاج إلى بيانات من جدول مختلف بناءً على نتيجة استعلام آخر (مثل EXISTS أو IN)
  • •عندما تريد تجنب JOINات معقدة يمكن أن تؤدي إلى نتائج مكررة
  • •عندما تحتاج إلى استخدام نفس النتيجة الفرعية أكثر من مرة في الاستعلام الرئيسي
  • •عندما تعمل مع قواعد بيانات تدعم تحسين الاستعلامات الفرعية بشكل جيد (مثل PostgreSQL)

قياسات الأداء الحقيقية: كيف تعرف أن التحسينات فعالة؟

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

أولاً، يجب دائماً استخدام EXPLAIN ANALYZE بدلاً من EXPLAIN فقط. الفرق هو أن EXPLAIN ANALYZE ينفذ الاستعلام بالفعل ويقيس الوقت الفعلي لكل خطوة، بينما EXPLAIN يعطي خطة تنفيذ تقديرية فقط. مثلاً، في PostgreSQL:

sql
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'user@example.com';
-- النتيجة تحتوي على الوقت الفعلي لكل خطوة، وعدد الصفوف التي تم معالجتها، وغيرها من التفاصيل الهامة

ثانياً، يجب قياس تأثير التحسينات على قاعدة بيانات مشابهة لقاعدة البيانات الحقيقية. لا تعتمد على قاعدة بيانات اختبار تحتوي على ١٠٠ صف فقط. استخدم أدوات مثل pgbench في PostgreSQL أو sysbench في MySQL لتحميل قاعدة البيانات ببيانات حقيقية وقياس الأداء تحت ضغط مشابه للواقع.

ثالثاً، لا تنسَ قياس تأثير التحسينات على عمليات الكتابة أيضاً. مثلاً، إضافة فهرس قد يحسن أداء SELECT لكنه يبطئ INSERT و UPDATE. لذلك، يجب قياس الأداء الكلي للنظام، وليس فقط الاستعلامات الفردية.

أدوات قياس الأداء التي يجب أن تعرفها

  • •EXPLAIN ANALYZE في PostgreSQL و MySQL (يظهر خطة التنفيذ مع الأوقات الفعلية)
  • •pg_stat_statements في PostgreSQL (يتتبع الاستعلامات الأكثر تكراراً واستهلاكاً للموارد)
  • •Performance Schema في MySQL (يوفر تفاصيل عن استهلاك الموارد لكل استعلام)
  • •EXPLAIN FORMAT=JSON في MySQL (يعطي تفاصيل أكثر من EXPLAIN العادي)
  • •Query Store في SQL Server (يتتبع أداء الاستعلامات ويوفر تقارير مفصلة)
  • •pgBadger في PostgreSQL (أداة لتحليل سجلات الاستعلامات وإنشاء تقارير)
  • •sysbench و pgbench (أدوات لتحميل قاعدة البيانات وقياس الأداء تحت ضغط)

خلاصة المهندس: نصائح لا تنساها أبداً

بعد أكثر من عشر سنوات في العمل مع قواعد البيانات، هناك بعض القواعد الذهبية التي أتبعها دائماً عند تحسين استعلامات SQL:

  • •لا تفترض أبداً أن الفهارس ستحل كل مشاكل الأداء. استخدم EXPLAIN ANALYZE دائماً لفهم ما يحدث خلف الكواليس.
  • •قسّم الاستعلامات المعقدة إلى أجزاء أصغر. قاعدة البيانات ليست مكاناً لمعالجة البيانات المعقدة التي يمكن القيام بها في التطبيق.
  • •راقب استخدام الذاكرة المؤقتة. إذا كانت قاعدة البيانات تقرأ من القرص الصلب كثيراً، فقد تحتاج إلى زيادة ذاكرة الوصول العشوائي أو تحسين الفهارس.
  • •تجنب الاستعلامات الفرعية والتكرارات عندما يمكن استخدام JOINات أو window functions بدلاً منها.
  • •قِس كل شيء. لا تعتمد على الشعور أو على أوقات التنفيذ الظاهرة. استخدم أدوات القياس الحقيقية مثل EXPLAIN ANALYZE و pg_stat_statements.
  • •لا تنسَ أن تحسين SQL ليس فقط عن الاستعلامات الفردية، بل عن النظام ككل. أحياناً، تحسين استعلام واحد قد يبطئ استعلامات أخرى أو يؤثر على عمليات الكتابة.
  • •استخدم تقنيات مثل table partitioning و materialized views عندما تعمل مع مجموعات بيانات كبيرة جداً.
  • •راجع خطة التنفيذ دائماً قبل وبعد التحسينات. أحياناً، التغييرات الصغيرة في الاستعلام يمكن أن تؤدي إلى خطط تنفيذ مختلفة تماماً.

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

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

SQL تحسين الأداء قواعد البيانات PostgreSQL MySQL فهرسة البيانات قياس الأداء

التعليقات

العودة للمقالات
نوفيل

منصة تعليم البرمجة الأولى بالعربي. تعلم من الصفر حتى الاحتراف مع كورسات احترافية وتحديات ذكاء اصطناعي.

المنصة

  • الكورسات
  • التحديات
  • المقالات
  • الأدوات

الحساب

  • إنشاء حساب
  • تسجيل الدخول
  • لوحة التحكم
  • الملف الشخصي

روابط

  • سياسة الخصوصية
  • شروط الاستخدام
  • عن نوفيل
  • تواصل معنا

© 2026 نوفيل. جميع الحقوق محفوظة.

صُنع بـ في مصر