مجموعة بيانات Stack Overflow
s3://datasets-documentation/stackoverflow/parquet/:
المفاتيح الأساسية والعلاقات الموضحة ليست مفروضة من خلال القيود (إذ إن Parquet تنسيق ملفات وليس تنسيق جداول)، وإنما تشير فقط إلى كيفية ارتباط البيانات والمفاتيح الفريدة التي تتضمنها.
تحتوي مجموعة بيانات Stack Overflow على عدد من الجداول المترابطة. في أي مهمة لنمذجة البيانات، نوصي المستخدمين بالتركيز أولًا على تحميل جدولهم الأساسي. وليس بالضرورة أن يكون هذا أكبر جدول، بل الجدول الذي تتوقع أن تَرِد عليه معظم الاستعلامات التحليلية. يتيح لك ذلك التعرّف على مفاهيم ClickHouse الأساسية وأنواعها، وهو أمر مهم خاصةً إذا كنت قادمًا من خلفية يغلب عليها OLTP. وقد يتطلب هذا الجدول إعادة نمذجة مع إضافة جداول أخرى للاستفادة الكاملة من ميزات ClickHouse وتحقيق أفضل أداء. المخطط أعلاه غير مثالي عمدًا لأغراض هذا الدليل.
إنشاء المخطط الأولي
posts سيكون الهدف لمعظم استعلامات التحليلات، فإننا نركز على إنشاء مخطط لهذا الجدول. هذه البيانات متاحة في حاوية S3 العامة s3://datasets-documentation/stackoverflow/parquet/posts/*.parquet، مع ملف لكل سنة.
يمثّل تحميل البيانات من S3 بتنسيق Parquet الطريقة الأكثر شيوعًا والمفضلة لتحميل البيانات إلى ClickHouse. وقد صُمّم ClickHouse لمعالجة Parquet بكفاءة، ويمكنه نظريًا قراءة وإدراج عشرات الملايين من الصفوف من S3 في الثانية الواحدة.يوفّر ClickHouse إمكانية استدلال المخطط لتحديد الأنواع تلقائيًا لمجموعة بيانات. وهذا مدعوم لجميع تنسيقات البيانات، بما في ذلك Parquet. ويمكننا الاستفادة من هذه الميزة لتحديد أنواع ClickHouse للبيانات عبر دالة الجدول S3 والأمر
DESCRIBE. لاحظ أدناه أننا نستخدم نمط glob *.parquet لقراءة جميع الملفات في المجلد stackoverflow/parquet/posts.
تتيح دالة الجدول S3 الاستعلام عن البيانات الموجودة في S3 مباشرةً من ClickHouse دون الحاجة إلى استيرادها أولًا. هذه الدالة متوافقة مع جميع تنسيقات الملفات التي يدعمها ClickHouse.يوفّر لنا هذا مخططًا أوليًا غير محسّن. افتراضيًا، يعيّن ClickHouse هذه الأنواع إلى أنواع Nullable المكافئة. يمكننا إنشاء جدول ClickHouse باستخدام هذه الأنواع عبر أمر
CREATE EMPTY AS SELECT بسيط.
ORDER BY () أنه ليس لدينا فهرس، وبشكل أدق لا يوجد أي ترتيب في بياناتنا. سنتناول هذا بمزيد من التفصيل لاحقًا. أما الآن، فيكفي أن تعرف أن جميع الاستعلامات ستتطلب مسحًا خطيًا.
لتأكيد أنه تم إنشاء الجدول:
INSERT INTO SELECT، مع قراءة البيانات عبر دالة الجدول S3. يحمّل المثال التالي بيانات posts في نحو دقيقتين على مثيل ClickHouse Cloud مزوّد بثماني نوى.
يحمّل الاستعلام أعلاه 60 مليون صف. ومع أن هذا العدد يُعد صغيرًا بالنسبة إلى ClickHouse، فقد يرغب المستخدمون الذين لديهم اتصالات إنترنت أبطأ في تحميل مجموعة فرعية من البيانات. ويمكن تحقيق ذلك ببساطة من خلال تحديد السنوات التي يريدون تحميلها باستخدام نمط glob، مثلhttps://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/posts/2008.parquetأوhttps://datasets-documentation.s3.eu-west-3.amazonaws.com/stackoverflow/parquet/posts/{2008, 2009}.parquet. راجع هنا لمعرفة كيفية استخدام أنماط glob لاستهداف مجموعات فرعية من الملفات.
تحسين الأنواع
لمعرفة سبب كفاءة ClickHouse العالية في ضغط البيانات، نوصي بقراءة هذه المقالة. باختصار، وبحكم أنه قاعدة بيانات موجَّهة بالأعمدة، تُكتب القيم بترتيب الأعمدة. وإذا كانت هذه القيم مرتبة، فستتجاور القيم المتشابهة. وتستفيد خوارزميات الضغط من الأنماط المتجاورة في البيانات. وإضافةً إلى ذلك، يوفّر ClickHouse codecs وأنواع بيانات دقيقة التحبّب تتيح لك ضبط تقنيات الضغط بمزيد من الدقة.يتأثر الضغط في ClickHouse بثلاثة عوامل رئيسية: ordering key، وأنواع البيانات، وأي codecs مستخدمة. وكل ذلك يُضبط عبر المخطط. يمكن تحقيق أكبر تحسن أولي في الضغط وأداء الاستعلامات من خلال عملية بسيطة لتحسين الأنواع. ويمكن تطبيق بعض القواعد البسيطة لتحسين المخطط:
- استخدم أنواعًا صارمة - استخدم المخطط الأولي لدينا Strings في كثير من الأعمدة التي من الواضح أنها رقمية. ويضمن استخدام الأنواع الصحيحة الدلالات المتوقعة عند التصفية والتجميع. وينطبق الأمر نفسه على أنواع التاريخ، التي وُفِّرت بالفعل بشكل صحيح في ملفات Parquet.
- تجنب الأعمدة Nullable - يُفترض افتراضيًا أن الأعمدة أعلاه يمكن أن تكون Null. ويتيح النوع Nullable للاستعلامات التمييز بين القيمة الفارغة وقيمة Null. ويؤدي ذلك إلى إنشاء عمود منفصل من النوع UInt8. ويجب معالجة هذا العمود الإضافي في كل مرة يعمل فيها المستخدم مع عمود Nullable. وهذا يؤدي إلى استهلاك مساحة تخزين إضافية، وغالبًا ما يؤثر سلبًا في أداء الاستعلامات. لا تستخدم Nullable إلا إذا كان هناك فرق فعلي بين القيمة الفارغة الافتراضية لنوع ما وقيمة Null. فعلى سبيل المثال، من المرجح أن تكون القيمة 0 للقيم الفارغة في العمود
ViewCountكافية لمعظم الاستعلامات ولن تؤثر في النتائج. وإذا كان ينبغي التعامل مع القيم الفارغة بصورة مختلفة، فيمكن غالبًا استبعادها من الاستعلامات باستخدام filter. - استخدم أقل precision ممكن للأنواع الرقمية - يوفّر ClickHouse عددًا من الأنواع الرقمية المصممة لنطاقات رقمية ومستويات precision مختلفة. واحرص دائمًا على تقليل عدد bits المستخدمة لتمثيل العمود. وإلى جانب الأعداد الصحيحة ذات الأحجام المختلفة مثل Int16، يوفّر ClickHouse أيضًا متغيرات غير موقعة تكون قيمتها الدنيا 0. ويمكن أن يتيح ذلك استخدام bits أقل للعمود؛ فعلى سبيل المثال، الحد الأقصى لـ UInt16 هو 65535، أي ضعف Int16. ويفضَّل استخدام هذه الأنواع بدلًا من المتغيرات الموقعة الأكبر متى أمكن.
- أقل precision لأنواع التاريخ - يدعم ClickHouse عددًا من أنواع التاريخ والتاريخ والوقت. ويمكن استخدام Date وDate32 لتخزين التواريخ فقط، مع دعم الثاني لنطاق تاريخ أكبر مقابل استخدام bits أكثر. ويوفّر DateTime وDateTime64 دعمًا لقيم date time. ويقتصر DateTime على granularity بالثانية ويستخدم 32 bits. أما DateTime64، وكما يوحي الاسم، فيستخدم 64 bits، لكنه يوفّر دعمًا يصل إلى granularity بالنانوثانية. وكما هو الحال دائمًا، اختر الإصدار الأقل دقة المقبول لاستعلاماتك، مع تقليل عدد bits المطلوبة.
- استخدم LowCardinality - يمكن ترميز الأعمدة من نوع Numbers وStrings وDate أو DateTime التي تحتوي على عدد قليل من القيم الفريدة باستخدام النوع LowCardinality. ويستخدم هذا النوع Dictionary لترميز القيم، مما يقلل الحجم على القرص. فكّر في هذا الخيار للأعمدة التي تحتوي على أقل من 10 آلاف قيمة فريدة.
- استخدم FixedString للحالات الخاصة - يمكن ترميز Strings ذات الطول الثابت بالنوع FixedString، مثل رموز اللغات والعملات. ويكون هذا فعالًا عندما يكون طول البيانات N بايتًا بالضبط. وفي جميع الحالات الأخرى، يُرجَّح أن يقلل الكفاءة، ويُفضَّل LowCardinality.
- استخدم Enums للتحقق من صحة البيانات - يمكن استخدام النوع Enum لترميز الأنواع المعدودة بكفاءة. ويمكن أن تكون Enums بطول 8 أو 16 bits، بحسب عدد القيم الفريدة المطلوب تخزينها. وفكّر في استخدام ذلك إذا كنت تحتاج إلى التحقق المرتبط عند insert time (ستُرفض القيم غير المعلنة) أو إذا كنت ترغب في تنفيذ استعلامات تستفيد من ترتيب طبيعي في قيم Enum، مثل عمود feedback يحتوي على ردود المستخدمين
Enum(':(' = 1, ':|' = 2, ':)' = 3).
نصيحة: للعثور على نطاق جميع الأعمدة وعدد القيم المميزة، يمكنك استخدام الاستعلام البسيط SELECT * APPLY min, * APPLY max, * APPLY uniq FROM table FORMAT Vertical. ونوصي بتنفيذ ذلك على subset أصغر من البيانات لأن ذلك قد يكون مكلفًا. ويتطلب هذا الاستعلام أن تكون القيم الرقمية معرّفة على هذا الأساس على الأقل للحصول على نتيجة دقيقة، أي ألا تكون من النوع String.
بتطبيق هذه القواعد البسيطة على posts table لدينا، يمكننا تحديد النوع الأمثل لكل عمود:
ينتج عن ذلك المخطّط التالي:
INSERT INTO SELECT بسيط، عبر قراءة البيانات من الجدول السابق وإدراجها في هذا الجدول:
insert أعلاه هذه القيم ضمنيًا إلى القيم الافتراضية لأنواعها المقابلة: 0 للأعداد الصحيحة وقيمة فارغة للسلاسل النصية. كما يحوّل ClickHouse تلقائيًا أي قيم رقمية إلى الدقة المستهدفة لها.
المفاتيح الأساسية (مفاتيح الترتيب) في ClickHouse
غالبًا ما يبحث المستخدمون القادمون من قواعد بيانات OLTP عن مفهوم مكافئ لذلك في ClickHouse.
اختيار مفتاح الترتيب
ستُرتَّب جميع الأعمدة في الجدول استنادًا إلى قيمة مفتاح الترتيب المحدد، سواء كانت مُدرجة في المفتاح نفسه أم لا. على سبيل المثال، إذا استُخدميمكن تطبيق بعض القواعد البسيطة للمساعدة في اختيار مفتاح ترتيب. وقد تتعارض النقاط التالية أحيانًا، لذا يُستحسن النظر فيها بهذا الترتيب. ويمكنك من خلال هذه العملية تحديد عدد من المفاتيح، ويكون 4-5 منها كافيًا عادةً:CreationDateكمفتاح، فسيطابق ترتيب القيم في جميع الأعمدة الأخرى ترتيب القيم في العمودCreationDate. ويمكن تحديد عدة مفاتيح ترتيب — وسيجري الترتيب هنا وفق الدلالات نفسها لعبارةORDER BYفي استعلامSELECT.
- اختر الأعمدة التي تتوافق مع عوامل التصفية الأكثر شيوعًا لديك. فإذا كان أحد الأعمدة يُستخدم كثيرًا في عبارات
WHERE، فأعطه أولوية عند تضمينه في المفتاح مقارنةً بالأعمدة الأقل استخدامًا. وفضّل الأعمدة التي تساعد، عند التصفية، على استبعاد نسبة كبيرة من إجمالي الصفوف، مما يقلل كمية البيانات التي يلزم قراءتها. - فضّل الأعمدة التي يُرجح أن تكون شديدة الارتباط بأعمدة أخرى في الجدول. فهذا يساعد على ضمان تخزين هذه القيم أيضًا بشكل متجاور، مما يحسّن الضغط.
كما يمكن جعل عمليات
GROUP BYوORDER BYللأعمدة الموجودة في مفتاح الترتيب أكثر كفاءة من حيث الذاكرة.
مثال
posts، فلنفترض أن المستخدمين يريدون إجراء تحليلات تتضمن التصفية حسب التاريخ ونوع المنشور، على سبيل المثال:
“ما الأسئلة التي حصدت أكبر عدد من التعليقات خلال الأشهر الثلاثة الماضية؟”
الاستعلام الخاص بهذا السؤال باستخدام جدول posts_v2 السابق ذي الأنواع المحسّنة ولكن من دون مفتاح ترتيب:
الاستعلام هنا سريع جدًا رغم أنه جرى فحص جميع الصفوف البالغ عددها 60 مليون صف فحصًا خطيًا — ClickHouse سريع فحسب :) سيتعين عليك أن تثق بنا بأن مفاتيح الترتيب تستحق ذلك عند العمل على نطاقات TB وPB!لنحدّد العمودين
PostTypeId وCreationDate كمفاتيح الترتيب لدينا.
ربما نتوقع في حالتنا أن يطبّق المستخدمون دائمًا عامل تصفية على PostTypeId. يبلغ عدد القيم المميّزة هنا 8، ما يجعله الخيار المنطقي للعنصر الأول في مفتاح الترتيب لدينا. ونظرًا إلى أن التصفية بدقة التاريخ يُرجّح أن تكون كافية (مع أنها ستظل مفيدة أيضًا لعوامل تصفية التاريخ والوقت)، فإننا نستخدم toDate(CreationDate) بوصفه المكوّن الثاني من مفتاحنا. وسيؤدي هذا أيضًا إلى إنشاء فهرس أصغر، لأن التاريخ يمكن تمثيله باستخدام 16، مما يسرّع التصفية. أما العنصر الأخير في مفتاحنا فهو CommentCount للمساعدة في العثور على المنشورات الأكثر احتواءً على التعليقات (الفرز النهائي).
التالي: تقنيات نمذجة البيانات
Posts جدولنا المركزي الذي تُجرى من خلاله معظم الاستعلامات التحليلية. ومع أنه لا يزال بالإمكان الاستعلام عن الجداول الأخرى بشكل مستقل، فإننا نفترض أن معظم أعمال التحليلات تُجرى في سياق posts.
في هذا القسم، نستخدم إصدارات محسّنة من جداولنا الأخرى. ومع أننا نوفر مخططاتها، فإننا نغفل القرارات المتخذة فيها اختصارًا. تستند هذه القرارات إلى القواعد الموضحة سابقًا، ونترك للقارئ استنتاجها.تهدف جميع الأساليب التالية إلى تقليل الحاجة إلى استخدام JOINs من أجل تحسين عمليات القراءة وتعزيز أداء الاستعلامات. ومع أن JOINs مدعومة بالكامل في ClickHouse، فإننا نوصي باستخدامها بحذر (فلا بأس بجدولين إلى ثلاثة جداول في استعلام JOIN) لتحقيق أفضل أداء.
لا يدعم ClickHouse مفهوم المفاتيح الخارجية. وهذا لا يمنع عمليات الربط، لكنه يعني أن إدارة التكامل المرجعي تُترك للمستخدم على مستوى التطبيق. في أنظمة OLAP مثل ClickHouse، غالبًا ما تُدار سلامة البيانات على مستوى التطبيق أو أثناء عملية إدخال البيانات، بدلًا من فرضها من قِبل قاعدة البيانات نفسها، لأن ذلك يضيف حملًا إضافيًا كبيرًا. يتيح هذا النهج مرونة أكبر وإدخالًا أسرع للبيانات. وهذا ينسجم مع تركيز ClickHouse على السرعة وقابلية التوسع في استعلامات القراءة والإدراج على مجموعات البيانات الكبيرة جدًا.لتقليل استخدام Joins في وقت الاستعلام، تتوفر للمستخدمين عدة أدوات/أساليب:
- إزالة تطبيع البيانات - أزِل تطبيع البيانات عبر دمج الجداول واستخدام الأنواع المعقدة للعلاقات التي ليست 1:1. وغالبًا ما يتضمن ذلك نقل أي عمليات ربط من وقت الاستعلام إلى وقت الإدراج.
- Dictionaries - ميزة خاصة في ClickHouse للتعامل مع direct joins وعمليات البحث عن المفتاح والقيمة.
- Incremental Materialized Views - ميزة في ClickHouse لنقل كلفة عملية حسابية من وقت الاستعلام إلى وقت الإدراج، بما في ذلك القدرة على حساب القيم التجميعية تدريجيًا.
- Refreshable Materialized Views - على غرار materialized views المستخدمة في منتجات قواعد البيانات الأخرى، يتيح ذلك حساب نتائج الاستعلام دوريًا وتخزين النتيجة مؤقتًا.