قواعد البيانات والتخزين

تخزين الكائنات والملفات الكبيرة

18 دقيقة الدرس 8 من 10

تخزين الكائنات والملفات الكبيرة

كل تطبيق جاد يُنتج في نهاية المطاف بيانات لم تُصمَّم قواعد البيانات العلائقية أصلًا لتخزينها: صور الملفات الشخصية، صور المنتجات، تسجيلات الفيديو، فواتير PDF، أوزان نماذج التعلم الآلي، نسخ احتياطية لقواعد البيانات. هذه الأصول كبيرة الحجم وغير قابلة للتغيير في الغالب بمجرد كتابتها، وتُستهلك في القراءة أكثر بكثير مما تُحدَّث. تخزينها في قواعد البيانات العلائقية كأعمدة BLOB ينجح على نطاق صغير لكنه ينهار تحت الحمل الحقيقي. تخزين الكائنات (Object Storage) هو الحل المعياري في الصناعة: مخزن مسطح قابل للتوسع بشكل لا محدود ومُحسَّن تحديدًا للكائنات الثنائية الكبيرة.

ما هو تخزين الكائنات؟

خلافًا لنظام الملفات الذي يُنظّم البيانات في هيكل هرمي من المجلدات، أو الجهاز الكتلي الذي يُتيح قطاعات خام، يُعامل تخزين الكائنات كل قطعة بيانات كـكائن مكتفٍ بذاته يتكون من ثلاثة أجزاء:

  • المفتاح (Key) — معرّف نصي فريد عالميًا، مثل users/42/avatar.jpg. لا يوجد هيكل هرمي حقيقي للمجلدات؛ الشرطة المائلة مجرد جزء من نص المفتاح، وإن كانت واجهات المستخدم تُحاكي المجلدات منه.
  • القيمة (Value) — البايتات الخام للملف، تتراوح من بضعة بايتات إلى تيرابايتات.
  • البيانات الوصفية (Metadata) — خريطة مفتاح-قيمة مسطحة مرفقة بالكائن: Content-Type، Cache-Control، وسوم مخصصة مثل user-id: 42، وقت الإنشاء، وETag وهو هاش المحتوى يُستخدم للتحقق من الكاش والطلبات الشرطية.

العرض الرائد في السحابة العامة هو Amazon S3 (Simple Storage Service)، الذي أُطلق عام 2006. لكل سحابة كبرى ما يعادله: Google Cloud Storage، Azure Blob Storage، وأنظمة مفتوحة المصدر أو داخلية متوافقة مع S3 مثل MinIO وCeph وCloudflare R2. أصبح واجهة HTTP الخاصة بـS3 معيارًا فعليًا في الصناعة؛ إذا فهمت S3 فهمت الجميع.

البنية الداخلية: كيف يعمل تخزين الكائنات

خدمات تخزين الكائنات هي بحد ذاتها أنظمة موزعة. فهم آلياتها الداخلية يساعدك على توقع ضمانات الاتساق وأنماط الفشل.

Object storage internal architecture Client PUT / GET API Gateway Auth + routing Rate limiting Metadata Store Key to location map (distributed KV) Storage Node A chunk replica 1 Storage Node B chunk replica 2 Storage Node C chunk replica 3 lookup write 3x CDN Edge PoP cache serves reads origin pull fast read (cached)
تدفق الطلبات في تخزين الكائنات: تُوزَّع الكتابة على عدة عُقد لضمان المتانة؛ وتُقدَّم القراءات من حافة CDN عند الكاش، وتعود إلى عُقد التخزين عند غيابه.

عند تنفيذ PUT لكائن، تُوثّق بوابة API الطلب، وتكتب ربط المفتاح بالموقع في مخزن بيانات وصفية موزع، وتُنسخ البايتات الخام عبر عدة عُقد تخزين فعلية، عادةً ثلاث نسخ داخل منطقة واحدة. في التطبيقات الكبيرة النطاق، يُستخدم غالبًا ترميز المحو (Erasure Coding) بدلًا من النسخ الكامل: يُقسَّم الكائن إلى أجزاء بيانات وأجزاء تكرار حتى يمكن إعادة بناء الكائن الكامل من أي مجموعة فرعية، مما يُحقق متانة عالية بأقل عبء تخزين مقارنةً بالنسخ الثلاثية.

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

نموذج الاتساق

قدّم Amazon S3 اتساقًا نهائيًا من عام 2006 حتى ديسمبر 2020. إذا رفعت كائنًا جديدًا وقرأته فورًا من عقدة مختلفة، كنت قد تحصل على 404. منذ ديسمبر 2020، يضمن S3 اتساقًا قويًا للقراءة بعد الكتابة للكائنات الجديدة واتساقًا قويًا للقوائم. GCS كان دائمًا متسقًا بالكامل. MinIO وCeph أيضًا متسقان داخل كتلة واحدة.

كود قديم مبني للاتساق النهائي. حجم كبير من كود الأنظمة الموزعة المكتوب قبل عام 2020 يُعيد المحاولة عند القراءة بشكل دفاعي، أو يُخزّن ETags، أو يستخدم حيلًا بروابط موقّعة للتعامل مع الاتساق النهائي في S3. معظم هذا التعقيد لم يعد ضروريًا للأعمال الجديدة المبنية على S3، لكنك ستصادفه في قواعد الكود القديمة.

القدرات الرئيسية

الروابط الموقّعة مسبقًا (Presigned URLs)

لا تريد أبدًا أن يجلس خادم تطبيقك في مسار البيانات لرفع أو تنزيل الملفات الكبيرة، فذلك يُهدر النطاق الترددي والمعالج والذاكرة. بدلاً من ذلك، يُنشئ الخادم رابطًا موقّعًا مسبقًا: رابط محدود الوقت وموقّع تشفيريًا يمنح حامله صلاحية تنفيذ عملية واحدة بالضبط، GET أو PUT، مباشرةً ضد خدمة تخزين الكائنات. يرفع العميل الملف مباشرةً إلى S3؛ ويستقبل خادمك إشعارًا فحسب بعد اكتمال الرفع.

Presigned URL upload flow Client Browser / App App Server signs URL (AWS SDK) Object Storage S3 / GCS / R2 1. Request upload URL 2. Presigned PUT URL (TTL=5 min) 3. Direct PUT to storage (app server bypassed) 4. S3 event notifies app server
نمط الروابط الموقّعة مسبقًا: يُصدر خادم التطبيق رابطًا قصير الأجل؛ يرفع العميل الملف مباشرةً إلى التخزين دون المرور بخادم التطبيق.

فئات التخزين وسياسات دورة الحياة

تُقدّم S3 عدة طبقات تخزين بنقاط سعر-أداء مختلفة:

  • S3 Standard — استرجاع بالميلي ثانية، أعلى تكلفة (~0.023 دولار/جيجابايت/شهر). للبيانات كثيرة الوصول.
  • S3 Standard-IA — نفس التأخير، تكلفة تخزين أقل، تكلفة أعلى لكل طلب. للأصول التي تُصل إليها شهريًا لا يوميًا.
  • S3 Glacier Instant Retrieval — استرجاع بالميلي ثانية، أرخص بـ68% من Standard. للوصول الفصلي.
  • S3 Glacier Deep Archive — استرجاع في ساعات، ~0.00099 دولار/جيجابايت/شهر. لأرشيف الامتثال طويل المدى كالسجلات الطبية والمالية.

تُؤتمت سياسات دورة الحياة الانتقال بين الطبقات. نمط شائع: الاحتفاظ بآخر 30 يومًا في Standard، الانتقال إلى Standard-IA بعد 30 يومًا، إلى Glacier بعد 90 يومًا، والحذف بعد 7 سنوات. يمكن أن يُخفّض هذا تكاليف التخزين بأكثر من 80% دون أي تغيير في كود التطبيق.

الإصدارات والمتانة

تُعلن S3 عن 11 تسعة من المتانة (99.999999999% سنويًا)، أي أنك تخسر في المتوسط كائنًا واحدًا من كل 100 مليار كائن مخزّن سنويًا — يتحقق هذا من خلال النسخ عبر مناطق توافر متعددة وترميز المحو. فعّل الإصدارات (Versioning) على أي سلة تحمل محتوى من إنشاء المستخدمين أو نسخًا احتياطية: تحتفظ بكل نسخة من كل كائن حتى يمكن استعادة الحذف العرضي والكتابة الفاسدة باستدعاء API واحد.

تخزين الكائنات ليس قاعدة بيانات. لا يمكنك الاستعلام عن الكائنات بمحتواها، ولا تحديثها جزئيًا، ولا تنفيذ معاملات ذرية متعددة الكائنات. إذا أردت إيجاد كل الصور المرتبطة بـuser_id=42، خزّن البيانات الوصفية (اسم المفتاح، معرّف المستخدم، الحجم، وقت الإنشاء) في قاعدة بيانات علائقية واستخدم تخزين الكائنات للبايتات فحسب. ربط المفتاح بالبيانات الوصفية في قاعدة البيانات رخيص؛ البيانات الثنائية في S3 رخيصة. تقسيمها هو التصميم الصحيح.

تقديم الكائنات للمستخدمين النهائيين

للكائنات الخاصة كالسجلات الطبية والعقود الموقّعة ومقاطع الفيديو الخاصة، أنشئ روابط GET موقّعة مسبقًا بوقت انتهاء صلاحية قصير، من 15 إلى 60 دقيقة، حتى يتمكن المستخدمون المصادق عليهم فحسب من الوصول إليها وتنتهي صلاحية الرابط سريعًا إذا سُرّب.

للكائنات العامة أو شبه العامة كصور الملفات الشخصية وصور المنتجات ومقاطع الفيديو العامة، ضع CDN أمامها. عيّن سلة التخزين كمصدر أصل لـCDN. يُخزّن CDN الكائن في عُقد حافة عالمية. مستخدم في طوكيو يجلب صورة مخزّنة في us-east-1 سيحصل عليها من أقرب عقدة حافة CDN في ميلي ثانية بدلاً من مئات الميلي ثانية من الأصل. اضبط Cache-Control: public, max-age=31536000, immutable على الأصول ذات الإصدارات (الأصول التي يتضمن مفتاحها هاش المحتوى) حتى يُخزّنها CDN والمتصفحات إلى أجل غير مسمى.

لا تكشف سلتك للعموم بدون CDN أو روابط موقّعة. سلة قابلة للقراءة العامة بدون CDN تعني أن كل طلب يضرب مصدرك الأصلي، وتدفع رسوم نطاق ترددي كاملة لعمليات الخروج (نطاق S3 الترددي ~0.09 دولار/جيجابايت)، ولا تكسب أي ميزة أداء جغرافية. والأسوأ، السلات العامة التي تحوي بيانات حساسة سبب متكرر لانتهاكات البيانات. اعتمد سياسة "السلة خاصة افتراضيًا" وقدّم كل شيء عبر روابط موقّعة أو CDN.

أرقام حقيقية من تطبيقات عملاقة

تُخزّن Netflix مكتبة الفيديو الكاملة لديها — مئات البيتابايت — في S3. انتقلت Dropbox من S3 إلى نظام تخزين كائنات خاص بها (Magic Pocket) عند مئات البيتابايت. خزّنت Instagram في ذروتها مليارات الصور في S3. بنت Facebook نظام تخزين مخصص للصور يُسمى Haystack تحديدًا لأن عبء الطلبات في نظام الملفات العام كان مرتفعًا جدًا عند مليارات قراءات الصور يوميًا. كل هذه الأنظمة تتقاطع في نفس الأنماط المعمارية التي تعلمتها للتو.

ملخص

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