20 ديسمبر 2025·7 دقيقة قراءة

مؤشرات B-tree مقابل GIN مقابل GiST: دليل عملي لـ PostgreSQL

B-tree مقابل GIN مقابل GiST: استخدم جدول قرار لاختيار المؤشر الصحيح في PostgreSQL للمرشحات، البحث، حقول JSONB، استعلامات الجغرافيا، والأعمدة عالية التفرّد.

مؤشرات B-tree مقابل GIN مقابل GiST: دليل عملي لـ PostgreSQL

ما الذي تختاره فعلاً عند اختيار مؤشر

تبدأ معظم مشاكل مؤشرات PostgreSQL بنفس الطريقة: عرض قائمة يبدو سريعًا عند 1,000 صف، ثم يصبح بطيئًا عند 1,000,000. أو مربع بحث عمل أثناء الاختبار لكنه يتحول إلى توقف لثانية في الإنتاج. عندما يحدث ذلك، من المغري أن تسأل: “أي مؤشر هو الأفضل؟” سؤال أوضح هو: “ما الذي تطلب هذه الشاشة من قاعدة البيانات فعله؟”

قد يحتاج الجدول نفسه إلى أنواع مؤشرات مختلفة لأن الشاشات تقرأه بطرق مختلفة. عرض واحد يفلتر بحسب حالة واحدة ويُرتّب بـ created_at. آخر يقوم بالبحث النصي الكامل. آخر يتفقد ما إذا كان حقل JSON يحتوي على مفتاح. آخر يجد عناصر بالقرب من نقطة على الخريطة. هذه أنماط وصول مختلفة، لذا لا يوجد نوع مؤشر واحد سيفوز في كل الحالات.

هذا ما تختاره عند اختيار مؤشر: كيف يصل التطبيق إلى البيانات. هل تقوم أساسًا بمطابقات دقيقة، نطاقات، وترتيب؟ هل تبحث داخل مستندات أو مصفوفات؟ هل تسأل «ما القريب من هذا الموقع» أم «ما الذي يتداخل مع هذا النطاق»؟ الجواب يحدد ما إذا كان B-tree أو GIN أو GiST هو الأنسب.

B-tree و GIN و GiST بلغة بسيطة

اختيار مؤشر يتعلق أكثر بما تفعله استعلاماتك مع العمود منه بنوع العمود. PostgreSQL يختار المؤشرات اعتمادًا على المشغِّلات مثل =, <, @>, أو @@، وليس على ما إذا كان العمود "نصًا" أو "json". لهذا يمكن أن يحتاج الحقل نفسه إلى مؤشرات مختلفة على شاشات مختلفة.

B-tree: سريع لعمليات البحث المرتبة

B-tree هو الافتراضي والأكثر شيوعًا. يبرع عندما تَفلتر بقيمة دقيقة، تَفلتر بنطاق، أو تحتاج نتائج بترتيب محدد.

مثال نموذجي هو قائمة إدارية مُفلترة بالحالة ومرتبة بـ created_at. مؤشر B-tree على (status, created_at) يمكنه مساعدة كل من الفلترة والفرز. B-tree هو أيضًا الأداة المعتادة للقيود الفريدة (unique constraints).

GIN: سريع عندما يحتوي كل صف على مفاتيح عديدة قابلة للبحث

تم تصميم GIN لأسئلة "هل يحتوي هذا الصف على هذا المصطلح/القيمة؟"، حيث يمكن أن يطابق الصف الواحد العديد من المفاتيح. أمثلة شائعة هي البحث النصي الكامل (المستند يحتوي كلمات) وعضوية JSONB/المصفوفات (JSON يحتوي على مفتاح/قيمة).

تخيل سجل عميل به كائن JSONB preferences، وشاشة تَفلتر للمستخدمين حيث preferences تحتوي {\"newsletter\": true}. هذا نمط بحث يناسب GIN.

GiST: مرن للنطاقات، الجغرافيا، والتشابه

GiST إطار عام يُستخدم لأنواع بيانات لا تناسب الترتيب البسيط. يناسب النطاقات (التداخل، الاحتواء)، الاستعلامات الهندسية والجغرافية (قريب، داخل)، وبعض عمليات البحث بالتشابه.

عند المقارنة بين B-tree و GIN و GiST، ابدأ بكتابة المشغِّلات التي تستخدمها شاشاتك الأكثر ازدحامًا. المؤشر الصحيح سيصبح أوضح بعد ذلك.

جدول قرار للشاشات الشائعة (المرشحات، البحث، JSON، الجغرافيا)

معظم التطبيقات تحتاج فقط بعض أنماط المؤشرات. الحيلة هي مطابقة سلوك الشاشة مع المشغِّلات التي تستخدمها الاستعلامات.

نمط الشاشةشكل الاستعلام النموذجينوع المؤشر الأفضلمشغل(ات) مثال
مرشحات بسيطة (status, tenant_id, email)العديد من الصفوف، تضييق بالمعادلةB-tree= IN (...)
فلترة نطاقية للتاريخ/الرقمنافذة زمنية أو min/maxB-tree>= <= BETWEEN
فرز + ترقيم صفحات (خلاصة، قائمة إدارية)فلترة ثم ORDER BY ... LIMITB-tree (غالبًا مركب)ORDER BY created_at DESC
عمود عالي التفرّد (user_id, order_id)استعلامات انتقائية جدًاB-tree=
مربع بحث نصي كاملبحث نص عبر حقلGIN@@ على tsvector
بحث "يحتوي" على نصتطابق جزء من السلسلة مثل "%term%"عادة لا يوجد (أو إعداد trigram خاص)LIKE '%term%'
JSONB احتواء (tags, flags, properties)مطابقة شكل JSON أو مفتاح/قيمةGIN على jsonb@>
مساواة مفتاح JSONB واحدفلترة بمفتاح JSON متكرر كثيرًاB-tree مستهدف على تعبير(data->>'plan') = 'pro'
قرب جغرافي / داخل نصف القطر"قريب مني" وعروض الخريطةGiST (PostGIS geometry/geography)ST_DWithin(...) <->
نطاقات، تداخل (جداول، نطاقات تسعير)فحوص تداخل الفتراتGiST (أنواع النطاق)&&
فلتر منخفض الانتقائية (boolean، enums صغيرة)معظم الصفوف تطابق القيمةالمؤشر غالبًا مفيد قليلًاis_active = true

يمكن لمؤشرين التعايش عندما تختلف نقاط النهاية. على سبيل المثال، قد تحتاج القائمة الإدارية مؤشر B-tree على (tenant_id, created_at) للفرز السريع، بينما صفحة البحث تحتاج GIN لـ @@. احتفظ بكليهما فقط إذا كانت أشكال الاستعلام شائعة.

إذا كنت غير متأكد، انظر أولًا إلى المشغل. تساعد المؤشرات عندما تستطيع قاعدة البيانات استخدامها لتجاوز أجزاء كبيرة من الجدول.

المرشحات والترتيب: حيث يفوز B-tree عادة

بالنسبة لمعظم الشاشات اليومية، B-tree هو الاختيار الممل الذي يعمل. إذا كان استعلامك يبدو كـ "اختَر الصفوف حيث العمود يساوي قيمة، ربما فرزهم، ثم عرض الصفحة الأولى"، فعادةً ما يكون B-tree هو أول ما تجربيه.

المرشحات بالمعادلة هي الحالة الكلاسيكية. أعمدة مثل status, user_id, account_id, type, أو tenant_id تظهر باستمرار في لوحات التحكم والقوائم الإدارية. يمكن لمؤشر B-tree القفز مباشرة إلى القيم المطابقة.

تنطبق فلترات النطاق أيضًا جيدًا على B-tree. عندما تُصفّي بالوقت أو النطاقات الرقمية، تساعد البنية المرتبة: created_at >= ..., price BETWEEN ..., id > .... إن كانت واجهة المستخدم تقدم "آخر 7 أيام" أو "$50 إلى $100"، فـ B-tree يفعل ما تريد بالضبط.

الفرز والترقيم الصفحي حيث يمكن لـ B-tree توفير أكبر فائدة. إذا كان ترتيب الفهرس يطابق ORDER BY، يمكن لـ PostgreSQL غالبًا إرجاع الصفوف مرتبة بالفعل بدلاً من فرز مجموعة كبيرة في الذاكرة.

-- A common screen: "My open tickets, newest first"
CREATE INDEX tickets_user_status_created_idx
ON tickets (user_id, status, created_at DESC);

تتبع المؤشرات المركبة قاعدة بسيطة: يمكن لـ PostgreSQL استخدام الجزء الرائد من المؤشر بكفاءة فقط. فكّر "من اليسار إلى اليمين". مع (user_id, status, created_at), الاستعلامات التي تَفلتر بـ user_id (وبشكل اختياري status) تستفيد. استعلام يَفلتر فقط بـ status عادةً لن يستفيد.

المؤشرات الجزئية هي ترقية قوية عندما تهتم شاشتك فقط بشريحة من البيانات. الشرائح الشائعة: "الصفوف النشطة فقط"، "غير المحذوفة منطقيًا"، أو "النشاط الحديث". تبقي المؤشرات الصغيرة أسرع وأسهل.

الأعمدة عالية التفرّد وتكلفة المؤشرات الإضافية

اصنع أداة داخلية
أنشئ أدوات داخلية لعمليات التشغيل والدعم مع Postgres، المصادقة، ومنطق العمل مضمّنًا.
ابدأ الآن

الأعمدة عالية التفرّد لها قيم فريدة عديدة، مثل user_id, order_id, email, أو created_at بالدقة الزمنية. تتألق المؤشرات هنا لأن الفلتر يمكنه تضييق النتائج بسرعة إلى شريحة صغيرة من الجدول.

الأعمدة منخفضة التفرّد هي العكس: booleans و enums الصغيرة مثل is_active, status IN ('open','closed'), أو plan IN ('free','pro'). غالبًا ما يخيب مؤشر على هذه الأعمدة لأن كل قيمة تطابق كمية كبيرة من الصفوف. قد يختار PostgreSQL المسح التسلسلي بدلًا من ذلك.

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

كل مؤشر إضافي له ثمن على عمليات الكتابة. يجب على الإدراجات الكتابة في كل مؤشر. التحديثات التي تغيّر أعمدة مفهرسة يجب أن تُحدّث تلك الإدخالات أيضًا. إضافة مؤشرات "للأمان" قد تُبطئ التطبيق كله، وليس شاشة واحدة فقط.

إرشادات عملية:

  • ابدأ بمؤشر/مؤشرين عمل لكل جدول مزدحم، مبنيين على المرشحات والفرز الحقيقية.
  • فضّل الأعمدة عالية الانتقائية المستخدمة في WHERE وORDER BY.
  • كن حذرًا عند فهرسة booleans و enums الصغيرة ما لم تُدمَج مع عمود انتقائي آخر.
  • أضِف مؤشرًا جديدًا فقط بعد أن تسمي الاستعلام الدقيق الذي سيسرعه.

مثال: قائمة تذاكر الدعم المصنفة بـ assignee_id (عالي التفرّد) تستفيد من مؤشر، بينما is_archived = false بمفرده غالبًا لا يفيد.

شاشات البحث: النص الكامل، البادئات، و"يحتوي"

مربعات البحث تبدو بسيطة، لكن المستخدمين يتوقعون الكثير: كلمات متعددة، أشكال كلمات مختلفة، وترتيب معقول. في PostgreSQL، هذا عادةً ما يكون البحث النصي الكامل: تخزّن tsvector (نص مُعَدّ) وتستعلم بـ tsquery (ما كتبه المستخدم، محللًا إلى مصطلحات).

للبحث النصي الكامل، GIN هو الافتراضي الشائع لأنه سريع في الإجابة عن "هل يحتوي هذا المستند على هذه المصطلحات؟" عبر العديد من الصفوف. المقابل هو كتابة أغلى: الإدراجات والتحديثات تصبح أكثر تكلفة.

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

البحث بالبادئة ليس بحثًا نصيًا كاملًا

البحث بالبادئة يعني "يبدأ بـ"، مثل البحث عن العملاء باستخدام بادئة البريد الإلكتروني. هذا ليس ما بُني له البحث النصي الكامل. للباترين البادئة، يمكن أن يساعد مؤشر B-tree (غالبًا مع فئة المُشغِّل المناسبة) لأنه يتطابق مع طريقة ترتيب السلاسل.

لبحوث "يحتوي" مثل ILIKE '%error%' عادة لا يساعد B-tree. هنا يبرز دور فهرسة trigram أو نهج بحث مختلف.

عندما يريد المستخدمون فلترة مع البحث النصي

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

  • مؤشر GIN (أو أحيانًا GiST) لعمود tsvector.
  • مؤشرات B-tree لأكثر المرشحات انتقائية (مثل account_id, status, created_at).
  • قاعدة صارمة "ابقه محدودًا"، لأن كثرة المؤشرات تُبطئ الكتابة.

مثال: شاشة تذاكر الدعم التي تبحث عن "refund delayed" وتفلتر إلى status = 'open' وaccount_id محدد. النص الكامل يعطيك الصفوف ذات الصلة، بينما تساعد B-tree PostgreSQL على تضييق الحساب بسرعة.

حقول JSONB: الاختيار بين GIN ومؤشرات B-tree الموجهة

بناء شاشات قوائم أسرع
حوّل مرشحات وشروط الفرز في شاشتك إلى نقاط نهاية مدعومة بـ Postgres دون كتابة كود يدوي.
جرب AppMaster

JSONB رائع للبيانات المرنة، لكنه قد يتحول إلى استعلامات بطيئة إذا تعاملت معه كعمود عادي. القرار الأساسي بسيط: هل تبحث "في أي مكان داخل هذا JSON"، أم أنك تَفلتر على مسارات محددة قليلة بشكل متكرر؟

للاستعلامات الاحتوائية مثل metadata @> '{"plan":"pro"}'، عادةً ما يكون GIN الخيار الأول. بُني للإجابة عن "هل يحتوي هذا المستند على هذا الشكل؟" ويدعم أيضًا فحوص وجود المفاتيح مثل ?, ?|, و?&.

إذا كان تطبيقك يفلتر في الغالب بمفتاح/مفتاحين من JSON، فمؤشر B-tree تعبيرية مستهدفة غالبًا ما تكون أسرع وأصغر. يساعد ذلك أيضًا عندما تحتاج إلى فرز أو مقارنات رقمية على القيم المستخرجة.

-- Broad support for containment and key checks
CREATE INDEX ON customers USING GIN (metadata);

-- Targeted filters and sorting on one JSON path
CREATE INDEX ON customers ((metadata->>'plan'));
CREATE INDEX ON events (((payload->>'amount')::numeric));

قاعدة إبهام جيدة:

  • استخدم GIN عندما يبحث المستخدمون في مفاتيح متعددة، أو علامات، أو هياكل متداخلة.
  • استخدم مؤشرات B-tree التعبيرية عندما تُفلتر على مسارات محددة متكررة.
  • افهرس ما يظهر في الشاشات الحقيقية، لا كل شيء.
  • إذا كان الأداء يعتمد على بعض مفاتيح JSON التي تستخدم دائمًا، فكّر في ترحيلها إلى أعمدة حقيقية.

مثال: شاشة دعم قد تَفلتر التذاكر بناءً على metadata->>'priority' وتُرتّب بـ created_at. افهرس مسار JSON للأولوية وعمود created_at العادي. تجنّب GIN واسع النطاق ما لم يبحث المستخدمون أيضًا في الوسوم أو السمات المتداخلة.

استعلامات الجغرافيا والنطاق: حيث يتناسب GiST أفضل

شاشات الجغرافيا والنطاق هي حيث يصبح GiST في كثير من الأحيان الخيار البديهي. GiST مصمم لـ "هل يتداخل هذا الشيء، يحتويه، أو قريب منه؟" بدلًا من "هل تساوي هذه القيمة تلك القيمة؟"

البيانات الجغرافية عادة تعني نقاط (موقع متجر)، خطوط (مسار)، أو مضلعات (منطقة توصيل). شاشات شائعة تشمل "المتاجر القريبة مني"، "وظائف خلال 10 كم"، "عرض العناصر داخل مربع الخريطة"، أو "هل هذا العنوان داخل منطقة خدمتنا؟" مؤشر GiST (غالبًا عبر PostGIS geometry/geography) يسرع هذه المشغِّلات المكانية ليجعل قاعدة البيانات تتجاوز معظم الصفوف بدلًا من فحص كل شكل.

النطاقات مماثلة. لدى PostgreSQL أنواع نطاق مثل daterange وint4range، والسؤال النموذجي هو التداخل: "هل هذا الحجز يتعارض مع حجز موجود؟" أو "عرض الاشتراكات النشطة خلال هذا الأسبوع." يدعم GiST مشغِّلات التداخل والاحتواء بكفاءة، لذلك هو شائع في الجداول الزمنية والجدولة وفحوص التوفر.

لا يزال B-tree مهمًا في شاشات شبيهة بالجغرافيا. العديد من الصفحات تَفلتر أولًا بحسب المستأجر، الحالة، أو الوقت، ثم تطبّق شرطًا مكانيًا، ثم تُرتب. على سبيل المثال: "طلبات التوصيل لشركتي فقط، من آخر 7 أيام، الأقرب أولًا." GiST يتعامل مع الجزء المكاني، لكن B-tree يساعد في المرشحات الانتقائية والفرز.

كيف تختار المؤشر خطوة بخطوة

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

اختيار المؤشر يتعلق بالمشغل أكثر من اسم العمود. نفس العمود قد يحتاج مؤشرات مختلفة اعتمادًا على ما إذا كنت تستخدم =, >, LIKE 'prefix%', بحث نصي كامل، احتواء JSON، أو مسافة جغرافية.

اقرأ الاستعلام كقائمة مراجعة: WHERE يقرر الصفوف المؤهلة، JOIN يحدد كيف ترتبط الجداول، ORDER BY يقرر ترتيب الإخراج، وLIMIT يقرر كم صف تحتاج فعليًا. المؤشر الأفضل غالبًا ما يساعدك في إيجاد أول 20 صفًا بسرعة.

عملية بسيطة تفيد معظم الشاشات:

  1. اكتب المشغِّلات الدقيقة التي تستخدمها شاشتك (مثال: status =, created_at >=, name ILIKE, meta @>, ST_DWithin).
  2. ابدأ بمؤشر يطابق أكثر فلتر انتقائي أو فرز افتراضي. إن كانت الشاشة تُرتب بـ created_at DESC، ابدأ من هناك.
  3. أضِف مؤشرًا مركبًا فقط عندما ترى نفس المرشحات معًا دائمًا. ضع أعمدة المعادلة أولًا، ثم أعمدة النطاق، ثم مفتاح الفرز.
  4. استخدم مؤشرًا جزئيًا عندما تُفلتر دائمًا إلى مجموعة فرعية (مثال: فقط status = 'open'). استخدم مؤشرًا على تعبير عندما تستعلم عن قيمة محسوبة (مثال: lower(email) للبحث غير الحساس لحالة الأحرف).
  5. حقِّق باستخدام EXPLAIN ANALYZE. احتفظ به إن قلل زمن التنفيذ وعدد الصفوف المقروءة بشكل كبير.

مثال ملموس: لوحة دعم قد تَفلتر التذاكر بـ status وتُرتّب بالأحدث. B-tree على (status, created_at DESC) محاولة أولى قوية. إن كانت نفس الشاشة تَفلتر أيضًا بعلم JSONB مثل meta @> '{"vip": true}'، فذلك مشغل مختلف وعادة ما يحتاج مؤشر JSON منفصل.

أخطاء شائعة تضيع الوقت (وتُبطئ الكتابة)

صمّم قاعدة بياناتك بصريًا
صمّم الجداول في مُصمّم البيانات ودع المؤشرات تتبع استعلامات الواجهة الحقيقية.
ابدأ البناء

طريقة شائعة للإحباط هي اختيار نوع "صحيح" للمؤشر للمشغل الخاطئ. يمكن لـ PostgreSQL استخدام المؤشر فقط عندما يَتناسب الاستعلام مع ما بُني المؤشر للإجابة عنه. إن كان تطبيقك يستخدم ILIKE '%term%'، فلن يُستخدم B-tree العادي، وستظل تفحص الجدول.

فخ آخر هو بناء مؤشرات متعددة الأعمدة ضخمة "لمجرد الاحتياط". تبدو آمنة، لكنها مكلفة للصيانة وغالبًا لا تتطابق مع أنماط الاستعلام الحقيقية. إن لم تُستخدم الأعمدة الأيسر من المؤشر في الفلتر، قد لا يفيد بقية المؤشر.

من السهل أيضًا الإفراط في فهرسة الأعمدة منخفضة الانتقائية. مؤشر B-tree على boolean مثل is_active أو حالة بعدد قليل من القيم قد يكون عديم الفائدة ما لم تجعله جزئيًا ليتطابق مع ما تستعلم عنه بالفعل.

لـ JSONB، هناك محاذير خاصة. قد يكون GIN واسعًا ممتازًا للمرونة، لكن العديد من فحوص مسارات JSON أسرع بمؤشر تعبيري على القيمة المستخرجة. إن كانت شاشتك دومًا تَفلتر بـ payload->>'customer_id'، ففهرسة ذلك التعبير أصغر وأسرع من فهرسة المستند كله.

أخيرًا، كل مؤشر إضافي يثقل كاهل الكتابة. على الجداول التي تُحدّث كثيرًا، يجب على كل إدراج وتحديث تحديث كل مؤشر.

قبل إضافة مؤشر، توقف وتحقق:

  • هل يطابق المؤشر المشغل الدقيق الذي يستخدمه الاستعلام؟
  • هل يمكنك استبدال مؤشر متعدد الأعمدة واسع بواحد أو اثنين موجهين؟
  • هل يجب أن يكون مؤشرًا جزئيًا لتجنّب ضوضاء الانتقائية المنخفضة؟
  • بالنسبة لـ JSONB، هل يناسب المؤشر التعبيري الشاشة أفضل؟
  • هل الجدول يعاني من كثافة كتابة تجعل تكلفة المؤشر أكبر من فائدة القراءة؟

فحوصات سريعة قبل أن تضيف (أو تحتفظ) بمؤشر

قبل إنشاء مؤشر جديد، كن محددًا بشأن ما تفعله الواجهة فعليًا. مؤشرات "جميلة أن تكون موجودة" غالبًا ما تتحول إلى كتابات أبطأ ومساحة أكبر مع عائد ضئيل.

ابدأ بأهم ثلاث شاشات (أو نقاط النهاية) واكتب شكل الاستعلامات الدقيقة: المرشحات، وترتيب الفرز، وما يكتبه المستخدم في البحث. كثير من "مشاكل المؤشرات" هي في الحقيقة "مشاكل استعلام غير واضحة"، خاصة عندما يناقش الناس B-tree مقابل GIN مقابل GiST دون تسمية المشغل.

قائمة تحقق بسيطة:

  • اختر 3 شاشات حقيقية وسرد أنماط WHERE وORDER BY الدقيقة (بما في ذلك الاتجاه والتعامل مع NULL).
  • أكد نوع المشغل: مساواة (=)، نطاق (>, BETWEEN)، بادئة، احتواء، تداخل، أو مسافة.
  • اختر مؤشرًا واحدًا لكل نمط شاشة شائع، اختبره، واحتفظ فقط بما يقلّل الزمن أو عدد الصفوف المقروءة بشكل ملحوظ.
  • إذا كان الجدول يكتب بكثافة، فكن صارمًا: المؤشرات الإضافية تضاعف تكلفة الكتابة وقد تزيد ضغط vacuum.
  • أعد التحقق بعد تغيُّرات الميزة. مرشح جديد، ترتيب افتراضي جديد، أو التحول من "يبدأ بـ" إلى "يحتوي" قد يجعل المؤشر القديم غير ذي صلة.

مثال: أضافت لوحة ترتيب افتراضي جديد last_activity DESC. إن كنت فهرستِ فقط status، قد تظل الفلترة سريعة، لكن الفرز قد يفرض عملًا إضافيًا الآن.

مثال: مطابقة شاشات تطبيق حقيقية بالمؤشر المناسب

من النموذج الأولي إلى المقياس
نمذِّج لوحة قيادة بسرعة ثم حسّن الأداء عندما تظهر المرشحات والترتيبات الحقيقية.
ابدأ الآن

جدول القرار مفيد فقط عندما تستطيع مطابقة الشاشات الحقيقية التي تُطلقها. هنا ثلاث شاشات شائعة وكيف تترابط مع اختيارات المؤشر.

شاشةنمط الاستعلام النموذجيالمؤشر الذي يناسب عادةلماذا
قائمة إدارية: مرشحات + فرز + بحث نصي حرstatus = 'open' زائد فرز created_at، زائد بحث في title/notesB-tree على (status, created_at) وGIN على tsvectorالفلاتر + الفرز لـ B-tree. البحث النصي عادةً GIN.
ملف العميل: تفضيلات JSON وسماتprefs->>'theme' = 'dark' أو فحص وجود علمGIN على عمود JSONB للبحث المرن، أو B-tree موجه للتعابير لمسارين ساخنيناختر بناءً على إن كنت تبحث في مفاتيح عديدة أو في مسارات مستقرة قليلة.
مواقع قريبة: مسافة + فلتر فئةأماكن داخل X كم، مُفلترة بـ category_idGiST على geometry/geography وB-tree على category_idGiST يتعامل مع المسافة/الضمن. B-tree يتعامل مع المرشحات العادية.

طريقة عملية لتطبيق هذا تبدأ من واجهة المستخدم:

  • اذكر كل عنصر واجهة يُضيّق النتائج (المرشحات).
  • لاحظ ترتيب الفرز الافتراضي.
  • كن محددًا بسلوك البحث (نصي كامل أم يبدأ بـ أم يحتوي).
  • اذكر الحقول "الخاصة" (JSONB، جغرافيا، نطاقات).

الخطوات التالية: اجعل الفهرسة جزءًا من سير العمل

المؤشرات الجيدة تتبع شاشاتك: المرشحات التي ينقرها الناس، ترتيب الفرز المتوقع، ومربع البحث الذي يستخدمونه فعليًا. اعتبر الفهرسة عادة أثناء التطوير وستتجنّب معظم مفاجآت الأداء لاحقًا.

اجعلها قابلة للتكرار: حدد 1-3 استعلامات شائعة لكل شاشة، أضِف أصغر مؤشر يطابقها، اختبر ببيانات واقعية، ثم أزل ما لا يقدم فائدة حقيقية.

إن كنت تبني أداة داخلية أو بوابة عملاء، خطط لاحتياجات المؤشرات مبكرًا لأن هذه التطبيقات غالبًا ما تكبر بإضافة مرشحات وشاشات قوائم. إذا بنيتَ باستخدام AppMaster (appmaster.io)، ففكّر في معاملة إعدادات الفلتر والفرز لكل شاشة كعقد استعلام واضح، ثم أضِف فقط المؤشرات التي تدعم تلك النقرات الحقيقية.

الأسئلة الشائعة

How do I choose between B-tree, GIN, and GiST for a real screen?

ابدأ بكتابة ما تفعله أكثر الشاشات ازدحامًا لديك بصيغة SQL: مشغلات WHERE، وترتيب ORDER BY، والحد LIMIT. عادةً ما يناسب B-tree المطابَقات والأنطاق والترتيب؛ يناسب GIN عمليات «هل تحتوي هذه الصفوف على X؟» مثل البحث النصي الكامل واحتواء JSONB؛ ويناسب GiST حالات التداخل، والمسافة، واستعلامات القرب/الضمنية.

When is a B-tree index the right choice?

مؤشر B-tree يناسب عندما تُصفّي بقِيَم دقيقة، أو تُصفّي بنطاقات زمنية/رقمية، أو تحتاج نتائج بترتيب محدد. هو الاختيار الشائع للقوائم الإدارية ولوحات التحكم والتقسيم الصفحي حيث تكون الصيغة «تصفية، ترتيب، حد».

When should I use a GIN index?

استخدم GIN عندما يمكن أن تطابق كل صف العديد من المفاتيح أو المصطلحات والسؤال هو «هل يحتوي هذا الصف على X؟». هو الإعداد الافتراضي المعتاد للبحث النصي الكامل (@@ على tsvector) واحتواء JSONB/المصفوفات مثل @> أو فحوص وجود المفتاح.

What is GiST best for in PostgreSQL?

GiST مناسب للبيانات التي لا تُرتّب بسهولة، وحيث تكون الاستعلامات عن القرب أو التداخل أو الاحتواء بالمعنى الهندسي أو بالمدى. حالات شائعة: استعلامات PostGIS "قريب مني" / داخل نصف القطر وأنواع النطاق في PostgreSQL حيث تتحقق من التداخل.

How do I order columns in a composite B-tree index?

ضع أعمدة التطابق (=) أولًا، ثم أعمدة النطاق، ثم عمود الفرز. على سبيل المثال، (user_id, status, created_at DESC) جيد عندما تُصفّي دائمًا بـ user_id وstatus وتُظهر الأحدث أولًا؛ لن يساعد كثيرًا إن كنت تصفّي فقط بـ status.

When does a partial index make sense?

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

Should I index low-cardinality columns like booleans or status?

مؤشر بسيط على قيمة ذات إحصائية منخفضة مثل boolean أو تعداد صغير غالبًا ما يخيب لأن كل قيمة تطابق جزءًا كبيرًا من الجدول، لذلك قد يفضّل PostgreSQL المسح التسلسلي. قد يفيد عند الدمج مع عمود انتقائي آخر (مثل tenant_id) أو إن جعلته جزئيًا ليتوافق مع الشريحة التي تستعلم عنها.

For JSONB, when do I choose GIN vs an expression B-tree index?

استخدم GIN على عمود JSONB كامل عندما تحتاج احتواءً مرنًا وفحوص مفاتيح عبر مفاتيح متعددة. استخدم مؤشرات تعبيرية B-tree موجهة عندما تُصفّي أو تُرتب تكرارًا حسب مسارات JSON ثابتة قليلة، مثل (metadata->>'plan') أو تحويل قيمة JSON إلى رقم.

Why doesn’t my index help with ILIKE '%term%' searches?

للبحث "يبدأ بـ" مثل email LIKE 'abc%' يمكن أن يساعد B-tree لأن ذلك يتوافق مع ترتيب السلاسل. للبحث "يحتوي" مثل ILIKE '%abc%' عادةً لن يستخدم B-tree العادي؛ تحتاج نهجًا مختلفًا (غالبًا فهرس trigram) أو تصميم بحث مختلف.

What’s the safest way to add indexes without slowing down writes?

انشئ أصغر مؤشر يطابق نمط استعلام محدد وعالي الحركة، ثم تحقق باستخدام EXPLAIN ANALYZE وبيانات واقعية. إذا كان الجدول عمليًا يتعرّض لكتابات كثيرة، فكن صارمًا: المؤشرات الإضافية تضاعف تكلفة الكتابة وتزيد ضغط vacuum. اجعل كل مؤشر يملك سببًا واضحًا مرتبطًا باستعلام حقيقي.

من السهل أن تبدأ
أنشئ شيئًا رائعًا

تجربة مع AppMaster مع خطة مجانية.
عندما تكون جاهزًا ، يمكنك اختيار الاشتراك المناسب.

البدء