استراتيجية التخزين: Valkey مقابل JSON مقابل NumPy

يشرح هذا المقال سبب استخدامنا لتقنيات تخزين مختلفة لأنواع مختلفة من البيانات في خطة عمل SEO وخدمة البحث.

المشكلة: لا يناسب الحل الواحد جميع الاحتياجات

البيانات المختلفة لها أنماط وصول مختلفة:

  • التضمينات (Embeddings) (5K × 384 رقم عائم): تحتاج إلى عمليات متجهية سريعة (تشابه جيب التمام)

  • تعيينات العبارات (أكثر من 2500 عبارة): تحتاج إلى تحرير بشري، التحكم بالإصدارات

  • ذاكرة التخزين المؤقت للاستعلامات (استعلامات حية): تحتاج إلى عمليات بحث سريعة من نوع مفتاح-قيمة، وانتهاء صلاحية TTL

  • بيانات المنتج (64 ألف منتج): تحتاج إلى وصول منظم، قواعد التوافق

استخدام نفس التخزين لكل هذه الأنواع سيكون غير فعال.

ثلاث تقنيات تخزين

1. مصفوفات NumPy: العمليات المتجهية

حالة الاستخدام: التضمينات (المنتجات، الاستعلامات، العبارات)

لماذا NumPy:

  • رياضيات متجهية سريعة: مكتبات C/Fortran مُحسَّنة لعمليات المصفوفات

  • ملفات معينة للذاكرة (Memory-mapped): تحميل مصفوفات كبيرة دون نسخها إلى ذاكرة الوصول العشوائي (RAM)

  • عمليات الدُفعات: معالجة آلاف المتجهات في أجزاء من الثانية

  • تنسيق قياسي: متوافق مع مكتبات تعلم الآلة (scikit-learn, TensorFlow)

تنسيق الملف: ملفات ثنائية .npy

التحميل:

import numpy as np

# معينة للذاكرة (لا تحمل الملف بأكمله إلى ذاكرة الوصول العشوائي)
embeddings = np.load('embeddings.npy', mmap_mode='r')

# حساب تشابه جيب التمام
from sklearn.metrics.pairwise import cosine_similarity
similarities = cosine_similarity(query_embedding, embeddings)

الأداء: عمليات بحث ضخمة عن التشابه بسرعات مناسبة للاستدلال الفوري (real-time inference).

2. ملفات JSON: البيانات القابلة للتحرير البشري

حالة الاستخدام: التكوين، التعيينات، البيانات الوصفية (Metadata)

لماذا JSON:

  • قابلة للقراءة البشرية: سهلة الفحص والتشخيص

  • التحكم بالإصدارات: يعرض Git diff بالضبط ما تغير

  • التحرير اليدوي: يمكن إصلاح الأخطاء دون الحاجة إلى كود

  • تنسيق عالمي: كل لغة برمجة يمكنها تحليل JSON

ما نخزنه:

  • تعيينات العبارة إلى المرشح (Filter): عبارات → قواعد التصفية

  • ميزات المنتج: منتجات → قواميس الميزات

  • البيانات الوصفية للاستعلام: استعلامات → نقرات، ظهورات، مصادر

  • تكوين خط العمل: معاملات الخطوة، العتبات

تنسيق الملف: ملفات نصية .json

التحميل:

import json

with open('phrase_mappings.json') as f:
    mappings = json.load(f)

# الوصول إلى البيانات
filters = mappings['mini pc']  # ['form_factor:mini', 'category:pc']

3. Valkey (Redis): ذاكرة تخزين مؤقت سريعة من نوع مفتاح-قيمة

حالة الاستخدام: البحث المباشر، الإكمال التلقائي، الاستعلامات الشائعة

لماذا Valkey:

  • في الذاكرة: زمن انتقال ميكروثانية لعمليات البحث

  • RediSearch: بحث تشابه متجهي مع فهارس

  • انتهاء صلاحية TTL: إبطال ذاكرة التخزين المؤقت تلقائيًا

  • النشر/الاشتراك (Pub/sub): تحديثات فورية عبر الخوادم

  • الثبات (Persistence): لقطات قرص اختيارية للمتانة

ما نخزنه:

  • ذاكرة التخزين المؤقت لتضمينات الاستعلام: الاستعلامات الحديثة → تضمينات

  • الاستعلامات الشائعة: أفضل 1000 استعلام حسب حركة المرور

  • فهرس الإكمال التلقائي: البادئة → اقتراحات الاستعلام

  • ذاكرة التخزين المؤقت لاستخراج المرشحات: الاستعلام → المرشحات المستخرجة

  • ذاكرة التخزين المؤقت للبحث ذي الصلة: الاستعلام → استعلامات ذات صلة

هياكل البيانات:

  • السلاسل النصية (Strings): مفتاح-قيمة بسيط (استعلام → تضمين)

  • المجموعات المرتبة (Sorted sets): بيانات مصنفة (استعلامات شائعة حسب النتيجة)

  • فهارس RediSearch: بحث تشابه متجهي

  • الجداول التجزئة (Hashes): بيانات منظمة (بيانات وصفية للاستعلام)

التحميل:

from app.shared.valkey_cache import get_valkey

valkey = get_valkey()
# ... (تفاصيل التنفيذ محذوفة)

مصفوفة القرار

متى تستخدم NumPy

المعايير:

  • البيانات رقمية (أرقام عائمة، أعداد صحيحة)

  • تحتاج إلى عمليات متجهية سريعة (ضرب نقطي، تشابه جيب التمام)

  • البيانات ثقيلة القراءة (نادرًا ما يتم تحديثها)

  • البيانات كبيرة (ملايين الأرقام)

  • معالجة دُفعية (معالجة العديد من العناصر دفعة واحدة)

أمثلة:

  • التضمينات (المنتجات، الاستعلامات، العبارات)

  • نواقل الميزات (Feature vectors)

  • مصفوفات التشابه

متى تستخدم JSON

المعايير:

  • البيانات منظمة (كائنات، مصفوفات)

  • تحتاج إلى قابلية القراءة البشرية

  • تحتاج إلى التحكم بالإصدارات (Git)

  • البيانات تتغير أحيانًا (تحرير يدوي)

  • البيانات صغيرة-متوسطة (<100 ميجابايت)

أمثلة:

  • ملفات التكوين

  • تعيينات العبارات

  • البيانات الوصفية للمنتج

  • معاملات خط العمل

متى تستخدم Valkey

المعايير:

  • تحتاج إلى عمليات بحث سريعة (ميكروثانية)

  • البيانات تتغير بشكل متكرر (استعلامات حية)

  • تحتاج إلى انتهاء صلاحية TTL (إبطال ذاكرة التخزين المؤقت)

  • تحتاج إلى نشر/اشتراك (تحديثات فورية)

  • تحتاج إلى بحث متجهي (RediSearch)

أمثلة:

  • ذاكرة التخزين المؤقت للاستعلام

  • الإكمال التلقائي

  • الاستعلامات الشائعة

  • بيانات الجلسة

  • تحديد معدل الطلبات (Rate limiting)

نهج هجين

نحن نجمع بين الثلاثة للحصول على أفضل أداء:

خط العمل (المعالجة خارج الخط)

NumPy: حساب التضمينات، مصفوفات التشابه

JSON: تخزين التعيينات، البيانات الوصفية، التكوين

Valkey: غير مستخدم (خط العمل يعمل خارج الخط)

خدمة البحث (استعلامات حية)

NumPy: تحميل التضمينات من القرص (معينة للذاكرة)

JSON: تحميل التعيينات من القرص (مخزنة مؤقتًا في الذاكرة)

Valkey: تخزين مؤقت للاستعلامات الحية، الإكمال التلقائي، الاستعلامات الشائعة

تدفق البيانات

graph LR
    Pipeline[خط عمل SEO
خارج الخط] subgraph Storage NP[ملفات NumPy
embeddings.npy] JS[ملفات JSON
mappings.json] end Search[خدمة البحث
مباشر] VK[Valkey
ذاكرة تخزين مؤقت] Pipeline --> NP Pipeline --> JS NP --> Search JS --> Search Search --> VK VK --> Search

مقارنة الأداء

NumPy (معينة للذاكرة):

  • وقت التحميل: فوري (تعيين بدون نسخ إلى الذاكرة الظاهرية)

  • وقت البحث: قريب من الصفر (مؤشر وحدة المعالجة المركزية مباشرة إلى فهرس المصفوفة)

  • الذاكرة: ضئيلة (ذاكرة التخزين المؤقت للصفحات المدارة بواسطة نظام التشغيل، غير مقيمة في ذاكرة الوصول العشوائي للعملية)

JSON:

  • وقت التحميل: زمن انتقال مرتفع (تحليل تسلسلي وإنشاء كائنات)

  • وقت البحث: فعال (نفقات عامة قياسية لخريطة التجزئة)

  • الذاكرة: كبيرة (هيكل التسلسل بأكمله مقيم في الكومة)

Valkey:

  • وقت التحميل: ثابت (مقيم في خدمة الخلفية)

  • وقت البحث: متوسط (يشمل رحلة ذهاب وإياب عبر الشبكة وتسلسل البروتوكول)

  • الذاكرة: خارجية (معزولة داخل عملية قاعدة البيانات)

الفائز: NumPy للمعالجة الدُفعية عالية الإنتاجية، Valkey للوصول الموزع للمفتاح الواحد

بحث التشابه المتجهي

NumPy (cosine_similarity):

  • عمليات الدُفعات: سريعة (مُحسَّنة عبر الجبر الخطي المتجهي/SIMD)

  • قابلة للتوزيع: عالية (تتوسع عبر جميع نوى وحدة المعالجة المركزية المتاحة)

  • الذاكرة: خطية (تتوسع مباشرة مع أبعاد التضمين)

Valkey (RediSearch):

  • سرعة البحث: متفوقة (تستخدم فهرسة HNSW/Vector المتخصصة)

  • قابلة للتوزيع: محدودة (مقيدة بنموذج خيوط المحرك)

  • الذاكرة: مكثفة (تتطلب تضمينات أولية بالإضافة إلى بيانات وصفية للفهرسة)

الفائز: Valkey للبحث المباشر دون الإدراك الحسي، NumPy للمعالجة التحليلية الثقيلة خارج الخط

بحث التكوين

JSON:

  • وقت التحميل: متغير (يتناسب مع تعقيد التكوين)

  • وقت البحث: سريع (وصول قياسي للقاموس)

  • وقت التحرير: سلس (تعديل نصي قابل للقراءة البشرية)

Valkey:

  • وقت التحميل: فوري (نشط عند الاتصال)

  • وقت البحث: مقيد بالشبكة (يعتمد على النفقات العامة للطلب)

  • وقت التحرير: برمجي (يتطلب أوامر CLI أو SET من العميل)

الفائز: JSON للتكوينات الثابتة، Valkey للحالة المشتركة الديناميكية

المراجع

التقنيات

  • NumPy - مكتبة حساب المصفوفات

  • JSON - تنسيق تبادل البيانات

  • Valkey - مخزن بيانات في الذاكرة (فرع من Redis)

  • RediSearch - وحدة بحث متجهي

المفاهيم التقنية

مقالات ذات صلة

الملخص

نستخدم ثلاث تقنيات تخزين مُحسَّنة لحالات استخدام مختلفة:

NumPy (التضمينات):

  • عمليات متجهية سريعة (تشابه جيب التمام)

  • معينة للذاكرة

  • معالجة دُفعية

  • تنسيق قياسي لتعلم الآلة

JSON (التكوين):

  • قابلة للقراءة البشرية (سهلة التشخيص)

  • التحكم بالإصدارات (Git diffs)

  • تحرير يدوي (لا حاجة للكود)

  • تنسيق عالمي

Valkey (ذاكرة التخزين المؤقت المباشرة):

  • عمليات بحث سريعة (ميكروثانية)

  • بحث متجهي (RediSearch)

  • انتهاء صلاحية TTL (إبطال تلقائي)

  • نشر/اشتراك (تحديثات فورية)

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


← العودة إلى فهرس الوثائق