أسس التوسّع

البيانات على نطاق واسع: تكرير وتقسيم وتوزيع

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

التكرير (replication): نسخ تقرأ وتنجو

في تكرير القائد والأتباع (leader–follower)، يستقبل القائد كل الكتابات ويبثها إلى الأتباع الذين يخدمون القراءات. القراءة تتوسع أفقياً — أضف تابعاً، تحصل على قدرة قراءة أكبر. والمقايضة بين sync وasync: التكرير المتزامن لا يفقد أي كتابة لكنه يجعل كل كتابة تنتظر؛ واللامتزامن سريع لكن سقوط القائد قد يأخذ أحدث الكتابات معه. وعندما يموت القائد، يصعّد الـfailover أحد الأتباع — وهذه بالضبط اللحظة التي يصبح فيها تأخر التكرير مرئياً للمستخدمين.

عندما يتأخر الأتباع

مع التكرير اللامتزامن، تحط الكتابة على القائد وتصل الأتباع بعد بضعة ميلي ثانية — أو بضع ثوانٍ. فيحدّث المستخدم ملفه الشخصي، يحدّث الصفحة، يُوجَّه إلى تابع متأخر، فيرى القيمة القديمة: كتابته اختفت. هذه هي مشكلة read-your-writes. والقريب الأخفى هو monotonic reads: تحديثان متتاليان يضربان تابعين مختلفين بتأخر مختلف، فتبدو البيانات وكأنها ترجع بالزمن إلى الوراء. والمعالجات المعيارية رخيصة ومملة: وجّه قراءات المستخدم نفسه إلى القائد لفترة قصيرة بعد كل كتابة، أو اجعل العميل يتذكر الـtimestamp أو الـLSN لآخر كتابة له ولا يقرأ إلا من تابع لحق بها، أو ثبّت المستخدم على تابع واحد فلا ترجع قراءاته إلى الوراء أبداً. لا شيء من هذا يجعل التكرير متزامناً — هو فقط يخفي التأخر عمن كان سيلاحظه.

التقسيم (sharding): اقطع البيانات بمفتاح واحد

عندما تعجز قاعدة واحدة عن استيعاب البيانات أو تحمّل الكتابات، قسّمها: الصفوف ذات مفتاح التقسيم 1–1M تذهب إلى الخادم A، و1M–2M إلى الخادم B. اختيار مفتاح التقسيم هو اللعبة كلها — المفتاح السيئ يكثّف الحمل بدل توزيعه. ومشكلة المشاهير هي الفشل الكلاسيكي: بيانات مستخدم واحد ساخنة لدرجة أن shard الخاص به ينصهر بينما الباقي عاطل. وإعادة التقسيم لاحقاً — نقل الصفوف بين الخوادم — من أكثر العمليات ألماً في الهندسة، فاختر المفتاح وكأنك ستعيش معه سنوات. ستعيش معه فعلاً.

التقسيم بالنطاقات مقابل التقسيم بالـhash

التقسيم بالنطاقات يعيّن نطاقات متجاورة من المفاتيح لكل shard — المستخدمون من A إلى F هنا، ومن G إلى M هناك. والمكسب هو المسوح المرتّبة: استعلامات النطاق والتنقل المرتّب تبقى داخل shard واحد وتبقى سريعة. والثمن هو النقاط الساخنة: قسّم بمعرّف تزايدي أو بـtimestamp، وستحط كل كتابة جديدة على الـshard الأخير بينما الباقي عاطل. أما التقسيم بالـhash فيمرّر المفتاح عبر دالة hash، فتتوزع الكتابات بالتساوي شبه التام — لا نقطة ساخنة أبداً. والثمن: الترتيب يتدمر، و«أعطني المستخدمين من A إلى M» تصبح scatter-gather على كل shard. اختر النطاقات عندما تهيمن المسوح وتستطيع تحمّل إعادة التوازن؛ واختر الـhash عندما يهم التوزيع المتساوي أكثر من الترتيب.

SQL مقابل NoSQL، بصدق

استخدم SQL افتراضياً: المعاملات (transactions) والربط (joins) والقيود وأربعون عاماً من الاعتمادية المكتسبة بصعوبة تغطي معظم التطبيقات بارتياح. والجأ إلى NoSQL عندما يفرض الحجم أو نمط الوصول ذلك فعلاً — إنتاجية كتابة هائلة، أو مخططات مرنة، أو استعلامات key-value بسيطة بحجم ضخم، أو بيانات هي بطبيعتها مستند أو مخطط بياني. الخلاصة الصادقة: SQL هو الافتراضي، وNoSQL استثناء تبرره بالأرقام، لا بالموضة.

الـhashing المتسق: الحيلة المعيارية

كيف تقرر أي خادم يحتفظ بأي مفتاح، دون إعادة توزيع كل شيء عند انضمام خادم أو مغادرته؟ طبّق دالة hash على الخوادم والمفاتيح معاً على حلقة واحدة؛ وكل مفتاح يتبع أول خادم في اتجاه عقارب الساعة. أضف أو احذف خادماً واحداً ولن تنتقل سوى ‏~1/N‏ من المفاتيح — والباقي يبقى في مكانه. هذه الفكرة الواحدة تشغّل بهدوء الـcaches وقواعد البيانات المقسّمة وموزّعات الحمل في كل مكان.

النسخ (Replication)كل نسخة تحمل كل البيانات — القراءة تتوسعالكتابة ↓القائديستقبل الكتابةنسخنسختابع 1يقرأ منهتابع 2يقرأ منه↑ القراءة من التابعينالتقسيم (Sharding)كل جزء يحمل شريحة مختلفة — التخزين يتوسعالتطبيقيوجّه حسب المفتاحa–mn–st–zShard Aمفاتيح a–mShard Bمفاتيح n–sShard Cمفاتيح t–zالمفتاح يحدد الجهة
التكرير ينسخ البيانات نفسها على عدة خوادم؛ والتقسيم يوزّع بيانات مختلفة على عدة خوادم.
جرّب بنفسك
إعادة التقسيم دون توقف

إعادة التقسيم الساذجة — hash للمفتاح modulo N ثم تغيير N — تعيد توزيع كل صف تقريباً وتسقط النظام معها. والحل المعياري هو عدد ثابت من الـshards المنطقية، لنقل 1,024، تُربط بعدد أقل بكثير من العقد الفعلية: فتصبح إعادة التوازن نقل shards منطقية كاملة بين العقد — تغيير في جدول توجيه، لا جراحة في نموذج البيانات. وأثناء الترحيل نفسه تكتب مرتين (double-write) — كل كتابة تذهب إلى الموقع القديم والجديد معاً — بينما مهمة خلفية تملأ الفارغ، ولا تتحول القراءات إلا بعد التحقق من النسخة. والقاعدة التي توفر عليك سنوات لاحقاً: اختر shards منطقية أكثر بكثير من عدد العقد منذ اليوم الأول، فيصبح توسيع الأسطول دائماً «سلّم بعض الـshards للآلة الجديدة» وليس أبداً «أعد التفكير في مخطط التقسيم تحت الحمل».

المبدأ

التكرير يشتري لك قراءات ونجاة؛ والتقسيم يشتري لك كتابات وحجماً. كل منهما يحل مشكلة مختلفة — ومعظم الأنظمة الحقيقية على النطاق الواسع ينتهي بها الحال محتاجةً إلى كليهما: تكرير داخل كل shard.

تحقّق سريع

تطبيقك يجري 95% قراءات وقاعدة بياناته الوحيدة تحتضر — تكرير أم تقسيم؟ وأيهما ينفع عندما تتوقف البيانات ببساطة عن السعة على قرص واحد؟ (التكرير لفيضان القراءات — الأتباع يمتصونها؛ والتقسيم لمشكلة الحجم — فلا نسخة مكررة تجعل البيانات أصغر.)

الخلاصة

انسخ البيانات لتنجو وتقرأ أكثر؛ وقسّمها لتكتب أكثر وتستوعب أكثر. اجعل SQL هو الافتراضي، وبرّر NoSQL بالأرقام، واستخدم الـhashing المتسق كلما احتجت توزيع مفاتيح على خوادم تأتي وتذهب.

📌 افعل هذا الاثنين

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

أسس التوسّع