دليل ضبط أداء Go وPostgres لواجهات API المولّدة آلياً: إدارة التجمعات، قراءة خطط الاستعلام، فهرسة ذكية، ترقيم آمن، وتشكيل JSON بسرعة.

قد تبدو واجهات API المُولّدة آلياً سريعة في الاختبار المبكر. تدعو نقطة نهاية عدة مرات، ومجموعة البيانات صغيرة، والطلبات تأتي واحدة تلو الأخرى. ثم يصل تحميل حقيقي: نقاط نهاية مختلطة، حمولة متقطعة، كاشات أبرد، وعدد صفوف أكبر مما توقعت. نفس الشيفرة يمكن أن تبدأ بالظهور بطيئة عشوائياً رغم أن شيئاً لم ينكسر فعلياً.
عادة ما يظهر البطء بعدة طرق: قفزات زمن الاستجابة (معظم الطلبات جيدة، وبعضها يستغرق 5× إلى 50× أطول)، مهلات (نسبة صغيرة تفشل)، أو وحدة المعالجة تعمل بنشاط مرتفع (CPU في Postgres من العمل على الاستعلامات، أو CPU في Go من JSON، goroutines، التسجيل، وإعادة المحاولات).
سيناريو شائع هو نقطة قائمة بها فلتر بحث مرن تعيد استجابة JSON كبيرة. في قاعدة اختبار تفحص بضعة آلاف صفوف وتُنجز بسرعة. في الإنتاج تفحص بضعة ملايين صفوف، ترتّبها، ثم تطبّق LIMIT. الـ API ما يزال "يعمل"، لكن زمن p95 يتفجّر وت timeout تحدث لعدد من الطلبات أثناء الارتفاع.
لفصل بطء قاعدة البيانات عن بطء التطبيق، احتفظ بنموذج ذهني بسيط.
إذا كانت قاعدة البيانات بطيئة، يقضي معالج Go معظم وقته في انتظار الاستعلام. قد ترى أيضاً الكثير من الطلبات عالقة "قيد التنفيذ" بينما يبدو CPU في Go طبيعياً.
إذا كان التطبيق بطيئاً، ينتهي الاستعلام سريعاً، لكن الوقت يُهدر بعد الاستعلام: بناء كائنات استجابة كبيرة، تسلسل JSON، استعلامات إضافية لكل صف، أو عمل كبير لكل طلب. يرتفع CPU في Go، ترتفع الذاكرة، ويزداد زمن الاستجابة مع حجم الاستجابة.
"ما يكفي" من الأداء قبل الإطلاق ليس الكمال. بالنسبة للعديد من نقاط CRUD، استهدف زمن p95 مستقر (ليس المتوسط فقط)، سلوك متوقع أثناء الارتفاعات، وعدم وجود مهلات عند الذروة المتوقعة. الهدف واضح: لا مفاجآت بخصوص طلبات بطيئة عندما تنمو البيانات والحركة، وإشارات واضحة عندما يحدث انحراف.
قبل أن تضبط أي شيء، قرّر ما الذي يعنيه "جيد" لواجهتك. بدون خط أساس، من السهل قضاء ساعات في تغيير إعدادات ولا تعرف إنك حسّنت أم غيّرت عنق الزجاجة.
ثلاثة أرقام عادةً تروي معظم القصة:
p95 هو مقياس "اليوم السيئ". إن كان p95 مرتفعاً لكن المتوسط جيد، مجموعة صغيرة من الطلبات تقوم بعمل زائد، تُحاصر على أقفال، أو تُشغّل خططاً بطيئة.
اجعل الاستعلامات البطيئة مرئية مبكراً. في Postgres فعّل تسجيل الاستعلامات البطيئة بعتبة منخفضة للاختبار قبل الإطلاق (مثلاً 100–200 ملّي ثانية)، وسجّل العبارة كاملة لتتمكن من نسخها إلى عميل SQL. احتفظ بهذا مؤقتاً. تسجيل كل استعلام بطيء في الإنتاج يصير مزعجاً سريعاً.
بعد ذلك، اختبر بطلبات شبيهة بالواقعية، لا نقطة "hello world" واحدة. مجموعة صغيرة كافية إذا طابقت ما يفعله المستخدمون فعلاً: نداء قائمة بفلاتر وفرز، صفحة تفصيل مع بعض الانضمامات، إنشاء أو تعديل مع تحقق، واستعلام شبيه بالبحث مع تطابقات جزئية.
إذا كنت تُولّد نقاط نهاية من مواصفة (مثلاً بأداة ذكية مثل Koder.ai)، شغّل نفس مجموعة الطلبات مراراً مع مدخلات ثابتة. ذلك يجعل تغييرات مثل الفهارس، ضبط الترقيم، وإعادة كتابة الاستعلامات أسهل للقياس.
أخيراً، اختر هدفاً يمكنك قوله بصوت عالٍ. مثال: "معظم الطلبات تبقى تحت 200 ملّي ثانية p95 عند 50 مستخدماً متزامناً، والأخطاء أقل من 0.5%." الأرقام الدقيقة تعتمد على منتجك، لكن هدف واضح يمنع العبث اللامتناهي.
تجمّع الاتصالات يحافظ على عدد محدود من اتصالات قاعدة البيانات المفتوحة ويعيد استخدامها. بدون تجمع، قد يفتح كل طلب اتصالاً جديداً، ويضيع Postgres وقتاً وذاكرة في إدارة الجلسات بدلاً من تشغيل الاستعلامات.
الهدف هو إبقاء Postgres منشغلاً بعمل مفيد، لا بالتنقل بين اتصالات كثيرة. هذا غالباً أول فوز ملموس، خصوصاً لواجهات API المولّدة آلياً التي قد تصبح دردشية بصمت.
في Go، عادةً تضبط max open connections، max idle connections، وconnection lifetime. نقطة بداية آمنة للعديد من APIs الصغيرة هي عدة أضعاف عدد أنوية المعالج (غالباً 5 إلى 20 اتصالاً إجمالاً)، مع عدد مماثل محفوظ خالياً، وإعادة تدوير الاتصالات دورياً (مثلاً كل 30 إلى 60 دقيقة).
إذا كنت تشغّل عدة مثيلات للـ API، تذكّر أن التجمع يتضاعف. تجمع من 20 عبر 10 مثيلات يعني 200 اتصال تصيب Postgres، ومن هنا تحدث فرق عن القيود غير المتوقعة.
مشاكل التجمع مختلفة عن SQL البطيء.
إذا كان التجمع صغيراً جداً، تنتظر الطلبات قبل أن تصل حتى إلى Postgres. تظهر قفزات في الزمن، لكن CPU وقيم زمن الاستعلام في قاعدة البيانات قد تبدو طبيعية.
إذا كان التجمع كبيراً جداً، يبدو Postgres مثقلاً: عدد جلسات نشطة كبير، ضغط على الذاكرة، وزمن استجابة متباين عبر النقاط.
طريقة سريعة للفصل هي توقيت مكالمات DB في جزئين: الوقت المستغرق في انتظار الاتصال مقابل الوقت المستغرق في تنفيذ الاستعلام. إذا كان معظم الوقت "انتظاراً"، فالتجمع هو عنق الزجاجة. إذا كان معظم الوقت "في الاستعلام"، ركّز على SQL والفهارس.
فحوص سريعة مفيدة:
max_connections.إذا استخدمت pgxpool، تحصل على تجمع مُصمّم لPostgres مع إحصاءات واضحة وإعدادات افتراضية جيدة لسلوك Postgres. إذا استخدمت database/sql، تحصل على واجهة معيارية تعمل عبر قواعد بيانات، لكن عليك أن تكون صريحاً بشأن إعدادات التجمع وسلوك السائق.
قاعدة عملية: إذا كنت ملتزماً بـ Postgres وتريد تحكماً مباشراً، غالباً ما يكون pgxpool أبسط. إذا اعتمدت على مكتبات تتوقع database/sql، التزم بها، اضبط التجمع صراحةً، وقِس أوقات الانتظار.
مثال: نقطة قائمة تعرض الطلبات قد تعمل في 20 ملّي ثانية، لكن تحت 100 مستخدم متزامن تقفز إلى 2 ثانية. إذا أظهرت السجلات 1.9 ثانية انتظاراً للحصول على اتصال، فلن يساعد ضبط الاستعلام حتى يتم ضبط التجمع وإجمالي اتصالات Postgres بشكل صحيح.
عندما تبدو نقطة النهاية بطيئة، تحقق مما يفعله Postgres فعلاً. قراءة سريعة لـ EXPLAIN غالباً تشير إلى الحل خلال دقائق.
شغّل هذا على SQL الدقيق الذي يرسله API:
EXPLAIN (ANALYZE, BUFFERS)
SELECT id, status, created_at
FROM orders
WHERE user_id = $1 AND status = $2
ORDER BY created_at DESC
LIMIT 50;
بضعة أسطر تهمك أكثر. انظر إلى العقدة العلوية (ما اختاره الـ planner) والمجاميع في الأسفل (كم استغرق). ثم قارن الصفوف المقدرة بالفعلية. الفجوات الكبيرة عادةً تعني أن المخطط خمن خطأ.
إن رأيت Index Scan أو Index Only Scan، فـPostgres يستخدم فهرساً، وهو عادةً جيد. Bitmap Heap Scan قد يكون مقبولاً للنتائج متوسطة الحجم. Seq Scan يعني أنه قرأ الجدول بأكمله، وهو مقبول فقط عندما يكون الجدول صغيراً أو عندما تتطابق معظم الصفوف.
علامات حمراء شائعة:
ORDER BY)الخطط البطيئة عادةً تأتي من أنماط قليلة:
WHERE + ORDER BY الخاص بك (مثلاً (user_id, status, created_at))WHERE (مثلاً WHERE lower(email) = $1)، ما قد يجبر على المسح ما لم تضف فهرس تعبير مناسبإذا بدا المخطط غريباً وكانت التقديرات متباعدة، فعادةً الإحصاءات قديمة. شغّل ANALYZE (أو دع autovacuum ي catch up) حتى يتعلم Postgres عدّ الصفوف وتوزيع القيم الحالي. هذا مهم بعد استيرادات كبيرة أو عندما تبدأ نقاط نهاية جديدة بالكتابة بكثافة.
الفهارس تنفع فقط عندما تطابق كيف يستعلم تطبيقك عن البيانات. إن بنتها من تخمينات، تحصل على كتابات أبطأ ومساحة أكبر وفائدة ضئيلة.
طريقة عملية للتفكير: الفهرس هو اختصار لسؤال محدد. إذا سأل تطبيقك سؤالاً مختلفاً، يتجاهل Postgres الاختصار.
إن كانت نقطة النهاية تفرز على account_id وتعرض بحسب created_at DESC، فهرس مركب واحد غالباً يتفوق على فهرسين منفصلين. يساعد Postgres في العثور على الصفوف الصحيحة وإرجاعها بالترتيب المطلوب بعمل أقل.
قواعد عامة تصمد غالباً:
مثال: إن كانت استعلاماتك GET /orders?status=paid وتعرض الأحدث أولاً، فهرس مثل (status, created_at DESC) مناسب. إن كانت معظم الاستعلامات أيضاً تُفلتر حسب customer، فـ (customer_id, status, created_at) قد يكون أفضل، لكن فقط إذا كان هذا نمط الاستعلام الفعلي في الإنتاج.
إذا كانت معظم الحركة تضرب شريحة ضيقة من الصفوف، يمكن أن يكون الفهرس الجزئي أرخص وأسرع. مثلاً، إن كان التطبيق يقرأ غالباً السجلات النشطة، ففهرستها فقط WHERE active = true يبقي الفهرس أصغر وأكثر احتمالاً للبقاء في الذاكرة.
لتأكيد أن الفهرس يساعد، قم بفحوص سريعة:
EXPLAIN (أو EXPLAIN ANALYZE في بيئة آمنة) وابحث عن index scan يطابق استعلامك.أزل الفهارس غير المستخدمة بحذر. تحقق من إحصاءات الاستخدام (مثلاً إن كان الفهرس قد فُحص). احذف واحداً في كل مرة خلال نوافذ منخفضة المخاطر، واحتفظ بخطة استرجاع. الفهارس غير المستخدمة ليست بلا تأثير؛ فهي تبطئ الإدخالات والتحديثات في كل كتابة.
الترقيم غالباً ما يكون المكان الذي يبدأ فيه API السريع بالظهور بطيئاً، حتى عندما تكون قاعدة البيانات صحية. عامل الترقيم كمشكلة تصميم استعلام، لا كتفصيل واجهة.
LIMIT/OFFSET يبدو بسيطاً، لكن الصفحات العميقة تكلف أكثر. Postgres لا يزال يضطر لتجاوز (وغالباً فرز) الصفوف التي تتخطاها. الصفحة 1 قد تلمس بضعة عشرات من الصفوف. الصفحة 500 قد تجبر قاعدة البيانات على مسح والتخلص من عشرات الآلاف فقط لإرجاع 20 نتيجة.
كما أنه يخلق نتائج غير مستقرة عند إدراج أو حذف صفوف بين الطلبات. قد يرى المستخدمون تكرارات أو يفقدون عناصر لأن معنى "الصف 10,000" يتغير مع تغير الجدول.
ترقيم keyset يطرح سؤالاً مختلفاً: "أعطني الـ 20 صفاً التالية بعد آخر صف رأيته." هذا يحافظ على عمل القاعدة على شريحة صغيرة ومتسقة.
نسخة بسيطة تستخدم id متزايد:
SELECT id, created_at, title
FROM posts
WHERE id > $1
ORDER BY id
LIMIT 20;
ترجع API next_cursor يساوي آخر id في الصفحة. الطلب التالي يستخدم تلك القيمة كقيمة $1.
للفرز القائم على الوقت، استخدم ترتيباً ثابتاً وكسر التعادل. created_at وحده غير كافٍ إذا شارك صفان نفس الطابع الزمني. استخدم cursor مركب:
WHERE (created_at, id) < ($1, $2)
ORDER BY created_at DESC, id DESC
LIMIT 20;
قواعد تمنع التكرارات والصفحات المفقودة:
ORDER BY (غالباً id).created_at وid معاً).سبب شائع ومفاجئ لإحساس البطء ليس قاعدة البيانات. إنه الاستجابة. JSON الكبير يستغرق وقتاً أطول للبناء، وقتاً أطول للإرسال، ووقتاً أطول لتحليل العميل. أسرع فوز غالباً هو إعادة أقل.
ابدأ بـ SELECT. إن كانت نقطة النهاية تحتاج فقط id, name, وstatus، اطلب تلك الأعمدة ولا شيء غيرها. SELECT * يثقل الصمت مع مرور الوقت مع إضافة نص طويل، كائنات JSON، وأعمدة تدقيق.
تباطؤ آخر شائع هو بناء الاستجابات بنمط N+1: تجلب قائمة من 50 عنصراً، ثم تشغّل 50 استعلاماً إضافياً لإرفاق بيانات مرتبطة. قد يجتاز الاختبارات ثم ينهار تحت حركة حقيقية. فضّل استعلاماً واحداً يعيد ما تحتاج (انضمامات محسوبة بحذر)، أو استعلامين حين يجمع الثاني حسب المعرفات.
طرق للحفاظ على الحمولات أصغر دون كسر العملاء:
include= (أو قناع fields=) حتى تبقى استجابات القوائم خفيفة وتضمّن التفاصيل عند الطلب.كلاهما قد يكون سريعاً. اختر بناءً على ما تحاول تحسينه.
دوال JSON في Postgres (jsonb_build_object, json_agg) مفيدة عند رغبتك في تقليل عدد الرحلات والحصول على أشكال متوقعة من استعلام واحد. التشكيل في Go مفيد عند حاجتك إلى منطق شرطي، إعادة استخدام structs، أو الإبقاء على SQL أسهل للصيانة. إذا أصبح SQL الخاص بتشكيل JSON صعب القراءة، يصبح من الصعب ضبطه.
قاعدة جيدة: دَع Postgres يقوم بالتصفية والفرز والتجميع. ثم دع Go يتولى العرض النهائي.
إذا كنت تُولّد واجهات بسرعة (مثلاً مع Koder.ai)، فإن إضافة أعلام include مبكراً يساعد على تجنب نقاط نهاية تنتفخ مع الزمن. كما يوفّر طريقة آمنة لإضافة حقول دون جعل كل استجابة أثقل.
لا تحتاج مختبراً ضخماً لالتقاط معظم مشكلات الأداء. تمريرة قصيرة وقابلة للتكرار تكشف المشاكل التي تتحول إلى انقطاعات عندما تظهر الحركة فعلاً، خصوصاً عندما تكون نقطة البداية شيفرة مُولّدة تخطط لشحنها.
قبل تغيير أي شيء، دوّن خط أساس صغير:
ابدأ صغيراً، غيّر شيئاً واحداً في كل مرة، وأعد الاختبار بعد كل تغيير.
شغّل اختبار تحميل لمدة 10 إلى 15 دقيقة يشبه الاستخدام الحقيقي. اضرب نفس النقاط التي سيستخدمها أول زبائنك (تسجيل دخول، صفحات قائمة، بحث، إنشاء). ثم رتب المسارات حسب زمن p95 والوقت الإجمالي.
افحص ضغط الاتصالات قبل ضبط SQL. تجمع كبير جداً يرهق Postgres؛ تجمع صغير جداً يخلق انتظاراً طويلاً. ابحث عن ارتفاع وقت انتظار الحصول على اتصال وعدد اتصالات يقفز أثناء الارتفاعات. عدّل حدود التجمع والidle أولاً، ثم أعد الاختبار.
EXPLAIN لأبطأ الاستعلامات وأصلح أكبر علامة حمراء. المذنبون المعتادون: المسح الكامل للجداول الكبيرة، الفرز على مجموعات نتائج ضخمة، وانضمامات تؤدي إلى انفجار عدد الصفوف. اختر أسوأ استعلام واجعله مملّاً.
أضف أو عدّل فهرساً واحداً، ثم أعد الاختبار. الفهارس تساعد عندما تطابق WHERE وORDER BY. لا تضف خمسة دفعة واحدة. إذا كانت نقطة النهاية البطيئة "قائمة الطلبات حسب user_id مرتبة بcreated_at"، ففهرس مركب على (user_id, created_at) قد يكون الفرق بين فوري ومؤلم.
قِص الاستجابات وعدّل الترقيم، ثم أعد الاختبار مجدداً. إن كانت نقطة النهاية تُرجع 50 صفاً بحقول JSON كبيرة، ستدفع قاعدة البيانات والشبكة والعميل الثمن. أعد فقط الحقول التي يحتاجها الواجهة، وفضّل ترقيماً لا يبطئ مع نمو الجداول.
احتفظ بسجل تغييرات بسيط: ماذا تغير، لماذا، وما الذي تحرك في p95. إن لم يحسّن تغييرٌ ما خط الأساس، ارجعه وامضِ قدماً.
معظم مشكلات الأداء في واجهات Go على Postgres من صنعنا. الخبر الجيد أن بعض الفحوصات تلتقط كثيراً منها قبل وصول الحركة الحقيقية.
فخ كلاسيكي هو اعتبار حجم التجمع كمقبض سرعة. ضبطه "عالياً قدر الإمكان" غالباً يجعل الأمور أبطأ. Postgres يقضي وقتاً أكثر في إدارة الجلسات والذاكرة والأقفال، ويبدأ تطبيقك بالمهلة على موجات. تجمع أصغر ومستقر مع توافق حرفي عادةً يفوز.
خطأ شائع آخر هو "فهرسة كل شيء." الفهارس الإضافية قد تساعد القراءة، لكنها تبطئ الكتابة وتغير خطط الاستعلام بطرق مفاجئة. إن كان تطبيقك يدخل أو يحدث كثيراً، كل فهرس إضافي يعمل عملة إضافية. قِس قبل وبعد، وأعد فحص الخطط بعد إضافة فهرس.
دين الترقيم يتسلل بهدوء. الترقيم بالـ offset يبدو جيداً مبكراً، ثم يرتفع p95 مع الزمن لأن قاعدة البيانات تضطر لتجاوز المزيد والمزيد من الصفوف.
حجم حمولة JSON ضريبي آخر مخفي. الضغط يساعد عرض النطاق، لكنه لا يزيل تكلفة البناء، والتخصيص، والتحليل. قلّص الحقول، تجنّب التعشيق العميق، وأعد فقط ما يحتاجه الشاشة.
إن راقبت المتوسط فقط، ستفوت مكان بدأ ألم المستخدم الحقيقي. p95 (وأحياناً p99) هو المكان الذي يظهر فيه احتقان التجمع، انتظار الأقفال، والخطط البطيئة أولاً.
فحص سريع قبل الإطلاق:
EXPLAIN بعد إضافة فهارس أو تغيير فلاتر.قبل وصول المستخدمين الحقيقيين، تريد دليلاً على أن API يبقى متوقعاً تحت الضغط. الهدف ليس أرقام مثالية. إنه التقاط القضايا القليلة التي تسبب مهلات، قفزات، أو قاعدة بيانات تتوقف عن قبول عمل جديد.
شغّل الفحوص في بيئة staging تشبه الإنتاج (حجم DB مماثل، نفس الفهارس، نفس إعدادات التجمع): قِس p95 لكل نقطة رئيسية تحت الحمولة، التقط أبطأ الاستعلامات حسب الوقت الكلي، راقب وقت انتظار التجمع، شغّل EXPLAIN (ANALYZE, BUFFERS) لأوسع استعلام لتتأكد أنه يستخدم الفهرس الذي تتوقعه، وتحقق عقلانياً من أحجام الحمولات على المسارات الأكثر نشاطاً.
ثم نفّذ تجربة أسوأ حالة تحاكي كيف تنكسر المنتجات: اطلب صفحة عميقة، طبّق أعرض فلتر، وجرب ذلك في بداية باردة (أعد تشغيل الـ API واضرب نفس الطلب أولاً). إذا كان الترقيم العميق يزداد بطئاً كل صفحة، انتقل إلى ترقيم بالـ cursor قبل الإطلاق.
دون قواعد افتراضية حتى يختار الفريق بتناسق لاحقاً: حدود التجمع والمهلات، قواعد الترقيم (أقصى حجم صفحة، إن كان الـ offset مسموحاً أم لا، صيغة الـ cursor)، قواعد الاستعلام (اختر الأعمدة اللازمة فقط، تجنّب SELECT *, حد الفلاتر المكلفة)، وقواعد التسجيل (عتبة الاستعلام البطيء، كم تدوم العينات، كيفية وسم النقاط).
إذا كنت تُنشئ وتصدّر خدمات Go + Postgres مع Koder.ai، فإن تمريرة تخطيط قصيرة قبل النشر تساعد على الحفاظ على الفلاتر، الترقيم، وأشكال الاستجابات مقصودة. بمجرد أن تبدأ بضبط الفهارس وأشكال الاستعلامات، تجعل اللقطات والاسترجاع من السهل التراجع عن "تحسين" يفيد نقطة ويؤذي أخرى. إن أردت مكاناً واحداً لتكرار تلك الدورة، فإن Koder.ai على koder.ai مصممة حول توليد وتنقيح تلك الخدمات عبر الدردشة، ثم تصدير المصدر عندما تكون جاهزاً.
ابدأ بفصل زمن الانتظار في قاعدة البيانات عن وقت عمل التطبيق.
أضف توقيت بسيط حول "انتظار الحصول على اتصال" و"تنفيذ الاستعلام" لترى أي جانب يهيمن.
استخدم خط أساس صغير قابل للتكرار:
اختر هدفاً واضحاً مثلاً: «p95 أقل من 200 ملّي ثانية عند 50 مستخدماً متزامناً، والأخطاء أقل من 0.5%». غيّر شيئاً واحداً في كل مرة وأعد اختبار نفس مزيج الطلبات.
فعل تسجيل الاستعلامات البطيئة بعتبة منخفضة أثناء اختبارات ما قبل الإطلاق (مثلاً 100–200 ملّي ثانية) وسجّل الجملة بالكامل لتتمكن من نسخها إلى عميل SQL.
احتفظ بذلك مؤقتاً:
بعد العثور على المسببات الأبرز، انتقل إلى أخذ عينات أو ارفع العتبة.
قاعدة عملية: عدة أضعاف صغيرة لعدد أنوية CPU لكل مثيل API، غالباً من 5 إلى 20 اتصالاً مفتوحاً كحد أقصى، مع عدد مماثل من الاتصالات الخاملة، وإعادة تدوير الاتصالات كل 30–60 دقيقة.
حالتان شائعتان للفشل:
تذكّر أن التجمعات تتكاثر عبر المثيلات (20 اتصال × 10 مثيلات = 200 اتصال).
قِس المكالمات لقاعدة البيانات بجزئين:
إذا كان معظم الوقت في انتظار التجمع، عدل حجم التجمع والمهلات وعدد المثيلات. إذا كان معظم الوقت في التنفيذ، ركّز على EXPLAIN والفهارس.
وتأكد دائماً من إغلاق الصفوف promptly حتى تعود الاتصالات إلى التجمع.
شغّل EXPLAIN (ANALYZE, BUFFERS) على SQL الذي يرسله API تماماً وابحث عن:
يبنى الفهرس فقط عندما يطابق كيف يستعلم تطبيقك عن البيانات. إن أنشأته من تخمينات، فستحصل على كتابة أبطأ ومساحة تخزين أكبر وفائدة ضئيلة.
القاعدة العملية:
مثال: إذا كنت تستعلم وتعرض الأحدث أولاً، فهرس مثل مناسب.
استخدم فهرساً جزئياً عندما يستهدف معظم الحركة شريحة متوقعة من الصفوف.
نمط مثال:
active = trueفهرس جزئي مثل ... WHERE active = true يبقى أصغر، أكثر احتمالاً للبقاء في الذاكرة، ويقلل تحميل الكتابة مقارنة بفهرسة كل شيء.
تحقق عبر أن Postgres فعلاً يستخدمه لطلبات المرور العالي.
LIMIT/OFFSET يزداد بطئاً في الصفحات العميقة لأن Postgres يظل يمر على (وغالباً يرتّب) الصفوف التي تتخطاها. الصفحة 1 قد تلمس عدد صفوف قليل؛ الصفحة 500 قد تجبر قاعدة البيانات على مسح واستبعاد عشرات الآلاف لإرجاع 20 نتيجة.
فضلًا عن ذلك استخدم ترقيم keyset (cursor):
عادةً نعم، خصوصاً لنقاط القوائم. أسرع استجابة هي التي لا ترسلها.
خدمات عملية:
SELECT *).include= أو fields= ليختار العميل الحقول الثقيلة.ORDER BY)اصلح أكبر علامة حمراء أولاً؛ لا تحاول ضبط كل شيء دفعة واحدة.
GET /orders?status=paid(status, created_at DESC)EXPLAINid).ORDER BY مماثلاً عبر الطلبات.(created_at, id) أو ما شابه إلى cursor.بهذه الطريقة تبقى تكلفة كل صفحة تقريباً ثابتة مع نمو الجداول.
ستقلّ غالباً CPU وذاكرة Go وزمن الذيل فقط بتقليص الحمولات.