Table Partitioning في PostgreSQL: من 4.2 ثانية لـ 12 مللي ثانية على جدول 80 مليون صف
مستوى المقال: متوسط — يفترض إنك تعرف CREATE TABLE، EXPLAIN ANALYZE، والفرق بين Sequential Scan و Index Scan. لو لسه مبتدئ في PostgreSQL، ابدأ بمقال B-Tree Indexes الأول.
لو جدول orders عندك بقى 80 مليون صف وأي تقرير على آخر 30 يوم بياخد 4.2 ثانية حتى مع index على created_at، المشكلة مش في الـ DB. المشكلة إن PostgreSQL لازم يفتح index ضخم (حوالي 2.4GB) ويعمل random reads على القرص. Partitioning بيخلّي نفس الـ query يقرأ partition واحد بحجم 2.6 مليون صف ويرد في 12 مللي ثانية.
المثال الأبسط: أرشيف الفواتير في محل خردوات
تخيّل محل خردوات شغّال من 7 سنين، وكل فاتورة بتترمي في كرتونة واحدة كبيرة في المخزن. لما المحاسب يطلب فواتير شهر مارس 2026، الموظف بيقعد يقلّب 80 ألف فاتورة من السبع سنين علشان يطلع 2,500 فاتورة بس. ساعتين شغل.
الحل اللي أي محاسب شاطر هيعمله: 84 كرتونة (شهر لكل سنة)، كل كرتونة عليها لاصق "مارس 2026"، "فبراير 2026"، وهكذا. لما المحاسب يطلب مارس، الموظف بيمشي على الكرتونة دي بالظبط ويرجع في 5 دقائق.
ده بالظبط اللي Partitioning بيعمله. الجدول الكبير بيتقسّم لـ "كراتين" أصغر (partitions)، وPostgreSQL بيختار الـ partition الصح قبل ما يبدأ أصلاً يقرأ.
التعريف العلمي: Declarative Partitioning
من توثيق PostgreSQL 16 الرسمي (قسم 5.11): "Partitioning refers to splitting what is logically one large table into smaller physical pieces." الجدول الأصلي بيبقى parent table فاضي من غير بيانات، والبيانات الحقيقية بتعيش في child partitions. لما تكتب SELECT على الـ parent، Planner بيستخدم خاصية اسمها partition pruning علشان يستبعد الـ partitions اللي مش هتطابق شرط WHERE.
PostgreSQL 10 (2017) أضاف Declarative Partitioning بصيغة PARTITION BY. قبله، الناس كانت بتستخدم Table Inheritance + Triggers يدويًا، وكان مؤلم. PostgreSQL 16 (سبتمبر 2023) حسّن partition pruning في المرحلة التنفيذية، وبقى يدعم Logical Replication على الـ partitions مباشرة.
أنواع Partitioning الثلاثة:
- RANGE — تقسيم على مدى قيمة (الأكثر شيوعًا، مثلاً تاريخ).
- LIST — تقسيم على قيمة محدّدة (مثلاً
country_code). - HASH — تقسيم بالـ hash على عمود (لتوزيع موحّد عند عدم وجود بُعد طبيعي).
الكود التنفيذي: تقسيم جدول الطلبات على RANGE شهري
الـ stack المفترض: PostgreSQL 16، جدول الحالي 80 مليون صف، الحقل بتاريخ من 2019 لـ 2026. هنعمل partition شهري: