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

تقسيم PostgreSQL لجداول الأحداث في تسجيل التدقيق

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

تقسيم PostgreSQL لجداول الأحداث في تسجيل التدقيق

لماذا تصبح جداول الأحداث والتدقيق مشكلة

تبدو جداول الأحداث وجداول التدقيق متشابهة، لكنها موجودة لأسباب مختلفة.

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

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

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

ستلاحظ عادةً بعض الأعراض أولًا:

  • الفلاتر تستغرق ثوانٍ (أو تتوقف) حتى مع نطاق زمني ضيّق.
  • الفهارس تكبر بحيث تتباطأ الإدخالات وتزداد تكلفة التخزين.
  • تستغرق عمليات VACUUM و autovacuum وقتًا أطول، وتبدأ ملاحظات الصيانة بالظهور.
  • يصبح الاحتفاظ خطيرًا: حذف الصفوف القديمة بطيء ويخلق نفخًا في البيانات.

التقسيم (partitioning) هو طريقة للتعامل مع هذا. ببساطة، يقسم جدولًا كبيرًا إلى جداول أصغر كثيرة (أقسام) تشترك في اسم منطقي واحد. يقوم PostgreSQL بتوجيه كل صف جديد إلى القسم المناسب بناءً على قاعدة، غالبًا الوقت.

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

التقسيم ليس مفتاح سرعة سحري. يمكن أن يساعد كثيرًا في استعلامات مثل "آخر 7 أيام" ويجعل الاحتفاظ أبسط (حذف الأقسام القديمة سريع). لكنه قد يخلق أيضًا مشكلات جديدة:

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

إذا كانت لوحة الإدارة تعتمد كثيرًا على فلاتر زمنية وقواعد احتفاظ متوقعة، يمكن أن يكون التقسيم مكسبًا حقيقيًا. إذا كانت معظم الاستعلامات هي "العثور على كل الإجراءات للمستخدم X عبر التاريخ كله" فقد يسبب متاعب ما لم تصمم واجهة المستخدم والفهارس بعناية.

أنماط الوصول النموذجية للسجلات والتدقيق

تنمو جداول الأحداث والتدقيق في اتجاه واحد: صعودًا. تحصل على تدفق ثابت من الإدخالات وقليل من التحديثات. تُكتب معظم الصفوف مرة واحدة ثم تُقرأ لاحقًا أثناء عمل الدعم، مراجعات الحوادث، أو الفحوصات الامتثالية.

شكل "الإلحاق فقط" هذا مهم. أداء الكتابة مصدر قلق دائم لأن الإدخالات تحدث طوال اليوم، بينما يهم أداء القراءة في دفعات (عندما يحتاج الدعم أو العمليات لإجابات سريعة).

معظم القراءات هي فلاتر، ليست بحثًا عشوائيًا. في لوحة الإدارة، يبدأ أحدهم عادةً بصورة واسعة (آخر 24 ساعة) ثم يضيق إلى مستخدم، كيان، أو إجراء.

الفلاتر الشائعة تشمل:

  • نطاق زمني
  • فاعل (معرف المستخدم، حساب الخدمة، عنوان IP)
  • هدف (نوع الكيان + معرف الكيان، مثل Order #1234)
  • نوع الإجراء (تم الإنشاء، تم التحديث، تم الحذف، فشل تسجيل الدخول)
  • حالة أو شدة (نجاح/خطأ)

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

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

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

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

كيف تعرف ما إذا كان التقسيم يستحق العناء

التقسيم ليس ممارسة قياسية افتراضية لسجلات التدقيق. يحقق فائدة عندما يصبح جدول واحد كبيرًا جدًا بحيث تتعارض الاستعلامات اليومية والصيانة الروتينية.

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

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

تشابكات الصيانة مهمة بنفس القدر:

  • تستغرق عمليات VACUUM و autovacuum وقتًا أطول مما كانت عليه.
  • يتأخر autovacuum وتتراكم الصفوف الميتة (النفخ).
  • تنمو الفهارس أسرع من المتوقع، خصوصًا الفهارس متعددة الأعمدة.
  • يظهر احتدام الأقفال عندما تتداخل الصيانة مع حركة المرور.

التكاليف التشغيلية هي القطرة البطيئة التي تدفع الفرق نحو التقسيم. تصبح النسخ الاحتياطية والاستعادة أبطأ مع نمو جدول واحد، ويتسرب التخزين، وتصبح وظائف الاحتفاظ مكلفة لأن الحذف الكبير يخلق نفخًا وعملًا إضافيًا للـ vacuum.

إذا كانت أهدافك الرئيسية سياسة احتفاظ نظيفة واستعلامات أسرع للفترات الحديثة، فالتقسيم عادةً يستحق النظر بجدية. إذا كان الجدول متوسطًا والاستعلامات سريعة مع فهرسة جيدة، قد يضيف التقسيم تعقيدًا دون منفعة واضحة.

خيارات التقسيم المناسبة لجداول الأحداث والتدقيق

لغالبية بيانات التدقيق والأحداث، الخيار الأبسط هو تقسيم النطاق حسب الزمن. تدخل السجلات بترتيب زمني، وغالبًا ما تركز الاستعلامات على "آخر 24 ساعة" أو "آخر 30 يومًا"، والاحتفاظ عادةً مبني على الوقت. مع أقسام زمنية، يمكن أن يصبح حذف البيانات القديمة بسيطًا كسقوط قسم قديم بدلًا من تشغيل DELETE كبير يسبب نفخًا.

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

هناك أنماط تقسيم أخرى، لكنها تنطبق على حالات أقل:

  • List (حسب العميل/المستأجر) يمكن أن تعمل عندما يكون لديك عدد قليل من المستأجرين الكبار جدًا وتبقى الاستعلامات داخل مستأجر واحد عادةً. تصبح مؤلمة عندما يكون لديك مئات أو آلاف من المستأجرين.
  • Hash (توزيع متساوٍ للكتابة) يمكن أن يساعد عندما لا تملك استعلامات نافذة زمنية وتريد توزيع الكتابات بالتساوي. بالنسبة لسجلات التدقيق، أقل شيوعًا لأن ذلك يجعل الاحتفاظ والتصفح الزمني أصعب.
  • Subpartitioning (الزمن زائد المستأجر) يمكن أن يكون قويًا، لكن التعقيد ينمو بسرعة. مخصص للأنظمة عالية الحجم التي تحتاج عزلًا صريحًا بين المستأجرين.

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

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

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

كيف تختار مفتاح التقسيم الصحيح

شحن نقاط نهاية جاهزة للتقسيم
أنشئ واجهات برمجة لتصفح الأحداث حسب نافذة زمنية وترقيم المؤشر بدون كتابة شيفرة يدوية.
بناء الخلفية

مفتاح التقسيم الجيد يطابق كيف تقرأ الجدول، لا كيف تبدو البيانات في مخطط.

بالنسبة للسجلات والتدقيق، ابدأ بلوحة الإدارة: ما الفلتر الذي يستخدمه الناس أولًا، تقريبًا في كل مرة؟ بالنسبة لمعظم الفرق، هو نطاق زمني (آخر 24 ساعة، آخر 7 أيام، تواريخ مخصّصة). إذا كان هذا صحيحًا لديك، يعطى التقسيم بالزمن أكبر فائدة متوقعة لأن PostgreSQL يمكنه تجاهل أقسام كاملة خارج النطاق المحدد.

عامل المفتاح كتعهد طويل الأمد. أنت تُحسّن للاستعلامات التي ستظل قيد التشغيل لسنوات.

ابدأ بـ "الفلتر الأول" الذي يستخدمه الناس

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

فحص واقعي سريع:

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

تجنّب المفاتيح عالية التفرّد

يعمل التقسيم بشكل أفضل عندما يكون كل قسم قطعة بيانات ذات معنى. مفاتيح مثل user_id، session_id، request_id، أو device_id قد تؤدي إلى آلاف أو ملايين من الأقسام. هذا يزيد العبء التعريفي، يعقّد الصيانة، وغالبًا يبطئ التخطيط.

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

اختر الطابع الزمني المناسب: created_at أم occurred_at

كن صريحًا حول معنى الوقت:

  • occurred_at: متى حدث الحدث في المنتج.
  • created_at: متى سجّلته قاعدة البيانات.

بالنسبة للتدقيق، غالبًا ما يهتم المدراء بـ "وقت الحدوث". لكن وصول الرسائل المتأخر (عملاء جوال دون اتصال، إعادة المحاولة، قوائم الانتظار) يعني أن occurred_at قد يصل متأخرًا. إذا كانت تكرارات الوصول المتأخر شائعة، قد يكون التقسيم حسب created_at وفهرسة occurred_at للفلترة أكثر استقرارًا تشغيليًا. الخيار الآخر هو تحديد سياسة تعبئة خلفية واضحة وقبول أن الأقسام القديمة ستتلقى أحداثًا متأخرة أحيانًا.

حدد أيضًا كيف تخزن الوقت. استخدم نوعًا ثابتًا (غالبًا timestamptz) واعتبر UTC مصدر الحقيقة. قم بالتنسيق لعرض المنطقة الزمنية للمستخدم في الواجهة. هذا يحافظ على حدود الأقسام مستقرة ويتجنب مفاجآت تغيير التوقيت الصيفي.

خطوة بخطوة: التخطيط ونشر التقسيم

خطط للاحتفاظ بدون ألم
حوّل قواعد الاحتفاظ إلى نموذج بيانات واجعل العمليات متوقعة.
ابدأ الآن

التقسيم أسهل عندما تتعامل معه كمشروع ترحيل صغير، وليس تعديلًا سريعًا. الهدف هو كتابة بسيطة، قراءات متوقعة، واحتفاظ يصبح عملية روتينية.

خطة نشر عملية

  1. اختر حجم القسم الذي يتناسب مع حجمك. الأقسام الشهرية عادةً كافية عند مئات الآلاف من الصفوف شهريًا. إذا كنت تدخل عشرات الملايين شهريًا، فالأقسام الأسبوعية أو اليومية تحافظ على الفهارس أصغر وعمل الـ vacuum أكثر احتواءً.

  2. صمم المفاتيح والقيود للجداول المقسمة. في PostgreSQL، يجب أن تتضمن القيد الفريد مفتاح التقسيم (أو تُطبق بطريقة أخرى). نمط شائع هو (created_at, id) حيث يُولد id و created_at هو مفتاح التقسيم. هذا يتجنب المفاجآت لاحقًا عندما تكتشف أن قيدًا اعتدت أن يكون مسموحًا ليس مسموحًا.

  3. أنشئ أقسامًا مستقبلية قبل الحاجة. لا تنتظر أن تفشل الإدخالات لأن لا يوجد قسم مطابق. قرر إلى أي مدى تسبق الإنشاء (مثلاً شهرين-ثلاثة) واجعلها وظيفة روتينية.

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

  5. خطط للاحتفاظ حول إسقاط الأقسام، لا حذف الصفوف. إذا تحتفظ بـ 180 يومًا من السجلات، فإن إسقاط قسم قديم سريع ويتجنب عمليات DELETE طويلة المدى و النفخ. دون القاعدة، من يديرها ومن يحقّق من نجاحها.

مثال صغير

إذا كان جدول التدقيق يتلقى 5 ملايين صف أسبوعيًا، فالأقسام الأسبوعية على created_at بداية معقولة. أنشئ أقسامًا قبل 8 أسابيع واحتفظ بفهرسين لكل قسم: واحد للبحث الشائع حسب actor_id وآخر لـ entity_id. عندما ينتهي نافذة الاحتفاظ، احذف أقدم قسم أسبوعي بدلًا من حذف ملايين الصفوف.

إذا كنت تبني أدوات داخلية في AppMaster، يساعد اتخاذ قرار مفتاح التقسيم والقيود مبكرًا حتى يتبع نموذج البيانات والشيفرة المولّدة نفس الافتراضات من اليوم الأول.

ما الذي يغيّره التقسيم في فلاتر لوحة الإدارة

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

أكبر تغيير عملي: لم يعد الوقت اختياريًا. إذا سمحت الواجهة بتشغيل بحث غير محدود (بدون نطاق تاريخ، فقط "أظهر كل شيء للمستخدم X"), فقد تحتاج PostgreSQL إلى فحص كل قسم. حتى لو كان كل فحص سريعًا، فتح أقسام كثيرة يضيف عبئًا وتشعر الصفحة بالبطء.

قاعدة جيدة: اجعل نطاق التاريخ مطلوبًا لعمليات البحث في السجلات والتدقيق وعيّنه افتراضيًا إلى شيء معقول (مثل آخر 24 ساعة). إذا احتاج شخص ما فعلاً "لكل الوقت"، اجعلها خيارًا متعمدًا وحذِّر من أن النتائج قد تكون أبطأ.

اجعل الفلاتر تتوافق مع تقليم الأقسام

يساعد تقليم الأقسام فقط عندما تتضمن عبارة WHERE مفتاح التقسيم بطريقة يمكن لـ PostgreSQL استخدامها. الفلاتر مثل created_at BETWEEN X AND Y تقليمها واضح. الأنماط التي تكسر التقليم غالبًا تشمل تحويل الطابع الزمني إلى تاريخ، غلاف العمود داخل دالة، أو الفلترة بشكل أساسي على عمود زمن مختلف عن مفتاح التقسيم.

داخل كل قسم، يجب أن تطابق الفهارس كيفية تصفية الناس فعليًا. عمليًا، التوليفات التي تهم غالبًا هي الزمن زائد شرط واحد آخر: مستأجر/مساحة عمل، مستخدم، نوع الإجراء، معرف الكيان، أو الحالة.

الفرز والترقيم: اجعله مسطحًا

التقسيم لن يحل مشكلة الترقيم البطيء بنفسه. إذا كانت لوحة الإدارة ترتب بحسب الأحدث أولًا ويقفز المستخدمون إلى الصفحة 5000، فإن الترقيم العميق باستخدام OFFSET يجبر PostgreSQL على تخطّي الكثير من الصفوف. الترقيم بنمط المؤشر أفضل للسجلات: "حمل الأحداث قبل هذا الطابع الزمني/المعرف". يبقي قاعدة البيانات تستخدم الفهارس بدل تخطّي عدد ضخم من الصفوف.

الإعدادات المسبقة تساعد هنا أيضًا. قليل من الخيارات عادةً كافٍ: آخر 24 ساعة، آخر 7 أيام، اليوم، الأمس، نطاق مخصص. تقلل الإعدادات المسبقة عمليات "المسح الكلي" العرضية وتجعل تجربة الإدارة أكثر توقعًا.

أخطاء ومطبات شائعة

بناء شاشات سجلات أسرع
أنشئ عارض سجلات للإدارة مع فلاتر زمنية مطلوبة واستعلامات سريعة من اليوم الأول.
جرّب AppMaster

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

1) التقسيم على عمود الزمن الخطأ

يحدث تقليم الأقسام فقط عندما تطابق عبارة WHERE مفتاح التقسيم. خطأ شائع هو التقسيم حسب created_at بينما تقوم واجهة الإدارة بالفلترة حسب event_time (أو العكس). إذا كان فريق الدعم يسأل دائمًا "ماذا حدث بين 10:00 و10:15"، لكن الجدول مقسّم حسب زمن الاستيعاب، فقد تلمس بيانات أكثر من المتوقع.

2) إنشاء أقسام صغيرة جدًا

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

ما لم يكن لديك حجم كتابة ضخم جدًا واحتفاظ صارم، تكون الأقسام اليومية أو الشهرية أسهل التشغيل.

3) افتراض أن "التفريد العالمي" لا يزال يعمل

لجداول مقسمة قيود: بعض الفهارس الفريدة يجب أن تتضمن مفتاح التقسيم، وإلا لا يستطيع PostgreSQL فرضها عبر كل الأقسام.

يفاجئ هذا الفرق التي تتوقع أن event_id فريد دائمًا. إذا احتجت معرفًا فريدًا، استخدم UUID واجعله فريدًا مع مفتاح الزمن، أو طبق التفريد في طبقة التطبيق.

4) السماح لواجهة الإدارة بتشغيل بحثات مفتوحة على مصراعيها

غالبًا ما تُشحن لوحات الإدارة بمربع بحث ودود يعمل بلا فلاتر. على جدول سجلات مقسّم، قد يعني ذلك مسح كل قسم.

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

5) لا سياسة احتفاظ (ولا خطة للأقسام)

التقسيم لا يحل الاحتفاظ تلقائيًا. بدون سياسة، ينتهي بك الأمر مع كومة من الأقسام القديمة، تخزين فوضوي، وصيانة أبطأ.

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

قائمة تحقق سريعة قبل الالتزام

التكرار بدون دين تقني
انتقل من المخطط إلى التطبيق في الإنتاج بسرعة، ثم عدّل مع تغيّر المتطلبات.
ابدأ البناء

يمكن أن يكون التقسيم مكسبًا كبيرًا لسجلات التدقيق، لكنه يضيف عملًا روتينيًا. قبل تغيير المخطط، تحقق بعقلانية كيف يستخدم الناس الجدول فعليًا.

إذا كانت المشكلة الأساسية أن صفحات الإدارة تنتهي مهلة عندما يفتح أحدهم "آخر 24 ساعة" أو "هذا الأسبوع"، فأنت قريب من مناسبة جيدة. إذا كانت معظم الاستعلامات هي "معرف المستخدم عبر كل التاريخ"، قد يساعد التقسيم أقل ما لم تغير كيفية توجيه الواجهة للبحث.

قائمة تحقق قصيرة تحافظ على الفرق صادقة:

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

اختبار عملي: انظر إلى ثلاثة فلاتر التي يستخدمها فريق الدعم أو العمليات أكثر. إذا كان اثنان منها عادةً يُلبَّيان بواسطة "نطاق زمني + شرط واحد آخر"، فالتقسيم في PostgreSQL لجداول الأحداث غالبًا ما يستحق النظر الجدي.

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

فريق الدعم يحتفظ بشاشتين مفتوحتين طوال اليوم: "أحداث تسجيل الدخول" (ناجحة وفاشلة) و"تدقيقات الأمان" (إعادة تعيين كلمات المرور، تغييرات الأدوار، تحديثات مفاتيح API). عندما يبلغ عميل عن نشاط مريب، يفلتر الفريق حسب المستخدم، يتحقق من الساعات القليلة الماضية، ويصدر تقريرًا قصيرًا.

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

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

يبقى شيء واحد صعبًا: البحث الحر عبر "كل الوقت". إذا بحث شخص ما عن عنوان IP أو عبارة غامضة بدون حد زمني، لا يجعل التقسيم هذا رخيصًا. عادةً يكون الحل في سلوك المنتج: اجعل البحث الافتراضي ضمن نافذة زمنية واجعل "آخر 24 ساعة / 7 أيام / 30 يومًا" المسار الواضح.

خطوات عملية تالية التي تعمل جيدًا:

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

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

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

متى يكون تقسيم PostgreSQL مفيدًا فعلاً لجداول الأحداث أو التدقيق؟

يساعد التقسيم عادةً عندما تكون الاستعلامات الشائعة محدودة زمنياً (مثل "آخر 24 ساعة" أو "آخر 7 أيام") ويصبح الجدول كبيرًا بما يكفي لجعل الفهارس والصيانة مرهقة. إذا كانت الاستعلامات الأساسية هي "كل التاريخ لمستخدم X"، فقد يضيف التقسيم عبءً زائدًا ما لم تفرض فلاتر زمنية في واجهة المستخدم وتضيف الفهارس المناسبة لكل قسم.

ما هي طريقة التقسيم الأنسب للسجلات وبيانات التدقيق؟

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

كيف أختار مفتاح التقسيم الصحيح لجدول التدقيق؟

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

لماذا عادةً ما يكون تقسيم `user_id` فكرة سيئة؟

استخدم مفاتيح مثل الطابع الزمني أو معرف المستأجر فقط عندما تُنتج عددًا معقولًا من الأقسام. تجنّب المفاتيح عالية التفرّد مثل user_id أو session_id أو request_id لأنها قد تنشئ آلاف الأقسام، مما يزيد عبء التخطيط والعمليات دون منح تسريعات ثابتة.

هل يجب أن أقسّم حسب `created_at` أم `occurred_at`؟

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

هل أحتاج حقًا لفرض فلتر نطاق زمني في واجهة الإدارة؟

نعم. بعد تقسيم الجدول، يجب أن تطلب معظم لوحات الإدارة نطاقًا زمنيًا. بدون فلتر زمني، قد تحتاج PostgreSQL إلى فحص العديد من الأقسام أو كلها، مما يجعل الصفحات بطيئة حتى لو كان كل قسم مفهرسًا. افتراض جيد هو "آخر 24 ساعة"، واجعل "كل الوقت" خيارًا متعمدًا مع تحذير أن النتائج قد تكون أبطأ.

ما الذي يمكن أن يكسر تقليم الأقسام في استعلاماتي عن طريق الخطأ؟

غالبًا ما يحدث ذلك بالفعل. وضع الطابع الزمني داخل دالة (مثل التحويل إلى تاريخ) يمكن أن يمنع التقليم (pruning)، والفلترة على عمود زمن مختلف عن مفتاح التقسيم يمكن أن تجبر PostgreSQL على مسح أقسام إضافية. اجعل الفلاتر في شكل بسيط مثل created_at BETWEEN X AND Y ليعمل التقليم بشكل موثوق.

ما أفضل طريقة للترقيم (pagination) لجداول الأحداث المقسمة؟

تجنّب التصفح العميق باستخدام OFFSET لأن ذلك يجبر قاعدة البيانات على تخطّي الكثير من الصفوف. استخدم ترقيمًا قائمًا على المؤشر (cursor) مثل "تحميل الأحداث قبل هذا (timestamp, id)"، فهو يبقى صديقًا للفهرس ويحافظ على الأداء مع نمو الجدول.

كيف يؤثر التقسيم على القيود الفريدة والمعرفات؟

في PostgreSQL، يجب أن تتضمن بعض القيود الفريدة مفتاح التقسيم، وإلا فلن تستطيع قاعدة البيانات فرضها عبر جميع الأقسام. نمط عملي شائع هو التفريد المركب مثل (created_at, id) عندما يكون created_at مفتاح التقسيم. إذا احتجت معرفًا فريدًا للاستخدام الخارجي، احتفظ بـ UUID وتعامل مع التفريد العالمي بحذر.

ما أبسط استراتيجية احتفاظ بعد تقسيم الجدول؟

إسقاط الأقسام القديمة سريع ويتجنب النفخ والعمل الإضافي للـ VACUUM الناتج عن عمليات DELETE الكبيرة. المفتاح هو موائمة قواعد الاحتفاظ مع حدود الأقسام وأتمتة الروتين: أنشئ أقسام مستقبلية مقدمًا واحذف المنتهية بجدول زمني. بدون هذه الأتمتة، يتحول التقسيم إلى عبء يدوي.

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

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

البدء
تقسيم PostgreSQL لجداول الأحداث في تسجيل التدقيق | AppMaster