الوحدة 3 — المخزن غير المتّصل والمخزن المتّصل
ذكرنا في الوحدة السابقة أنّ مخزن المتغيّرات يحمل تخزينَين. هذه الوحدة تُفصّل لماذا اثنان، وما التكنولوجيّات المناسبة لكلّ منهما، وكيف نضمن ألّا يتباعد ما يقولانه.
قيدان لا يجتمعان في تكنولوجيا واحدة
نموذج تسجيل الاحتيال يحتاج، بحسب المرحلة، شيئين مختلفين تمامًا:
عند التدريب، نحتاج مليون معاملة تاريخيّة، مع لكلٍّ منها قيمة count_1h كما كانت ف ي اللحظة التي وقعت فيها. الاستعلام يُرجع ملايين الصفوف، ويجري مرّة واحدة في الليلة، ولا يهمّه أن يستغرق دقيقتين. الحجم كبير، والقراءة متسلسلة على أعمدة.
عند الخدمة، نحتاج لثلاثة معرّفات كيانات (customer_id, merchant_id, card_bin) قيمة آخر count_1h، وحدها هذه القيمة. الاستعلام يُرجع خمسة أعداد، ويجري 800 مرّة في الثانية، وعليه أن ينتهي في أقلّ من 5 مليّثانيات. الحجم ضئيل، والقراءة مفاتيح-قيم.
كلّ محاولة لخدمة الاثنين من قاعدة واحدة تفشل بأحد شكلين: قاعدة عموديّة (BigQuery, Snowflake) تُعطي التاريخ بلا مشكلة لكنّها لا تنزل تحت مئات المليّثانيات، أو قاعدة مفاتيح-قيم (Redis, DynamoDB) تُعطي 1 مليّثانية لكنّها لا تُخزّن التاريخ اقتصاديًّا.
المخزن غير المتّصل
الخيارات النموذجيّة:
| التكنولوجيا | متى نختارها |
|---|---|
| Parquet على S3 أو GCS | مشاريع صغيرة، مختبر، تكلفة دنيا |
| Delta Lake أو Iceberg | إدارة الإصدارات، حذف الصفوف (GDPR) |
| BigQuery, Snowflake | ما يوجد أصلًا في المؤسّسة، عدد مستعملين كبير |
| Databricks | كاملة مع الحوسبة والدفاتر |
في مشروعنا، سنستعمل Parquet على قرص محلّي لبساطة التشغيل، ويعمل Feast معه من دون ضبط إضافيّ.
المخزن غير المتّصل يحفظ التاريخ كاملًا، مُدعَّمًا بطابع زمنيّ لكلّ صفّ. هذا الحفظ للتاريخ هو ما يسمح لاحقًا بالدمج الصحيح زمنيًّا (الوحدة 5).
المخزن المتّصل
الخيارات النموذجيّة:
| التكنولوجيا | ملاحظات |
|---|---|
| Redis | افتراضيّ في Feast، سهل التركيب، حالة حرجة في الذاكرة |
| DynamoDB | مُدار كليًّا في AWS، ثمن حسب الاستعمال، دائم |
| Bigtable | نظير في GCP، مناسب لأحجام كبيرة |
| Cassandra | متعدّد المناطق، صعب الإدارة |
في المشروع سنستعمل Redis محلّيًّا. ما يستحقّ الفهم هو أنّ المخزن المتّصل يخزّن آخر قيمة لكلّ (كيان × متغيّر)، لا التاريخ. الأثر: كتلة بيانات صغيرة، وقراءة بمليّثانية أو أقلّ.
المفتاح داخل المخزن المتّصل هو تركيبة (feature_view, entity_id). هذا يعني عمليًّا أنّ الوصفات المُختلفة لا تتص ادم، وأنّ تحديث متغيّر لا يمسّ المتغيّرات الأخرى للكيان نفسه.
الجسر بين الاثنين: التجسيد
الاتّساق بين المخزنين لا يأتي بالصدفة. تُشغّله عمليّة تُسمّى التجسيد (materialization): مسح صفوف المخزن غير المتّصل الأحدث من وقت معيّن، وحسابها في وصفة المتغيّر، وكتابة النتيجة في المخزن المتّصل.
جدولة نموذجيّة: تجسيد كلّ ساعة للعدّادات القصيرة، وتجسيد كلّ ليلة للعدّادات على 7 أيّام. سنتفصّل في الوحدة 6.
الفكرة الأساسيّة هنا: المخزن المتّصل ليس مصدرًا للحقيقة، بل نسخة سريعة. الحقيقة تبقى في المخزن غير المتّصل. لو ضاع Redis غدًا، يُعاد تجسيد كلّ شيء انطلاقًا من Parquet، من دون فقدان ولا تقريب.
كيف يُضمَن التطابق فعلًا
ثلاث تقنيّات مجتمعة تضمن ألّا تنفصل القيمتان:
- حساب واحد: الوصفة نفسها تعمل على الاثنين. لا يُوجد كود تجميع مختلف بين التدريب والخدمة، بل تعريف واحد يُنفَّذ بمحرّكين (SparkSQL على المخزن غير المتّصل، ونفس المنطق على تدفّق Kafka نحو المخزن المتّصل).
- دمج زمنيّ صحيح: عند بناء مجموعة التدريب، لا تُؤخَذ القيمة الأحدث من المخزن المتّصل، بل تُستَرجَع من غير المتّصل القيمة كما كانت في زمن الحدث (الوحدة 5).
- اختبارات مقارنة: يمكن تشغيل مهمّة تختار كيانًا عشوائيًّا، وتُخرج قيمته من المخزنَين، وتقارن. هذه المهمّة تكشف انحرافات الترميز والمنطقة الزمنيّة قبل أن يكتشفها العملاء.
كلفة الاختيارات
المخزن المتّصل هو مركز التكلفة الأساسيّ في الإنتاج. Redis محلّيًّا شبه مجّانيّ، لكنّ Redis مُدار عبر عدّة مناطق مع تكرار، أو DynamoDB مع 1000 قراءة/ثانية، يكلّف أرقامًا محسوسة. القرارات المُعتادة لتخفيض التكلفة:
- TTL قصير على المتغيّرات التي لا تُستعمل إلّا لفترة قصيرة (بضع دقائق للمُصادَقة على البيع الفوريّ)
- ضغط أوّليّ للمتغيّرات النادرة الاستعمال في مخزن أرخص
- تجميع الوصفات القريبة في مفتاح واحد لتقليل عدد استعلامات Redis
بيانات شخصيّة كاملة (اسم، بريد، عنوان) لا تُخزّن في المخزن المتّصل: هذا يُخالف قواعد الاحتفاظ ويُصعّب التعامل مع طلبات المحو (GDPR). المتّصل يحمل متغيّرات مُشتقّة رقميّة أو فئويّة، لا معلومات هويّة خام.
الخلاصة
- قيدان مختلفان (حجم وتاريخ ضدّ سرعة وآخر قيمة) يفرضان تخزينَين لا تخزينًا واحدًا.
- المخزن غير المتّصل مصدر الحقيقة (Parquet, Delta, BigQuery)؛ المخزن المتّصل نسخة سريعة (Redis, DynamoDB, Bigtable).
- التجسيد يجسر بينهما بمنطق واحد، مع اختبارات مقارنة لضمان التطابق.
- تكلفة المخزن المتّصل قابلة للضبط بـTTL وتجميع الوصفات.
الوحدة الت الية: كيف نُصرّح وصفة متغيّر ونُصدرها من دون أن نكسر النماذج القائمة.