NoSQL: المستندات والمفتاح-القيمة
درس 5 من 5
5NoSQL: المستندات والمفتاح-القيمة
الصورة الكبيرةSQL يقسّم البيانات لجداول ويجمعها بـ JOIN وقت القراءة. قاعدة المستندات تخزّن الشيء كاملًا في مستند واحد يشبه JSON، فتقرأه دفعة واحدة. لا أحد أفضل مطلقًا، لكل واحد شكل بيانات يناسبه.
ماذا تشاهد في الرسم: عائلات NoSQL الأربع. كل واحدة صُمّمت لنمط وصول معيّن، والمستندات أكثرها انتشارًا في تطبيقات الويب.
خارطة هذا الجزء
المفاهيم بالترتيب، اضغط أيًّا منها للقفز إليه:
طريقان إلى نفس الفكرة
قاعدة SQL مثل أرشيف فيه دفتر للعملاء ودفتر للطلبات ودفتر للمنتجات، وتربط بينها بالأرقام. قاعدة المستندات مثل ملف لكل طلب فيه كل شيء: اسم العميل والأصناف والعنوان. فتح ملف واحد أسرع، لكن لو غيّر العميل اسمه عليك تعديل كل ملفاته.
NoSQL تتنازل عادةً عن شيء من ACID أو JOIN مقابل مخطّط مرن وتوسّع أفقي (sharding). MongoDB يخزّن BSON، ويدعم فهارس ثانوية وتجميعًا (aggregation pipeline) ومعاملات متعددة المستندات منذ الإصدار 4. القرار المحوري: التضمين (embed) لما يُقرأ معًا ويكون محدود الحجم، والإشارة (reference) لما يكبر بلا حد أو يُشارك. Redis مفتاح-قيمة في الذاكرة بهياكل (hash، list، sorted set) وانتهاء صلاحية TTL، فيُستخدم للتخزين المؤقت والجلسات والطوابير. ونظرية CAP تذكّرك أن الانقسام الشبكي يفرض اختيارًا بين الاتساق والإتاحة.
قبل أن تبدأ
🗄️SQL
دروس SQL والعلاقات في هذا المسار.
🧾JSON
تعرف القواميس والقوائم المتداخلة.
الفكرة الجوهرية
1العائلات الأربع
مفتاح-قيمة (Redis): أسرع شيء حين تعرف المفتاح. مستندات (MongoDB): كائنات JSON مرنة. أعمدة عريضة (Cassandra): كتابات ضخمة موزّعة مثل السجلات. رسوم بيانية (Neo4j): علاقات متشعّبة مثل «أصدقاء أصدقائي».
2صمّم المستند حسب القراءة
اسأل: ما الذي تعرضه الصفحة دفعة واحدة؟ صفحة الطلب تعرض الأصناف والعنوان معًا، فنضمّنها في مستند الطلب. أما العميل فيُشارك بين طلبات كثيرة، فنشير إليه بمعرّفه. هذه محاكاة بقواميس بايثون:
customers = {"c1": {"_id": "c1", "name": "Sara", "city": "Riyadh"}}
orders = [
{"_id": "o1", "customer_id": "c1", "status": "paid",
"items": [{"name": "Latte", "qty": 2, "price": 14},
{"name": "Cookie", "qty": 1, "price": 6}]},
{"_id": "o2", "customer_id": "c1", "status": "new",
"items": [{"name": "Mocha", "qty": 1, "price": 16}]},
]
def find(collection, **query):
return [doc for doc in collection if all(doc.get(k) == v for k, v in query.items())]
for order in find(orders, customer_id="c1"):
total = sum(i["qty"] * i["price"] for i in order["items"])
print(order["_id"], order["status"], total, customers[order["customer_id"]]["name"])3MongoDB بالأمثلة
بـ PyMongo: insert_one وfind بفلتر وupdate_one بمعاملات مثل $set و$inc، وفهرس للحقول التي تبحث بها كثيرًا.
from pymongo import MongoClient
db = MongoClient("mongodb://localhost:27017")["cafe"]
db.orders.insert_one({"customer_id": "c1", "status": "new",
"items": [{"name": "Latte", "qty": 2, "price": 14}]})
for order in db.orders.find({"status": "new"}, {"items": 1}):
print(order)
db.orders.update_one({"customer_id": "c1", "status": "new"},
{"$set": {"status": "paid"}})
db.orders.create_index([("customer_id", 1), ("status", 1)])4Redis للتخزين المؤقت
نمط cache-aside: اقرأ من Redis، وإن لم تجد اقرأ من قاعدة البيانات واحفظ النتيجة بمدة صلاحية. هنا محاكاة بقاموس وزمن انتهاء:
import time
cache = {}
db_reads = 0
def slow_menu():
global db_reads
db_reads += 1
return ["Espresso", "Latte"]
def get_menu(ttl=60):
hit = cache.get("menu")
if hit and hit[1] > time.time():
return hit[0]
value = slow_menu()
cache["menu"] = (value, time.time() + ttl)
return value
for _ in range(5):
get_menu()
print("database reads:", db_reads)r.set("menu", data, ex=60) وr.get("menu"). وتذكّر أن تمسح المفتاح حين تتغيّر القائمة.مثال محلول
المسألة: تطبيق يعرض ملف المستخدم مع آخر 5 طلبات وتفضيلات متغيّرة الحقول، وصفحة الملف بطيئة بسبب 4 JOIN. التحليل: القراءة دائمًا معًا، والتفضيلات بلا شكل ثابت. الحل: مستند مستخدم يضمّن التفضيلات، ومجموعة طلبات منفصلة بفهرس (user_id, created_at). قراءتان سريعتان بدل أربع ربطات، والطلبات لا تنفخ مستند المستخدم.
خرافة مقابل حقيقة
| الخرافة | الحقيقة |
|---|---|
| NoSQL أسرع من SQL دائمًا. | أسرع لنمط القراءة الذي صُمّم له، وقد يكون أبطأ بكثير لاستعلام لم تتوقعه. |
| NoSQL يعني بلا مخطّط. | المخطّط موجود في كودك بدل القاعدة. وبلا انضباط تصل بيانات بأشكال متضاربة. |
أين تقابله في العمل
🛒كتالوج المنتجات
منتجات بخصائص مختلفة (مقاس، لون، سعة) في مستندات مرنة.
⚡التخزين المؤقت والجلسات
Redis أمام قاعدة البيانات يخفف الحمل.
📊السجلات والأحداث
كتابات ضخمة متواصلة في قواعد الأعمدة العريضة.
اختبر فهمك
متى تضمّن البيانات في المستند؟
ما يُقرأ معًا ولا يكبر بلا حد يُضمَّن، وما يُشارك أو يكبر يُشار إليه.
أي قاعدة تناسب التخزين المؤقت بمدة صلاحية؟
Redis في الذاكرة ويدعم TTL لكل مفتاح.
نظام بنكي بتحويلات بين حسابات كثيرة. الخيار الأول؟
العلاقات والمعاملات الصارمة هي نقطة قوة SQL.
بطاقات مراجعة سريعة
تحقق من فهمك
لماذا لا تضمّن كل التعليقات في المنشور؟
لأنها تكبر بلا حد فتقترب من حد حجم المستند ويثقل كل تحديث.
ماذا تفعل حين تتغيّر بيانات مخزّنة مؤقتًا؟
تمسح المفتاح أو تحدّثه فورًا، وتعتمد على TTL كشبكة أمان.
متى تبقى مع SQL؟
حين البيانات علائقية بكثرة، والاستعلامات متنوعة وغير متوقعة، والمعاملات مهمة.
مصادر هذا الدرس
مرتّبة بالنوع. ابدأ بالتوثيق الرسمي إذا تبي المرجع الدقيق، وبالمقال أو الفيديو إذا تبي الشرح أولًا.
خلّصت الدرس؟
سجّل الدخول ليُحفظ تقدّمك.