- إنشاء مشروع dbt وإعداد مهايئ ClickHouse.
- تعريف نموذج.
- تحديث نموذج.
- إنشاء نموذج تزايدي.
- إنشاء نموذج لقطة.
- استخدام العروض المادية.
الإعداد
إعداد ClickHouse
العمود
created_at في الجدول roles، وتكون قيمته الافتراضية now(). نستخدمه لاحقًا لتحديد التحديثات التزايدية في نماذجنا — راجع النماذج التزايدية.s3 لقراءة البيانات من المصدر عبر نقاط النهاية العامة تمهيدًا لإدراجها. شغّل الأوامر التالية لتعبئة الجداول:
الاتصال بـ ClickHouse
-
أنشئ مشروع dbt. في هذه الحالة، نسمّيه تيمّنًا بمصدر
imdbلدينا. وعندما يُطلب منك ذلك، اخترclickhouseكمصدر قاعدة البيانات. -
انتقل باستخدام
cdإلى مجلد مشروعك: - في هذه المرحلة، ستحتاج إلى محرر النصوص الذي تفضله. في الأمثلة أدناه، نستخدم VS Code الشهير. عند فتح دليل IMDB، ينبغي أن ترى مجموعة من ملفات yml وsql:
-
حدّث ملف
dbt_project.ymlلتحديد أول نموذج لدينا، وهوactor_summary، واضبط profile علىclickhouse_imdb. -
نحتاج بعد ذلك إلى تزويد dbt بتفاصيل الاتصال الخاصة بمثيل ClickHouse لدينا. أضف ما يلي إلى
~/.dbt/profiles.yml.لاحظ ضرورة تعديل اسم المستخدم وكلمة المرور. تتوفر أيضًا إعدادات إضافية موثقة هنا. -
من دليل IMDB، نفّذ الأمر
dbt debugللتأكد من أن dbt قادر على الاتصال بـ ClickHouse.تأكد من أن الاستجابة تتضمنConnection test: [OK connection ok]، ما يشير إلى نجاح الاتصال.
إنشاء تجسيد عرض بسيط
CREATE VIEW AS في ClickHouse. لا يتطلب ذلك أي تخزين إضافي للبيانات، لكنه يكون أبطأ عند الاستعلام مقارنةً بتجسيدات الجداول.
-
من داخل المجلد
imdb، احذف الدليلmodels/example: -
أنشئ ملفًا جديدًا في المجلد
actorsداخل المجلدmodels. هنا ننشئ ملفات يمثّل كل ملف منها نموذجًا لممثل: -
أنشئ ملفَي
schema.ymlوactor_summary.sqlفي المجلدmodels/actors.يُعرّف الملفschema.ymlجداولنا. وستصبح هذه متاحة لاحقًا للاستخدام في وحدات الماكرو. حرّرmodels/actors/schema.ymlليحتوي على هذا المحتوى:يحدّدactors_summary.sqlالنموذج الفعلي لدينا. لاحظ أننا نطلب أيضًا في الدالةconfigأن تتم مَوضعة النموذج كـ view في ClickHouse. ويجري الرجوع إلى جداولنا من ملفschema.ymlعبر الدالةsource؛ فعلى سبيل المثال، يشيرsource('imdb', 'movies')إلى الجدولmoviesفي قاعدة البياناتimdb. حرّرmodels/actors/actors_summary.sqlليحتوي على هذا المحتوى:لاحظ أننا نُدرج العمودupdated_atفي actor_summary النهائي. وسنستخدمه لاحقًا في عمليات الـ materialization التزايدية. -
من المجلد
imdb، نفِّذ الأمرdbt run. -
سيمثّل dbt الـ model كـ view في ClickHouse كما هو مطلوب. ويمكننا الآن تنفيذ query على هذا الـ view مباشرةً. سيكون هذا الـ view قد أُنشئ في قاعدة البيانات
imdb_dbt، ويُحدَّد ذلك من خلال parameter الخاص بـ schema في الملف~/.dbt/profiles.ymlضمن profile clickhouse_imdb.عند الاستعلام عن هذا العرض، يمكننا الحصول على نتائج استعلامنا السابق نفسه بصياغة أبسط:
إنشاء تجسيد لجدول
INSERT TO SELECT. لاحظ أن هذا الجدول سيُعاد إنشاؤه في كل مرة، أي إنه ليس تزايديًا. لذلك قد تؤدي مجموعات النتائج الكبيرة إلى أوقات تنفيذ طويلة — راجع قيود dbt.
-
عدّل الملف
actors_summary.sqlبحيث تُضبط المعلَمةmaterializedعلىtable. لاحظ كيفية تعريفORDER BY، ولاحظ أننا نستخدم محرك الجدولMergeTree: -
من الدليل
imdb، نفّذ الأمرdbt run. قد تستغرق هذه العملية وقتًا أطول قليلًا — نحو 10 ثوانٍ على معظم الأجهزة. -
أكّد إنشاء الجدول
imdb_dbt.actor_summary:ينبغي أن يظهر الجدول بأنواع البيانات المناسبة: -
أكّد أن نتائج هذا الجدول متسقة مع النتائج السابقة. لاحظ التحسّن الملحوظ في وقت الاستجابة الآن بعد أن أصبح هذا النموذج جدولًا:
لا تتردد في تنفيذ استعلامات أخرى على هذا النموذج. على سبيل المثال، أيّ الممثلين لديهم أعلى الأفلام تصنيفًا مع أكثر من 5 ظهورات؟
إنشاء تجسيد تزايدي
-
أولًا، نعدّل نموذجنا ليصبح تزايديًا. وتتطلّب هذه الإضافة ما يلي:
- unique_key - لضمان أن يتمكن المهايئ من تمييز الصفوف بشكل فريد، يجب أن نوفر unique_key - وفي هذه الحالة، يكون الحقل
idمن الاستعلام كافيًا. يضمن ذلك عدم وجود صفوف مكررة في جدولنا المُجسَّد. لمزيد من التفاصيل حول قيود التفرّد، راجع هنا. - Incremental filter - نحتاج أيضًا إلى إخبار dbt بكيفية تحديد الصفوف التي تغيّرت عند تنفيذ تشغيل Incremental. ويتم ذلك من خلال توفير تعبير delta. وعادةً ما يتضمن هذا طابعًا زمنيًا لبيانات الأحداث؛ لذلك نستخدم حقل الطابع الزمني
updated_at. يتيح هذا العمود، الذي تكون قيمته الافتراضية هي now() عند إدراج الصفوف، التعرّف على الأدوار الجديدة. بالإضافة إلى ذلك، نحتاج إلى تحديد الحالة البديلة التي تُضاف فيها عناصر actors جديدة. وباستخدام المتغير{{this}}للدلالة على الجدول المُجسَّد الحالي، نحصل على التعبير التالي:where id > (select max(id) from {{ this }}) or updated_at > (select max(updated_at) from {{this}}). نُضمّن هذا داخل الشرط{% if is_incremental() %}، بما يضمن استخدامه فقط في عمليات التشغيل Incremental وليس عند إنشاء الجدول لأول مرة. لمزيد من التفاصيل حول تصفية الصفوف في نماذج Incremental، راجع هذا النقاش في وثائق dbt.
actor_summary.sqlعلى النحو التالي:لاحظ أن نموذجنا لن يتفاعل إلا مع التحديثات والإضافات على جدوليrolesوactors. وللتعامل مع جميع الجداول، يُنصح المستخدمون بتقسيم هذا النموذج إلى عدة نماذج فرعية، بحيث يكون لكل منها معايير incremental خاصة بها. ويمكن بعد ذلك الإشارة إلى هذه النماذج وربطها ببعضها. ولمزيد من التفاصيل حول الإشارة المتبادلة بين النماذج، راجع هنا. - unique_key - لضمان أن يتمكن المهايئ من تمييز الصفوف بشكل فريد، يجب أن نوفر unique_key - وفي هذه الحالة، يكون الحقل
-
نفّذ
dbt runوتحقّق من نتائج الجدول الناتج: -
سنضيف الآن بيانات إلى نموذجنا لتوضيح كيفية إجراء تحديث تزايدي. أضِف الممثل “Clicky McClickHouse” إلى جدول
actors: -
لنجعل “Clicky” يؤدي دور البطولة في 910 أفلام عشوائية:
-
تحقّق من أنه أصبح بالفعل الممثل الأكثر ظهورًا عبر الاستعلام مباشرةً عن جدول المصدر الأساسي وتجاوز أي نماذج في dbt:
-
نفّذ
dbt runوتحقّق من تحديث النموذج ومطابقته للنتائج أعلاه:
الآليات الداخلية
مهايئ لتنفيذ التحديثات التزايدية:
- يُنشئ
مهايئجدولًا مؤقتًا باسمactor_sumary__dbt_tmp. وتُمرَّر الصفوف التي تغيّرت إلى هذا الجدول على شكل تدفّق. - يُنشأ جدول جديد باسم
actor_summary_new,. ثم تُمرَّر الصفوف من الجدول القديم إلى الجديد على شكل تدفّق، مع التحقق من أن معرّفات الصفوف غير موجودة في الجدول المؤقت. وبهذا تُعالَج التحديثات والتكرارات بفعالية. - تُمرَّر النتائج من الجدول المؤقت إلى جدول
actor_summaryالجديد على شكل تدفّق: - أخيرًا، يُستبدل الجدول الجديد بالنسخة القديمة استبدالًا ذريًا عبر عبارة
EXCHANGE TABLES. ثم يُحذف الجدولان القديم والمؤقت.
استراتيجية الإلحاق (وضع الإدراج فقط)
incremental_strategy. ويمكن ضبطها على القيمة append. وعند ضبطها، تُدرَج الصفوف المحدَّثة مباشرةً في الجدول الهدف (المعروف أيضًا باسم imdb_dbt.actor_summary) ولا يُنشأ أي جدول مؤقت.
ملاحظة: يتطلب وضع الإلحاق فقط أن تكون بياناتك غير قابلة للتغيير، أو أن تكون القيم المكررة مقبولة. إذا كنت تريد نموذج جدول تزايدي يدعم الصفوف المعدَّلة، فلا تستخدم هذا الوضع!
لتوضيح هذا الوضع، سنضيف ممثلًا جديدًا آخر ثم نعيد تشغيل dbt run باستخدام incremental_strategy='append'.
-
اضبط وضع الإلحاق فقط في actor_summary.sql:
-
لنضف ممثلًا مشهورًا آخر - Danny DeBito
-
لنجعل Danny يشارك في 920 فيلمًا عشوائيًا.
-
نفّذ dbt run وتأكد من أن Danny قد أُضيف إلى جدول
actor_summary
query_log مرة أخرى الفروق بين عمليتَي التشغيل التزايديَّتين:
imdb_dbt.actor_summary، ولا يشمل ذلك إنشاء أي جدول.
وضع الحذف والإدراج (تجريبي)
incremental_strategy، أي:
- ينشئ المهايئ جدولًا مؤقتًا باسم
actor_sumary__dbt_tmp. وتُمرَّر الصفوف التي تغيّرت إلى هذا الجدول. - يُنفَّذ أمر
DELETEعلى جدولactor_summaryالحالي. وتُحذف الصفوف حسب المعرّف بالاعتماد علىactor_sumary__dbt_tmp - تُدرَج الصفوف من
actor_sumary__dbt_tmpفيactor_summaryباستخدامINSERT INTO actor_summary SELECT * FROM actor_sumary__dbt_tmp.
وضع insert_overwrite (تجريبي)
- إنشاء جدول مرحلي (مؤقت) له البنية نفسها لعلاقة النموذج التزايدي:
CREATE TABLE {staging} AS {target}. - إدراج السجلات الجديدة فقط (الناتجة عن SELECT) في الجدول المرحلي.
- استبدال الأقسام الجديدة فقط (الموجودة في الجدول المرحلي) في الجدول الهدف.
يوفّر هذا النهج المزايا التالية:
- هو أسرع من الاستراتيجية الافتراضية لأنه لا ينسخ الجدول بالكامل.
- وهو أكثر أمانًا من الاستراتيجيات الأخرى لأنه لا يعدّل الجدول الأصلي حتى تكتمل عملية INSERT بنجاح. وفي حال حدوث فشل أثناء التنفيذ، يبقى الجدول الأصلي من دون تعديل.
- ويطبّق أفضل ممارسات هندسة البيانات المتعلقة بـ “ثبات الأقسام”، مما يبسّط معالجة البيانات التزايدية والمتوازية وعمليات التراجع وغير ذلك.
إنشاء لقطة snapshot
-
أنشئ ملفًا باسم
actor_summaryفي دليل snapshots. -
حدّث محتوى ملف actor_summary.sql بالمحتوى التالي:
- يحدّد استعلام select النتائج التي تريد أخذ snapshot لها بمرور الوقت. وتُستخدم الدالة ref للإشارة إلى نموذج actor_summary الذي أنشأناه سابقًا.
- نحتاج إلى عمود timestamp للإشارة إلى تغيّرات السجلات. يمكن استخدام عمود updated_at لدينا هنا (راجع إنشاء نموذج جدول تزايدي). وتشير parameter المسماة strategy إلى استخدام timestamp للدلالة على التحديثات، بينما تحدد parameter المسماة updated_at العمود الذي سيُستخدم. وإذا لم يكن هذا موجودًا في النموذج لديك، يمكنك بدلًا من ذلك استخدام استراتيجية check. وهذا أقل كفاءةً بدرجة كبيرة، كما يتطلب من المستخدم تحديد قائمة بالأعمدة المطلوب مقارنتها. ويقارن dbt بين القيم الحالية والتاريخية لهذه الأعمدة، ويسجّل أي تغييرات (أو لا يفعل شيئًا إذا كانت متطابقة).
-
نفّذ الأمر
dbt snapshot.
-
عند معاينة عيّنة من هذه البيانات، سترى كيف أضاف dbt العمودين dbt_valid_from و dbt_valid_to. أما الأخير فقيَمه مضبوطة على NULL. وستُحدَّث هذه القيم في عمليات التشغيل اللاحقة.
-
اجعل ممثلنا المفضل Clicky McClickHouse يظهر في عشرة أفلام إضافية.
-
أعِد تنفيذ الأمر
dbt runمن الدليلimdb. سيؤدي ذلك إلى تحديث النموذج التزايدي. وبعد اكتمال ذلك، شغّلdbt snapshotلالتقاط التغييرات. -
إذا أجرينا الآن استعلامًا على اللقطة، فسنلاحظ وجود صفّين لـ Clicky McClickHouse. وأصبحت للسجل السابق الآن قيمة dbt_valid_to. وسُجِّلت القيمة الجديدة بالقيمة نفسها في العمود dbt_valid_from، مع قيمة dbt_valid_to تساوي null. وإذا كانت لدينا صفوف جديدة، فستُضاف هذه أيضًا إلى اللقطة.
استخدام ملفات seed
-
ننشئ قائمة برموز الأنواع من مجموعة البيانات الحالية لدينا. من دليل dbt، استخدم
clickhouse-clientلإنشاء الملفseeds/genre_codes.csv: -
نفّذ الأمر
dbt seed. سيؤدي ذلك إلى إنشاء table جديدة باسمgenre_codesفي database لديناimdb_dbt(كما هو محدد في إعدادات schema) مع rows من ملف CSV الخاص بنا. -
أكّد أنه تم تحميلها: