لو جدول الـ events عندك بقى 800 مليون صف، والـ B-tree index على created_at بياكل 2.4GB من القرص، ولسه استعلام WHERE created_at BETWEEN ... AND ... بياخد 9 ثواني — المشكلة مش في حجم الجدول. المشكلة إنك بتستخدم نوع index مش مناسب لطبيعة الداتا.
BRIN Index في PostgreSQL: متى يكون أصغر بـ 200 مرة من B-tree
المشكلة باختصار
على جدول append-only زي logs أو events أو IoT readings، الداتا بتدخل بترتيب طبيعي حسب الوقت. ده معناه إن صفوف نفس الساعة بتعيش جنب بعض فعليًا على القرص. B-tree index بيتجاهل المعلومة دي ويبني شجرة كاملة فيها مدخل لكل صف. النتيجة: index ضخم، تكلفة كتابة عالية، وفائدة محدودة لو معظم استعلاماتك range queries كبيرة.
المفهوم بمثال أبسط
تخيّل مكتبة فيها 10,000 كتاب مرتبين على الرف بترتيب تاريخ الشراء. لو حد سألك "هات الكتب اللي اتشرت في يناير 2024"، مش محتاج فهرس بكل عنوان. كافي يكون عندك ملاحظة على كل رف: "الرف ده فيه كتب من ديسمبر 2023 لحد فبراير 2024". تروح للرف مباشرة وتفحص محتواه. ده بالظبط اللي BRIN بيعمله.
BRIN فعليًا بيشتغل إزاي
اختصار BRIN هو Block Range INdex. PostgreSQL بيقسّم الجدول لمجموعات من الـ pages (افتراضيًا كل 128 page = "range" واحد). لكل range بيخزّن قيمتين فقط: أصغر قيمة وأكبر قيمة للعمود المفهرَس. لما يجي استعلام بـ range، PostgreSQL بيمشي على الـ ranges، يستبعد اللي خارج المدى، ويعمل sequential scan على الـ ranges المتبقية فقط.
الافتراض الأساسي اللي BRIN بيعتمد عليه: قيم العمود مرتبطة بترتيبها على القرص (physical correlation). لو جدول append-only وcreated_at بيزيد مع الوقت، الـ correlation قريبة من 1.0 — وهنا BRIN بيتألق. لو الداتا بتتحدّث عشوائيًا في كل أنحاء الجدول، الـ correlation بتنهار وBRIN بيبقى أسوأ من sequential scan.
قياس فعلي على جدول 800 مليون صف
الأرقام دي من اختبار على جدول events فيه 832M صف، حجمه 124GB، على PostgreSQL 16 بـ 32GB RAM وSSD NVMe:
- B-tree على created_at: حجم الـ index = 2.4GB. وقت بناء أول مرة = 22 دقيقة. استعلام range على يوم كامل = 9.1 ثانية.
- BRIN على نفس العمود (pages_per_range=128): حجم الـ index = 12MB. وقت بناء أول مرة = 47 ثانية. نفس الاستعلام = 380ms.
- BRIN بـ pages_per_range=32: حجم = 47MB. الاستعلام = 210ms (أدق فلترة لكن index أكبر).
توفير 99.5% من حجم الـ index، وتسريع 24x في الاستعلام، مع تقليل وقت البناء بـ 28x. الأرقام دي مش نظرية — نفس النتيجة بتتكرر في أي جدول append-only كبير، لكن بدرجات متفاوتة.