هذا هو الجزء 3 من دليل عن ترحيل البيانات من PostgreSQL إلى ClickHouse. ومن خلال مثال عملي، يوضّح كيفية نمذجة البيانات في ClickHouse عند الترحيل من PostgreSQL.نوصي المستخدمين الذين يرحّلون من Postgres بقراءة دليل نمذجة البيانات في ClickHouse. يستخدم هذا الدليل نفس Stack Overflow dataset، ويستعرض عدة أساليب بالاعتماد على ميزات ClickHouse.
غالبًا ما يبحث المستخدمون القادمون من قواعد بيانات OLTP عن المفهوم المقابل في ClickHouse. وعندما يلاحظون أن ClickHouse يدعم صيغة
PRIMARY KEY، فقد يميلون إلى تعريف مخطط الجدول لديهم باستخدام المفاتيح نفسها المستخدَمة في قاعدة بيانات OLTP المصدر. وهذا غير مناسب.
كيف تختلف المفاتيح الأساسية في ClickHouse؟
- تكون المفاتيح الأساسية في Postgres، بحكم التعريف، فريدة لكل صف. ويتيح استخدام هياكل B-tree إجراء lookup فعّال للصفوف الفردية باستخدام هذا المفتاح. وبينما يمكن تهيئة ClickHouse لتحسين lookup لقيمة صف واحدة، فإن أعباء عمل analytics تتطلب عادةً قراءة عدد قليل من الأعمدة عبر عدد كبير من الصفوف. كما أن عامل التصفية سيحتاج في كثير من الأحيان إلى تحديد مجموعة فرعية من الصفوف لإجراء aggregation عليها.
- تُعد كفاءة الذاكرة والتخزين على disk أمرًا بالغ الأهمية عند النطاق الذي يُستخدم فيه ClickHouse غالبًا. تُكتب البيانات في جداول ClickHouse على شكل chunks تُعرف باسم أجزاء، مع تطبيق قواعد لدمج هذه الأجزاء في الخلفية. في ClickHouse، لكل جزء فهرس أساسي خاص بها. وعند دمج الأجزاء، تُدمج أيضًا primary indexes الخاصة بالـ الجزء المدمج. وعلى خلاف Postgres، لا تُبنى هذه الفهارس لكل صف. وبدلًا من ذلك، يحتوي الفهرس الأساسي لكل جزء على index entry واحدة لكل مجموعة من الصفوف — وتُعرف هذه التقنية باسم الفهرسة المتفرقة.
- تصبح الفهرسة المتفرقة ممكنة لأن ClickHouse يخزّن صفوف كل جزء على disk مرتبةً بحسب مفتاح محدد. وبدلًا من تحديد الصفوف الفردية مباشرةً (كما في الفهرس المعتمد على B-Tree)، يتيح الفهرس الأساسي المتفرق تحديد مجموعات الصفوف التي قد تطابق query بسرعة (عبر binary search على index entries). ثم تُبث مجموعات الصفوف المحتمل تطابقها، بالتوازي، إلى ClickHouse engine للعثور على التطابقات الفعلية. ويجعل تصميم الفهرس هذا الفهرس الأساسي صغيرًا (بحيث يتسع بالكامل في main memory)، مع تسريع execution time للاستعلامات بشكل ملحوظ، لا سيما استعلامات النطاق الشائعة في حالات استخدام analytics.
ستُرتَّب جميع الأعمدة في الجدول استنادًا إلى قيمة مفتاح الترتيب المحدد، سواء أكانت مُدرجة في المفتاح نفسه أم لا. فعلى سبيل المثال، إذا استُخدمCreationDateكمفتاح، فسيطابق ترتيب القيم في جميع الأعمدة الأخرى ترتيب القيم في العمودCreationDate. ويمكن تحديد عدة مفاتيح ترتيب، وعندها سيكون الترتيب بنفس دلالات عبارةORDER BYفي query من نوعSELECT.
اختيار مفتاح الترتيب
التقسيمات
hash على مفتاح. ويتيح ذلك للمسؤولين تنظيم البيانات وفق معايير محددة مثل النطاقات الزمنية أو المواقع الجغرافية. ويساعد التقسيم في تحسين أداء الاستعلامات من خلال تسريع الوصول إلى البيانات عبر استبعاد الأقسام غير اللازمة أثناء الاستعلام وفهرسة أكثر كفاءة. كما يفيد في مهام الصيانة، مثل النسخ الاحتياطية وحذف البيانات، إذ يتيح تنفيذ العمليات على أقسام فردية بدلًا من الجدول بالكامل. بالإضافة إلى ذلك، يمكن أن يحسّن التقسيم قابلية التوسع في قواعد بيانات PostgreSQL بشكل كبير من خلال توزيع الحمل على عدة أقسام.
في ClickHouse، يُحدَّد التقسيم للجدول عند تعريفه أول مرة عبر العبارة PARTITION BY. ويمكن أن تحتوي هذه العبارة على تعبير SQL على أي أعمدة، وتحدّد نتيجته القسم الذي يُرسل إليه الصف.
ترتبط أجزاء البيانات منطقيًا بكل قسم على القرص، ويمكن الاستعلام عنها بشكل مستقل. في المثال أدناه، نقسّم جدول posts حسب السنة باستخدام التعبير toYear(CreationDate). ومع إدراج الصفوف في ClickHouse، سيُقيَّم هذا التعبير لكل صف ويُوجَّه إلى القسم الناتج إذا كان موجودًا (وإذا كان هذا الصف هو الأول لتلك السنة، فسيُنشأ القسم).
استخدامات التقسيم
- إدارة البيانات - في ClickHouse، ينبغي أساسًا النظر إلى التقسيم على أنه ميزة لإدارة البيانات، وليس أسلوبًا لتحسين الاستعلامات. ومن خلال فصل البيانات منطقيًا استنادًا إلى مفتاح، يمكن التعامل مع كل قسم بصورة مستقلة، مثل حذفه. يتيح لك ذلك نقل الأقسام، وبالتالي المجموعات الفرعية، بين طبقات التخزين بكفاءة استنادًا إلى الوقت أو انتهاء صلاحية البيانات/حذفها بكفاءة من العنقود. في المثال أدناه، نحذف المنشورات من عام 2008.
- تحسين الاستعلامات - رغم أن الأقسام قد تساعد في تحسين أداء الاستعلامات، فإن ذلك يعتمد بدرجة كبيرة على أنماط الوصول. فإذا كانت الاستعلامات تستهدف عددًا قليلًا فقط من الأقسام (ويُفضَّل أن يكون قسمًا واحدًا)، فقد يتحسن الأداء. ويكون هذا مفيدًا عادةً فقط إذا لم يكن مفتاح التقسيم ضمن المفتاح الأساسي، وكانت التصفية تتم بناءً عليه. ومع ذلك، فإن الاستعلامات التي تحتاج إلى تغطية عدد كبير من الأقسام قد يكون أداؤها أسوأ مما لو لم يُستخدم التقسيم أصلًا (إذ قد يؤدي التقسيم إلى زيادة عدد الأجزاء). كما أن فائدة استهداف قسم واحد ستكون أقل وضوحًا، وقد تنعدم تمامًا، إذا كان مفتاح التقسيم بالفعل عنصرًا مبكرًا في المفتاح الأساسي. ويمكن أيضًا استخدام التقسيم لتحسين استعلامات GROUP BY إذا كانت القيم داخل كل قسم فريدة. لكن بشكل عام، ينبغي التأكد من تحسين المفتاح الأساسي، وعدم اللجوء إلى التقسيم كتقنية لتحسين الاستعلامات إلا في حالات استثنائية تكون فيها أنماط الوصول موجَّهة إلى مجموعة فرعية محددة ويمكن التنبؤ بها من البيانات، مثل التقسيم حسب اليوم، عندما تتركز معظم الاستعلامات على اليوم الأخير.
توصيات بشأن الأقسام
داخليًا، ينشئ ClickHouse أجزاءً للبيانات المُدرجة. ومع إدراج المزيد من البيانات، يزداد عدد الأجزاء. ولمنع ارتفاع عدد الأجزاء بشكل مفرط، وهو ما سيؤدي إلى تراجع أداء الاستعلامات (بسبب زيادة عدد الملفات المطلوب قراءتها)، تُدمج الأجزاء معًا في عملية غير متزامنة تُجرى في الخلفية. وإذا تجاوز عدد الأجزاء حدًا مُعدًا مسبقًا، فسيطرح ClickHouse استثناءً عند insert على شكل خطأ “too many parts”. ولا ينبغي أن يحدث هذا في التشغيل المعتاد، ولا يقع إلا إذا كان ClickHouse مُهيأً على نحو غير صحيح أو استُخدم بشكل خاطئ، مثل تنفيذ عدد كبير من عمليات insert الصغيرة.
وبما أن الأجزاء تُنشأ لكل قسم بشكل مستقل، فإن زيادة عدد الأقسام تؤدي إلى زيادة عدد الأجزاء؛ أي إن عددها يصبح مضاعفًا لعدد الأقسام. لذلك، قد تتسبب مفاتيح التقسيم عالية العددية في هذا الخطأ، وينبغي تجنبها.
العروض المادية مقابل الإسقاطات
ORDER BY لجدول واحد.
في وثائق نمذجة البيانات الخاصة بـ ClickHouse، نستعرض كيف يمكن استخدام العروض المادية في ClickHouse للاحتساب المسبق للتجميعات، وتحويل الصفوف، وتحسين الاستعلامات لأنماط وصول مختلفة.
وفي الحالة الأخيرة، قدمنا مثالًا يُرسل فيه العرض المادي الصفوف إلى جدول الهدف ذي مفتاح ترتيب مختلف عن الجدول الأصلي الذي يستقبل عمليات الإدراج.
على سبيل المثال، تأمل الاستعلام التالي:
UserId ليس مفتاح الترتيب.
في السابق، عالجنا هذا باستخدام عرض مادي يعمل كآلية بحث عن PostId. ويمكن حل المشكلة نفسها
باستخدام إسقاط. يضيف الأمر أدناه
إسقاطًا لـ ORDER BY user_id.
ALTER، فسيكون الإنشاء غير متزامن عند إصدار الأمر MATERIALIZE PROJECTION. يمكنك التحقق من تقدّم هذه العملية باستخدام الاستعلام التالي، مع انتظار is_done=1.
EXPLAIN، نؤكد أيضًا أنه تم استخدام الإسقاط لتنفيذ هذا الاستعلام:
متى تستخدم إسقاطات
- تكون هناك حاجة إلى إعادة ترتيب كاملة للبيانات. وبينما يمكن للتعبير في
إسقاط، من الناحية النظرية، أن يستخدم
GROUP BY,فإن العروض المادية تكون أكثر فاعلية في الحفاظ على التجميعات. كما أن مُحسِّن الاستعلامات يكون أكثر احتمالًا للاستفادة من إسقاطات التي تستخدم إعادة ترتيب بسيطة، أيSELECT * ORDER BY x. يمكنك اختيار مجموعة فرعية من الأعمدة في هذا التعبير لتقليل البصمة التخزينية. - يكون المستخدمون متقبلين للزيادة المصاحبة في البصمة التخزينية وعبء كتابة البيانات مرتين. اختبر التأثير في سرعة الإدراج وقيّم العبء التخزيني.
اعتبارًا من الإصدار 25.5، يدعم ClickHouse العمود الافتراضي
_part_offset في
إسقاطات. يتيح ذلك طريقة أكثر كفاءة من حيث المساحة لتخزين إسقاطات.لمزيد من التفاصيل، راجع “Projections”