المستوى المطلوب: محترف — يفترض المقال إنك مرتاح مع PostgreSQL و EXPLAIN ANALYZE و triggers، وإن جدولك فيه ملايين الصفوف والـ autovacuum شغّال.
ليه SELECT COUNT(*) بطيء على الملايين، وإزاي تعدّه في مللي ثانية
لو صفحة لوحة التحكم بتعلّق ثانيتين أو تلاتة عشان بتعرض إجمالي عدد الصفوف، المشكلة مش في السيرفر ولا في الشبكة. المشكلة إن SELECT COUNT(*) على جدول فيه 50 مليون صف بيعمل فحص تسلسلي كامل. هتتعلّم هنا تنزّل الزمن من ~9.4 ثانية لـ ~3 مللي ثانية، والأهم: إمتى التقدير ده مقبول وإمتى لأ.
المشكلة باختصار
العدّ الإجمالي بيظهر في أماكن كتير: ترويسة صفحة إدارة، عدّاد نتائج بحث، أو حساب عدد الصفحات في الـ pagination. كل ما الجدول يكبر، العدّ بيبطأ خطيًا. النتيجة: صفحة بتحمّل في 9 ثواني بدل جزء من الثانية، والمستخدم بيفتكر الموقع واقع.
ليه COUNT(*) بطيء أصلًا
خلّينا نقرّبها بمثال الأول. تخيّل استاد فيه 50 ألف متفرج، وحد سألك: كام واحد قاعد دلوقتي بالظبط؟ مفيش رقم مخزّن جاهز، فمضطر تعدّ راس راس. لكن شبّاك التذاكر عنده رقم التذاكر المباعة، وده تقدير قريب جدًا في جزء من الثانية.
علميًا، PostgreSQL بيعمل نفس الحكاية. مفيش رقم إجمالي واحد مخزّن للجدول، والسبب هو نموذج التزامن MVCC. كل معاملة (transaction) ممكن تشوف مجموعة صفوف مختلفة حسب الـ snapshot بتاعها. فعلشان يجاوب على COUNT(*) بدقة، لازم يمرّ على كل صف ويتأكد إنه مرئي للمعاملة الحالية. ده بيخلّي التكلفة O(n). حتى لو استخدم index-only scan، هو لسه بيقرأ الفهرس كله، فبيبقى أسرع من فحص الـ heap بس لسه خطّي.
الحلول مرتّبة بالأولوية
- تقدير فوري بـ reltuples للأعداد الإجمالية اللي تقبل خطأ بسيط.
- تقدير المخطِّط (Plan Rows) لعدد صفوف استعلام مُفلتر بدون ما تنفّذه.
- عدّاد مُصان بـ trigger لما تحتاج رقم دقيق بقراءة
O(1).
1) التقدير الفوري من إحصائيات المخطِّط
-- الطريقة البطيئة: فحص كامل على 50 مليون صف
EXPLAIN ANALYZE SELECT COUNT(*) FROM events;
-- Seq Scan on events ... actual time=9411.802..9411.803
-- التقدير الفوري: رقم reltuples من كتالوج النظام
SELECT reltuples::bigint AS estimated_rows
FROM pg_class
WHERE relname = 'events';
-- ~50000000 (زمن التنفيذ أقل من 3ms)
-- reltuples بيتحدّث مع ANALYZE و autovacuum. لو الرقم قديم، حدّثه:
ANALYZE events;الرقم ده بيتحدّث كل ما autovacuum يشغّل ANALYZE. دقته عادة في حدود 1% إلى 5% على جدول نشِط، بس ممكن ينحرف كتير مباشرة بعد إدخال دفعة كبيرة قبل ما الإحصائيات تتحدّث.