أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

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

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول

بحث LIKE في PostgreSQL بطيء؟ pg_trgm ينقذه في دقائق

📅 ٢٥ أبريل ٢٠٢٦⏱ 4 دقائق قراءة
بحث LIKE في PostgreSQL بطيء؟ pg_trgm ينقذه في دقائق

بحث LIKE في PostgreSQL بطيء؟ pg_trgm ينقذه في دقائق

مستوى القارئ: متوسط

هتعرف في المقال ده إزاي تقلل بحث ILIKE '%word%' في PostgreSQL من حوالي 1.8 ثانية إلى 85ms في سيناريو واقعي، بدل ما تزود السيرفرات وتسيب الاستعلام بيحرق الـ CPU.

المشكلة باختصار

الطريقة الشائعة دي بتفشل لما البحث مش متثبت من أول النص. يعني WHERE title ILIKE 'iphone%' ممكن يستفيد من B-tree في حالات معينة، لكن WHERE title ILIKE '%iphone%' غالبًا بيضطر PostgreSQL يفتش في عدد ضخم من الصفوف. اللي بيحصل فعلاً إن قاعدة البيانات تقرأ صفوف كتير، تقارن النص، وبعدها ترجع عدد قليل جدًا من النتائج.

افتراض المقال إن عندك جدول منتجات أو مقالات فيه من 500 ألف إلى 5 مليون صف، والبحث مطلوب داخل الاسم أو العنوان. السيناريو الواقعي: متجر عنده 2 مليون منتج، وزائر بيكتب كلمة في صندوق البحث. قبل التحسين، طلب البحث بياخد 1.8 ثانية عند p95. بعد إضافة pg_trgm وقياس الخطة، نفس النمط نزل إلى 85ms تقريبًا. الأرقام تقديرية لكنها قريبة من اللي هتشوفه لو المشكلة فعلاً في full scan.

لوحة بيانات تقنية تعرض رسوم أداء تشبه قياس زمن استعلامات PostgreSQL قبل التحسين

الفكرة ببساطة: يعني إيه trigram؟

ركز مع المثال ده. كلمة phone ممكن تتقسم لقطع صغيرة من 3 حروف مثل pho وhon وone. بدل ما قاعدة البيانات تدور في النص كله من الصفر، هي تبني index يعرف القطع الصغيرة دي موجودة فين. لما المستخدم يبحث عن iphone، PostgreSQL يطلع صفوف مرشحة من الـ index الأول، ثم يفلترها بدقة.

علميًا، امتداد pg_trgm في PostgreSQL يوفر دوال ومشغلات لمقارنة النصوص بناءً على trigrams، ويوفر operator classes يمكن استخدامها مع GIN أو GiST indexes لتسريع بحث LIKE وILIKE وregex. أفضل طريقة هنا غالبًا GIN لو الجدول قراءته أكثر من كتابته. الـ trade-off هنا إن بناء الـ index بياخد وقت ومساحة، وتحديث الصفوف هيبقى أغلى شوية.

الخطوات العملية

  1. قيس الاستعلام الحالي بـ EXPLAIN ANALYZE قبل أي تعديل.
  2. فعّل امتداد pg_trgm داخل قاعدة البيانات.
  3. ابني GIN index على العمود اللي عليه البحث.
  4. شغل نفس الاستعلام تاني وقارن الزمن والخطة.
SQL
-- 1) القياس قبل التحسين
EXPLAIN (ANALYZE, BUFFERS)
SELECT id, title
FROM products
WHERE title ILIKE '%iphone%'
ORDER BY updated_at DESC
LIMIT 20;

-- 2) تفعيل الامتداد
CREATE EXTENSION IF NOT EXISTS pg_trgm;

-- 3) بناء index مناسب لبحث contains
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_products_title_trgm
ON products USING gin (title gin_trgm_ops);

-- 4) القياس بعد التحسين
EXPLAIN (ANALYZE, BUFFERS)
SELECT id, title
FROM products
WHERE title ILIKE '%iphone%'
ORDER BY updated_at DESC
LIMIT 20;

لو الخطة بعد التعديل لسه بتستخدم Seq Scan، متستعجلش تقول إن الـ index فشل. راجع حجم الجدول، نسبة النتائج الراجعة، وتأكد إن pattern فيه 3 حروف على الأقل. بحث زي '%ip%' ضعيف جدًا مع trigram لأن عدد القطع القابلة للاستخراج قليل.

شاشة كود وتحليل استعلامات SQL لاستخدامها مع EXPLAIN ANALYZE في PostgreSQL

الأرقام التي تهمك

قبل التحسين، خطة Seq Scan على 2 مليون صف ممكن تعمل قراءة كبيرة جدًا وتوصل إلى 1.8 ثانية عند p95. بعد GIN + pg_trgm، الخطة تتحول غالبًا إلى Bitmap Index Scan ثم Bitmap Heap Scan، والزمن ممكن ينزل إلى 85ms في بحث بكلمة مناسبة. المكسب هنا واضح: latency أقل وتجربة بحث أسرع. الخسارة: index إضافي قد يأخذ من 20% إلى 60% من حجم العمود النصي حسب البيانات، وعمليات insert/update هتدفع تكلفة تحديث الـ index.

استخدم EXPLAIN (ANALYZE, BUFFERS) لأن الزمن وحده مش كفاية. لو shared read blocks عالي جدًا، المشكلة I/O. لو rows removed by filter عالي، فالاستعلام كان بيقرأ مرشحين كتير. القياس ده يمنعك من تحسين الجزء الغلط.

متى لا تستخدم هذه الطريقة

لا تستخدم pg_trgm لو البحث عندك full-text حقيقي فيه ترتيب حسب relevance، stemming، أو بحث لغوي عميق. في الحالة دي شوف PostgreSQL Full Text Search أو Elasticsearch/OpenSearch حسب الحجم. لا تستخدمه كمان لو الجدول صغير جدًا، مثل 10 آلاف صف، لأن Seq Scan قد يكون أسرع وأبسط. ولو عندك write-heavy workload، يعني آلاف التحديثات في الثانية على نفس العمود، تكلفة GIN index ممكن تبقى مزعجة.

المصادر

  • توثيق PostgreSQL الرسمي: pg_trgm
  • توثيق PostgreSQL الرسمي: استخدام EXPLAIN
  • توثيق PostgreSQL الرسمي: GIN indexes

الخطوة التالية

افتح أبطأ search query عندك وشغّل EXPLAIN (ANALYZE, BUFFERS). لو لقيت Seq Scan مع ILIKE '%...%' على جدول كبير، جرّب pg_trgm على staging وقارن p95 قبل وبعد.

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة