أبسط داتابيز
ملف: set بيعمل append لسطر في الآخر، و get بيدور على آخر سطر للمفتاح. الكتابة سريعة جدًا. القراءة كارثة لأنك بتلف على الملف كله.
الـ index بنية إضافية مشتقة بتوصّلك بسرعة. كل index بيسرّع القراءة وبيبطّأ الكتابة، لأن لازم يتحدث مع كل write. أنت اللي بتختارهم حسب الكويريز.
Hash index و LSM
Hash map في الرام: كل key → مكانه في الملف. ممتاز لو المفاتيح تدخل الرام والقيم بتتحدث كتير (عداد مشاهدات). الملف بيكبر، فنقسّمه segments، ونعمل compaction (نسيب آخر قيمة) و merge.
الكتابة append-only أسرع من التعديل في المكان، والـ crash recovery أبسط. بس كل المفاتيح لازم تدخل الرام، والـ range queries مستحيلة عمليًا.
لو رتبت المفاتيح جوه كل segment يبقى SSTable:
- الـ merge سهل زي merge sort، ولو المفتاح اتكرر ناخد الأحدث.
- Sparse index: مش محتاج كل المفاتيح في الرام، لأن الترتيب بيقولك المفتاح بين مين ومين.
- تقدر تضغط كل block.
الكتابات العشوائية بتتحط الأول في memtable (شجرة في الرام). لما تكبر تتكتب SSTable. ولو السيرفر وقع، في log منفصل عشان الـ recovery. ده LSM-tree: LevelDB، RocksDB، Cassandra، HBase، وفكرة قريبة في Lucene.
B-tree
أشهر index من السبعينات. الديسك صفحات ثابتة (غالبًا ٤KB)، والتعديل في مكان الصفحة. فيه root، وكل صفحة فيها مفاتيح و pointers لأولادها. الشجرة دايمًا balanced: ٤ مستويات بـ branching عالي تشيل تيرابايتات.
لو الصفحة اتملت تتقسم نصين. الكتابة فوق كذا صفحة خطر لو السيرفر وقع في النص، فأي تعديل يتكتب الأول في WAL (redo log) قبل ما يتنفذ على الشجرة.
| LSM-tree | B-tree | |
|---|---|---|
| الكتابة | أسرع غالبًا: sequential و write amplification أقل | كل write = WAL + الصفحة، وأحيانًا الصفحة كلها عشان بايتات |
| القراءة | أبطأ شوية (memtable + كذا segment) | أسرع ومتوقعة |
| المساحة | ضغط أحسن | صفحات فيها فراغ |
| العيب | الـ compaction ممكن يعمل tail latency، ولو الكتابة أسرع من الدمج الديسك يتملى | write amplification أعلى، ومساحة فاضية |
مفيش قاعدة ثابتة. جرّب على الـ workload. الـ B-trees راسخة ومتوقعة ومريحة للـ transactions. الـ LSM بيزيد في الأنظمة الـ write-heavy.
Indexes تانية
| النوع | الفكرة |
|---|---|
| Secondary | على عمود مش primary. كل قيمة تشاور على ليستة صفوف. مهمة للـ joins |
| Clustered | الصف نفسه جوه الـ index (زي InnoDB). الـ secondary بيشاور على الـ primary key |
| Covering | الـ index فيه الأعمدة اللي الكويري محتاجاها، فمتروحش للصف |
| Multi-column | زي دليل التليفون: اسم العيلة ثم الاسم. مفيد بالترتيب ده بس |
| Multi-dimensional | lat/long أو لون. B-tree عادي مش يكفي. R-trees |
| Full-text | مرادفات وأخطاء إملائية. Lucene و Levenshtein |
OLTP و OLAP
| OLTP | OLAP | |
|---|---|---|
| القراءة | صفوف قليلة بالمفتاح | تجميع على ملايين الصفوف |
| الكتابة | random ومن المستخدم | bulk import أو event stream |
| مين | العميل من الأبلكيشن | المحلل |
| الداتا | أحدث حالة | تاريخ الأحداث |
| العائق | disk seek | disk bandwidth |
الـ data warehouse داتابيز منفصلة للتحليلات، read-only، فيها نسخة من أنظمة الـ OLTP. ليه منفصلة؟ عشان الكويري التقيلة متضربش العملاء. الطريق ETL: Extract ثم Transform ثم Load.
الكويري بتحتاج ٤ أعمدة من ١٠٠. التخزين صف-صف هيقرا الصف كله. Column storage: كل عمود في ملف، والصف رقم k هو العنصر k في كل ملف. الأعمدة فيها تكرار فبتتضغط (bitmap و run-length)، والـ CPU يعالجها vectorized بـ SIMD.
الكتابة في الـ column store صعبة لأن فيه ضغط وترتيب، فبيرجعوا LSM: الكتابة في الميموري وبعدين تتدمج. ولو نفس الـ aggregation بيتكرر، خزّن النتيجة (materialized view). الـ OLAP cube أسرع وأقل مرونة.