QueryContexts
QueryContext. يحتوي QueryContext على البُنى الأساسية المستخدمة لبناء الاستعلامات على قاعدة بيانات ClickHouse، بالإضافة إلى الإعدادات المستخدمة لمعالجة النتيجة وتحويلها إلى QueryResult أو أي بنية بيانات استجابة أخرى. ويشمل ذلك الاستعلام نفسه، والمعلمات، والإعدادات، وتنسيقات القراءة، وخصائص أخرى.
يمكن الحصول على QueryContext باستخدام طريقة العميل create_query_context. وتستقبل هذه الطريقة المعلمات نفسها التي تستقبلها طريقة الاستعلام الأساسية. ويمكن بعد ذلك تمرير سياق الاستعلام هذا إلى الطرق query أو query_df أو query_np باعتباره وسيط الكلمة المفتاحية context بدلًا من أي من الوسائط الأخرى لهذه الطرق أو جميعها. لاحظ أن أي وسائط إضافية تُحدَّد عند استدعاء الطريقة ستتجاوز أي خصائص في QueryContext.
أوضح Use case لـ QueryContext هو إرسال الاستعلام نفسه مع قيم مختلفة لمَعلمات الربط. ويمكن تحديث جميع قيم المعلمات باستدعاء الطريقة QueryContext.set_parameters باستخدام قاموس، كما يمكن تحديث أي قيمة مفردة باستدعاء QueryContext.set_parameter باستخدام زوج key وvalue المطلوب.
QueryContext ليست آمنة للاستخدام عبر الخيوط، ولكن يمكن الحصول على نسخة منها في بيئة متعددة الخيوط عبر استدعاء التابع QueryContext.updated_copy.
الاستعلامات المتدفقة
query_column_block_stream— يعيد بيانات query في كتل على هيئة تسلسل من الأعمدة باستخدام كائنات بايثون الأصليةquery_row_block_stream— يعيد بيانات query على هيئة كتلة من الصفوف باستخدام كائنات بايثون الأصليةquery_rows_stream— يعيد بيانات query كتسلسل من الصفوف باستخدام كائنات بايثون الأصليةquery_np_stream— يعيد كل كتلة من بيانات query في ClickHouse كمصفوفة NumPyquery_df_stream— يعيد كل كتلة من بيانات query في ClickHouse على هيئة Pandas DataFramequery_arrow_stream— يعيد بيانات query على هيئة PyArrow RecordBlocksquery_df_arrow_stream— يعيد كل كتلة من بيانات query في ClickHouse على هيئة Pandas DataFrame مستند إلى Arrow أو Polars DataFrame، وفقًا للوسيطdataframe_library(القيمة default هي “pandas”).
ContextStream، ويجب فتحه باستخدام عبارة with لبدء استهلاك التدفق.
كتل البيانات
query الأساسية كتدفق من الكتل التي يتلقاها من خادم ClickHouse. وتُنقل هذه الكتل من ClickHouse وإليه باستخدام تنسيق “Native” المخصص. والـ”كتلة” هي ببساطة تسلسل من أعمدة البيانات الثنائية، حيث يحتوي كل عمود على عدد متساوٍ من قيم البيانات من نوع البيانات المحدد. (وبما أن ClickHouse قاعدة بيانات عمودية، فهو يخزّن هذه البيانات بصيغة مشابهة.) ويتحكم في حجم الكتلة المُعادة من الاستعلام إعدادان للمستخدم يمكن ضبطهما على عدة مستويات (ملف تعريف المستخدم، أو المستخدم، أو الجلسة، أو الاستعلام). وهما:
- max_block_size — حد حجم الكتلة بالصفوف. القيمة الافتراضية 65536.
- preferred_block_size_bytes — حد غير صارم لحجم الكتلة بالبايت. القيمة الافتراضية 1,000,0000.
preferred_block_size_setting، لن تتجاوز أي كتلة أبدًا max_block_size صفًا. وبحسب نوع الاستعلام، قد تأتي الكتل الفعلية المُعادة بأي حجم. على سبيل المثال، قد تتضمن الاستعلامات على جدول موزّع يغطي عدة شظايا كتلًا أصغر جرى جلبها مباشرةً من كل شظية.
عند استخدام إحدى طرائق Client query_*_stream، تُعاد النتائج كتلةً بكتلة. ولا يحمّل ClickHouse Connect سوى كتلة واحدة في كل مرة. ويتيح ذلك معالجة كميات كبيرة من البيانات دون الحاجة إلى تحميل مجموعة نتائج كبيرة كاملةً إلى الذاكرة. لاحظ أنه ينبغي أن يكون التطبيق مستعدًا لمعالجة أي عدد من الكتل، ولا يمكن التحكم في الحجم الدقيق لكل كتلة.
مخزن مؤقت لبيانات HTTP للمعالجة البطيئة
http_buffer_size. وعادةً ما تكون قيم http_buffer_size الكبيرة مناسبة في هذه الحالة إذا كانت هناك ذاكرة كافية متاحة للتطبيق. وتُخزَّن البيانات في المخزن المؤقت بشكل مضغوط عند استخدام ضغط lz4 أو zstd، لذا فإن استخدام نوعَي الضغط هذين يزيد الحجم الإجمالي للمخزن المؤقت المتاح.
StreamContexts
query_*_stream (مثل query_row_block_stream) كائن StreamContext من ClickHouse، وهو كائن مدمج يجمع بين السياق والمولِّد في بايثون. وهذا هو الاستخدام الأساسي:
StreamContext من دون تعليمة with ستؤدي إلى حدوث خطأ. ويضمن استخدام سياق بايثون إغلاق التدفق (في هذه الحالة، استجابة HTTP متدفقة) بشكل صحيح حتى إذا لم تُستهلك جميع البيانات و/أو حدث استثناء أثناء المعالجة. كذلك، لا يمكن استخدام StreamContext لاستهلاك التدفق إلا مرة واحدة. وستؤدي محاولة استخدام StreamContext بعد الخروج منه إلى ظهور StreamClosedError.
يمكنك استخدام الخاصية source في StreamContext للوصول إلى الكائن الأب QueryResult، الذي يتضمن أسماء الأعمدة وأنواعها.
أنواع التدفق
query_column_block_stream الكتلة كتسلسل من بيانات الأعمدة المخزَّنة على هيئة أنواع بيانات بايثون الأصلية. وباستخدام استعلامات taxi_trips أعلاه، ستكون البيانات المعادة قائمةً يكون كل عنصر فيها قائمةً أخرى (أو tuple) تضم كل البيانات الخاصة بالعمود المقابل. لذا فإن block[0] سيكون tuple لا يحتوي إلا على سلاسل نصية. وتُستخدم التنسيقات المعتمدة على الأعمدة غالبًا لإجراء عمليات تجميعية على جميع القيم في عمود معيّن، مثل جمع إجمالي الأجور.
تعيد الطريقة query_row_block_stream الكتلة كتسلسل من الصفوف، كما في قواعد البيانات العلائقية التقليدية. وبالنسبة إلى رحلات التاكسي، ستكون البيانات المعادة قائمةً يكون كل عنصر فيها قائمةً أخرى تمثل صفًا من البيانات. لذا فإن block[0] سيحتوي على جميع الحقول (بالترتيب) لأول رحلة تاكسي، وblock[1] سيحتوي على صف يضم جميع الحقول الخاصة برحلة التاكسي الثانية، وهكذا. وتُستخدم النتائج المعتمدة على الصفوف عادةً لأغراض العرض أو عمليات التحويل.
تُعد query_row_stream طريقةً مريحة تنتقل تلقائيًا إلى الكتلة التالية عند التكرار عبر التدفق. وبخلاف ذلك، فهي مطابقة لـ query_row_block_stream.
تعيد الطريقة query_np_stream كل كتلة على شكل مصفوفة NumPy ثنائية الأبعاد. داخليًا، تُخزَّن مصفوفات NumPy (عادةً) على هيئة أعمدة، لذلك لا حاجة إلى طرق منفصلة للصفوف أو الأعمدة. وسيُعبَّر عن “shape” لمصفوفة NumPy بالشكل (الأعمدة، الصفوف). وتوفّر مكتبة NumPy العديد من الطرق لمعالجة مصفوفات NumPy. لاحظ أنه إذا كانت جميع الأعمدة في الاستعلام تشترك في نوع بيانات NumPy نفسه (dtype)، فإن مصفوفة NumPy المعادة سيكون لها dtype واحد أيضًا، ويمكن إعادة تشكيلها/تدويرها من دون تغيير بنيتها الداخلية فعليًا.
تعيد الطريقة query_df_stream كل كتلة ClickHouse على شكل Pandas DataFrame ثنائية الأبعاد. إليك مثالًا يوضّح أنه يمكن استخدام الكائن StreamContext كسياق بصورة مؤجلة (ولكن مرة واحدة فقط).
query_df_arrow_stream كل كتلة من ClickHouse على هيئة DataFrame باستخدام backend لأنواع بيانات PyArrow. تدعم هذه الدالة كلاً من DataFrame في Pandas (الإصدار 2.x أو أحدث) وPolars عبر المعامل dataframe_library (وقيمته الافتراضية "pandas"). ويُنتج كل تكرار DataFrame مُحوَّلاً من دفعات سجلات PyArrow، مما يوفّر أداءً أفضل وكفاءة أعلى في استخدام الذاكرة لبعض أنواع البيانات.
أخيرًا، تُرجِع الدالة query_arrow_stream نتيجة ClickHouse بتنسيق ArrowStream على هيئة pyarrow.ipc.RecordBatchStreamReader ومغلّفة داخل StreamContext. ويُرجِع كل تكرار في هذا stream قيمة PyArrow RecordBlock.
أمثلة على البيانات المتدفقة
تدفّق الصفوف
تدفق كتل الصفوف
تدفق Pandas DataFrames
تدفق دفعات Arrow
استعلامات NumPy وPandas وArrow
استعلامات NumPy
query_np نتائج الاستعلام على شكل مصفوفة NumPy بدلًا من كائن QueryResult في ClickHouse Connect.
استعلامات Pandas
query_df نتائج الاستعلام في صورة Pandas DataFrame بدلًا من QueryResult في ClickHouse Connect.
استعلامات PyArrow
query_arrow نتائج الاستعلام بصيغة PyArrow Table. وهي تستخدم تنسيق ClickHouse Arrow مباشرةً، لذا لا تقبل إلا ثلاث وسائط مشتركة مع الدالة الرئيسية query: query وparameters وsettings. بالإضافة إلى ذلك، هناك وسيطة إضافية هي use_strings، وتحدد ما إذا كان Arrow Table سيعرض أنواع ClickHouse String كسلاسل نصية (إذا كانت True) أو كبايتات (إذا كانت False).
DataFrames المدعومة بـ Arrow
query_df_arrow وquery_df_arrow_stream. وهما مجرد غلافين خفيفين حول طرق query الخاصة بـ Arrow، ويجريان تحويلات من دون نسخ إلى DataFrame حيثما أمكن:
query_df_arrow: ينفّذ الاستعلام باستخدام تنسيق الإخراجArrowفي ClickHouse ويُرجع DataFrame.- بالنسبة إلى
dataframe_library='pandas'، يُرجع DataFrame من pandas 2.x باستخدام أنواع بيانات مدعومة بـ Arrow (pd.ArrowDtype). ويتطلب ذلك pandas 2.x ويستفيد من مخازن من دون نسخ حيثما أمكن لتحقيق أداء ممتاز وتقليل استهلاك الذاكرة الإضافي. - بالنسبة إلى
dataframe_library='polars'، يُرجع DataFrame من Polars مُنشأ من Arrow Table (pl.from_arrow)، وهو بالكفاءة نفسها ويمكن أن يكون من دون نسخ بحسب البيانات.
- بالنسبة إلى
query_df_arrow_stream: يبث النتائج على شكل تسلسل من DataFrame (pandas 2.x أو Polars) مُحوَّلة من دفعات تدفق Arrow.
من الاستعلام إلى DataFrame مستند إلى Arrow
ملاحظات ومحاذير
- مطابقة أنواع Arrow: عند إرجاع البيانات بتنسيق Arrow، يطابق ClickHouse الأنواع مع أقرب أنواع Arrow المدعومة. بعض أنواع ClickHouse لا تملك مقابلًا أصليًا في Arrow، لذا تُعاد على هيئة بايتات خام في حقول Arrow (عادةً
BINARYأوFIXED_SIZE_BINARY).- أمثلة: يُمثَّل
IPv4على هيئة ArrowUINT32؛ بينما يُمثَّلIPv6والأعداد الصحيحة الكبيرة (Int128/UInt128/Int256/UInt256) غالبًا على هيئةFIXED_SIZE_BINARY/BINARYمع بايتات خام. - في هذه الحالات، سيحتوي عمود DataFrame على قيم بايتية تستند إلى حقل Arrow؛ وتقع على شيفرة العميل مسؤولية تفسير هذه البايتات أو تحويلها وفقًا لدلالات ClickHouse.
- أمثلة: يُمثَّل
- أنواع بيانات Arrow غير المدعومة (مثل UUID/ENUM كأنواع Arrow حقيقية) لا يتم إخراجها؛ بل تُمثَّل القيم باستخدام أقرب نوع Arrow مدعوم (وغالبًا على شكل بايتات ثنائية) في المخرجات.
- متطلب Pandas: تتطلب
dtypesالمستندة إلى Arrow إصدار pandas 2.x. بالنسبة إلى إصدارات pandas الأقدم، استخدمquery_df(غير Arrow) بدلًا من ذلك. - السلاسل النصية مقابل البيانات الثنائية: يتحكم الخيار
use_strings(عندما يكون مدعومًا بواسطة إعداد الخادمoutput_format_arrow_string_as_string) فيما إذا كانت أعمدة ClickHouseStringستُعاد كسلاسل نصية في Arrow أو كبيانات ثنائية.
أمثلة على تحويل الأنواع غير المتطابقة بين ClickHouse وArrow
FIXED_SIZE_BINARY أو BINARY)، تكون مسؤولية شيفرة التطبيق تحويل هذه البايتات إلى أنواع بايثون المناسبة. توضّح الأمثلة أدناه أن بعض التحويلات يمكن إجراؤها باستخدام واجهات برمجة تطبيقات لمكتبات DataFrame، بينما قد تتطلب تحويلات أخرى أساليب من بايثون الخالص مثل struct.unpack (وهي تضحي بالأداء لكنها تحافظ على المرونة).
قد تصل أعمدة Date بصيغة UINT16 (عدد الأيام منذ حقبة Unix، 1970‑01‑01). ويكون التحويل داخل DataFrame فعّالًا ومباشرًا:
Int128 بتنسيق FIXED_SIZE_BINARY على شكل raw bytes. ويوفّر Polars دعمًا أصليًا للأعداد الصحيحة ذات 128 بت:
تنسيقات القراءة
query وquery_np وquery_df. (أما raw_query وquery_arrow فلا تُعدِّلان البيانات الواردة من ClickHouse، لذلك لا ينطبق عليهما التحكم في التنسيق.) على سبيل المثال، إذا تغيّر تنسيق القراءة لـ UUID من التنسيق الافتراضي native إلى التنسيق البديل string، فستُعاد قيم عمود UUID في استعلام ClickHouse كسلاسل نصية (باستخدام تنسيق RFC 1422 القياسي 8-4-4-4-12) بدلًا من كائنات UUID في بايثون.
يمكن أن تتضمن وسيطة “نوع البيانات” لأي دالة تنسيق أحرف بدل. ويكون التنسيق سلسلة واحدة بأحرف صغيرة.
يمكن ضبط تنسيقات القراءة على عدة مستويات:
- على المستوى العام، باستخدام الطرق المعرّفة في الحزمة
clickhouse_connect.datatypes.format. ويتحكم ذلك في تنسيق نوع البيانات المُعدّ لجميع الاستعلامات.
- على مستوى الاستعلام بالكامل، باستخدام وسيطة القاموس الاختيارية
query_formats. في هذه الحالة، سيستخدم أي عمود (أو عمود فرعي) من أنواع البيانات المحددة التنسيق المُعد.
- بالنسبة إلى القيم في عمود محدد، باستخدام وسيطة القاموس الاختيارية
column_formats. يكون المفتاح هو اسم العمود كما يعيده ClickHouse، وformatلعمود البيانات أو قاموس “format” من المستوى الثاني يحتوي على اسم نوع في ClickHouse وقيمة من تنسيقات الاستعلام. ويمكن استخدام هذا القاموس الثانوي مع أنواع الأعمدة المتداخلة مثل Tuples أو Maps.
خيارات تنسيق القراءة (أنواع بايثون)
البيانات الخارجية
query* في العميل معاملاً اختياريًا باسم external_data للاستفادة من هذه الميزة. يجب أن تكون قيمة المعامل external_data كائنًا من نوع clickhouse_connect.driver.external.ExternalData. ويقبل مُنشئ هذا الكائن المعاملات التالية:
لإرسال استعلام مع ملف CSV خارجي يحتوي على بيانات “movie”، ودمج هذه البيانات مع جدول
directors الموجود مسبقًا على خادم ClickHouse:
ExternalData الأوّلي باستخدام الأسلوب add_file، الذي يقبل المعلمات نفسها الخاصة بالمنشئ. وبالنسبة إلى HTTP، تُرسَل جميع البيانات الخارجية ضمن عملية رفع ملف multi-part/form-data.
المناطق الزمنية
DateTime64 على هيئة رقم غير مرتبط بمنطقة زمنية، ويمثل عدد الثواني منذ epoch، أي 1970-01-01 00:00:00 بتوقيت UTC. وبالنسبة إلى قيم DateTime64، قد يكون التمثيل بالميلي ثانية أو الميكروثانية أو النانوثانية منذ epoch، وذلك بحسب الدقة. ونتيجةً لذلك، فإن تطبيق أي معلومات متعلقة بالمنطقة الزمنية يحدث دائمًا على طرف العميل. لاحظ أن هذا يتطلب حسابات إضافية مؤثرة، لذلك يُنصح في التطبيقات الحساسة للأداء بالتعامل مع أنواع DateTime على أنها timestamps منذ epoch، باستثناء العرض للمستخدم والتحويل (فعلى سبيل المثال، تكون Pandas Timestamps دائمًا عددًا صحيحًا بطول 64 بت يمثل نانوثواني epoch لتحسين الأداء).
عند استخدام أنواع بيانات مدركة للمنطقة الزمنية في الاستعلامات — وبخاصة الكائن datetime.datetime في بايثون — يطبّق clickhouse-connect منطقة زمنية على طرف العميل وفق قواعد الأولوية التالية:
- إذا تم تحديد المعامل الخاص بـ method للاستعلام
client_tzs، فستُطبَّق المنطقة الزمنية الخاصة بالعمود المحدد - إذا كان العمود في ClickHouse يحتوي على metadata للمنطقة الزمنية (أي إذا كان من نوع مثل DateTime64(3, ‘America/Denver’))، فستُطبَّق timezone الخاصة بعمود ClickHouse. (لاحظ أن metadata الخاصة بالمنطقة الزمنية هذه لا تكون متاحة لـ clickhouse-connect بالنسبة إلى أعمدة DateTime قبل version 23.2 من ClickHouse)
- إذا تم تحديد المعامل الخاص بـ method للاستعلام
query_tzلهذا الاستعلام، فستُطبَّق “timezone الخاصة بالاستعلام”. - إذا طُبّق إعداد timezone على الاستعلام أو session، فستُطبَّق تلك timezone. (هذه الوظيفة لم تُطرح بعد في ClickHouse server)
- وأخيرًا، إذا كان المعامل الخاص بالعميل
apply_server_timezoneمضبوطة على True (وهو الإعداد الافتراضي)، فستُطبَّق server timezone الخاصة بـ ClickHouse.
clickhouse-connect سيُرجع دائمًا كائن datetime.datetime في بايثون غير مرتبط بمنطقة زمنية. ويمكن بعد ذلك إضافة معلومات منطقة زمنية إضافية إلى هذا الكائن غير المرتبط بمنطقة زمنية من خلال شيفرة التطبيق عند الحاجة.