يفترض ما يلي أنك تُرسل البيانات إلى ClickHouse عبر عميل. وإذا كنت تسحب البيانات إلى ClickHouse، على سبيل المثال باستخدام دوال الجداول المضمّنة مثل s3 وgcs، فنوصي بالاطلاع على دليلنا “تحسين أداء الإدراج والقراءة لـ S3”.
عمليات الإدراج المتزامنة افتراضيًا
استخدم عمليات الإدراج المتزامنة إذا كان بإمكانك تجميع البيانات على جهة العميلإذا لم يكن ذلك ممكنًا، فراجع عمليات الإدراج غير المتزامنة أدناه.
خطوات جهة العميل
خطوات على جهة الخادم
عمليات الإدراج على دفعات في الوضع المتزامن
ضمان إعادة المحاولة دون أثر إضافي
- نجحت عملية الإدراج، لكن العميل لم يتلقَّ تأكيدًا بسبب انقطاع في الشبكة.
- فشلت عملية الإدراج على جهة الخادم وانتهت المهلة الزمنية.
اختر هدف الإدراج المناسب
- نفّذ الإدراج مباشرةً في جدول MergeTree أو ReplicatedMergeTree. هذا هو الخيار الأكثر كفاءة عندما يكون العميل قادرًا على تنفيذ موازنة الحمل عبر الـ shards. عند استخدام
internal_replication = true، يتولى ClickHouse النسخ المتماثل بشفافية. - نفّذ الإدراج في جدول Distributed. يتيح ذلك للعملاء إرسال البيانات إلى أي عقدة وترك ClickHouse يمررها إلى الـ shard الصحيح. هذا أبسط، لكنه أقل أداءً قليلًا بسبب خطوة التمرير الإضافية. ولا يزال
internal_replication = trueموصى به.
اختر التنسيق المناسب
insert واستخدام CPU والذاكرة والكفاءة العامة للنظام.
ومع أن المرونة مفيدة في هندسة البيانات وعمليات الاستيراد المعتمدة على الملفات، ينبغي للتطبيقات إعطاء الأولوية للتنسيقات المحسّنة للأداء:
- Native format (موصى به): الأكثر كفاءة. تنسيق عمودي، ويتطلب حدًا أدنى من التحليل على الخادم. ويُستخدم افتراضيًا في عميلَي Go وPython.
- RowBinary: تنسيق فعّال قائم على الصفوف، وهو مثالي إذا كان التحويل إلى تنسيق عمودي صعبًا من جهة العميل. ويستخدمه عميل Java.
- JSONEachRow: سهل الاستخدام، لكنه مكلف من حيث التحليل. مناسب لحالات الاستخدام منخفضة الحجم أو لعمليات التكامل السريعة.
استخدام الضغط
استخدم LZ4 للسرعة، وZSTD لنسبة الضغط
- LZ4: سريع وخفيف. يقلّل حجم البيانات بدرجة كبيرة مع حد أدنى من الحمل الإضافي على CPU، ما يجعله مثاليًا لعمليات الإدراج عالية الإنتاجية، وهو الخيار الافتراضي في معظم عملاء ClickHouse.
- ZSTD: يوفّر نسبة ضغط أعلى، لكنه يستهلك CPU أكثر. ويكون مفيدًا عندما تكون تكاليف نقل البيانات عبر الشبكة مرتفعة — مثل سيناريوهات العمل عبر المناطق أو لدى موفري الخدمات السحابية — لكنه يزيد قليلًا من متطلبات المعالجة على جهة العميل ووقت فك الضغط على جهة الخادم.
في اختبارات FastFormats benchmark، قلّصت عمليات الإدراج بصيغة Native والمضغوطة باستخدام LZ4 حجم البيانات بأكثر من 50%، ما خفّض وقت إدخال البيانات من 150 ثانية إلى 131 ثانية لمجموعة بيانات بحجم 5.6 GiB. وعند التبديل إلى ZSTD، انخفض حجم مجموعة البيانات نفسها إلى 1.69 GiB، لكن وقت المعالجة على جهة الخادم ارتفع قليلًا.
يقلّل الضغط من استخدام الموارد
الفرز المسبق إذا كانت تكلفته منخفضة
عمليات الإدراج غير المتزامنة
لا يُنصح بإرسال عدد كبير من الدفعات الصغيرة في الوضع المتزامن، لأن ذلك يؤدي إلى إنشاء عدد كبير من الأجزاء. وينتج عن ذلك ضعف في أداء الاستعلامات وظهور أخطاء “عدد كبير جدًا من الأجزاء”.
async_insert.

async_insert = 1)، تُخزَّن عمليات الإدراج مؤقتًا ولا تُكتب إلى القرص إلا عند تحقق أحد شروط التفريغ التالية:
- يصل المخزن المؤقت إلى حجم بيانات محدد (
async_insert_max_data_size، والقيمة الافتراضية 100 MiB). - انقضاء حد زمني (
async_insert_busy_timeout_ms، والقيمة الافتراضية 200 ms أو 1000 ms على Cloud). - تراكم عدد أقصى من استعلامات الإدراج (
async_insert_max_query_number، والقيمة الافتراضية 450).
اختيار وضع الإرجاع
wait_for_async_insert.
عند ضبطه على 1 (وهو الإعداد الافتراضي)، لا يؤكد ClickHouse عملية insert إلا بعد تفريغ البيانات إلى قرص بنجاح. ويضمن ذلك مستوىً قويًا من استمرارية البيانات، كما يجعل التعامل مع error مباشرًا: فإذا حدث خطأ أثناء الـ تفريغ، يُعاد هذا error إلى client. ويُنصح بهذا الوضع في معظم سيناريوهات production، خاصةً عندما يكون من الضروري تتبّع حالات فشل insert بشكل موثوق.
تُظهر المقارنات المعيارية أنه يتوسع جيدًا مع Concurrency، سواء كنت تشغّل 200 أو 500 client، وذلك بفضل inserts التكيفية والسلوك المستقر في إنشاء جزء.
يؤدي ضبط wait_for_async_insert = 0 إلى تفعيل وضع “fire-and-forget”. في هذا الوضع، يؤكد server عملية insert بمجرد تخزين البيانات في مخزن مؤقت، من دون انتظار وصولها إلى Storage.
يوفّر ذلك عمليات insert بزمن latency منخفض جدًا وأقصى throughput، ما يجعله مثاليًا للبيانات عالية التدفق ومنخفضة الأهمية. لكن لهذا الوضع مفاضلاته: فلا يوجد ضمان لاستمرار حفظ البيانات، ولا تظهر الأخطاء إلا أثناء تفريغ، كما لا توجد dead-letter queue لعمليات inserts الفاشلة — ولذلك يتطلب تتبّع حالات الفشل فحص سجلات الخادم وsystem tables بعد حدوثها. استخدم هذا الوضع فقط إذا كان workload لديك يمكنه تحمّل فقدان البيانات.
وتُظهر المقارنات المعيارية أيضًا انخفاضًا كبيرًا في عدد الأجزاء وتراجعًا في CPU usage عندما تكون عمليات تفريغ للـ مخزن مؤقت غير متكررة (مثلًا كل 30 Seconds)، لكن خطر الفشل الصامت يظل قائمًا.
نوصي بشدة باستخدام async_insert=1,wait_for_async_insert=1 عند استخدام عمليات الإدراج غير المتزامنة. فاستخدام wait_for_async_insert=0 ينطوي على مخاطر كبيرة جدًا، لأن INSERT client قد لا يكتشف وجود أخطاء، وقد يؤدي أيضًا إلى حمل زائد محتمل إذا واصل client الكتابة بسرعة في موقف يحتاج فيه ClickHouse server إلى إبطاء عمليات الكتابة وفرض بعض الضغط العكسي لضمان موثوقية service.
عمليات الإدراج غير المتزامنة التكيفية
async_insert_use_adaptive_busy_timeout). فبدلًا من فاصل زمني ثابت للتفريغ، تُضبط المهلة ديناميكيًا بين حد أدنى (async_insert_busy_timeout_min_ms، والقيمة الافتراضية 50 مللي ثانية) وحد أقصى (async_insert_busy_timeout_max_ms، والقيمة الافتراضية 200 مللي ثانية أو 1000 مللي ثانية على Cloud) استنادًا إلى معدل البيانات الواردة.
عندما تصل البيانات بوتيرة متكررة، تبقى المهلة أقرب إلى الحد الأدنى لتفريغ البيانات بشكل أسرع وتقليل زمن الانتقال من الطرف إلى الطرف. وعندما تكون البيانات متناثرة، ترتفع المهلة باتجاه الحد الأقصى لتجميع دفعات أكبر. ويكون ذلك مفيدًا بشكل خاص في الوضع الافتراضي (wait_for_async_insert=1)، إذ إن المهلة الثابتة المرتفعة ستجبر العميل على الانتظار طوال الفاصل الزمني الكامل حتى عندما تكون البيانات جاهزة للتفريغ.
معالجة الأخطاء
wait_for_async_insert=1)، يُعاد الخطأ إلى العميل. أما في وضع fire-and-forget، فتُسجَّل الأخطاء في سجلات الخادم وفي جدول system.asynchronous_inserts.
ينشئ كل تفريغ جزءًا واحدًا على الأقل لكل قيمة مميزة لمفتاح partition في المخزن المؤقت. وحتى في الجداول التي لا تحتوي على مفتاح partition، يمكن لعملية تفريغ واحدة أن تُنتج عدة أجزاء إذا تجاوزت البيانات المخزنة مؤقتًا max_insert_block_size (الافتراضي نحو مليون صف).
على الرغم من استخدام async inserts، فقد تظل تواجه أخطاء “too many parts” إذا كان مفتاح partitioning ذا كاردينالية عالية.
إزالة التكرار والموثوقية
*ReplicatedMergeTree بهذا السجل افتراضيًا؛ أما جدول MergeTree العادي، فيتطلب ضبط non_replicated_deduplication_window على قيمة موجبة. يتحكم الإعداد deduplicate_insert في كلا نوعَي عمليات الإدراج، وقيمته الافتراضية هي enable. كما تُدعَم طرق العرض المجسّدة التابعة.
عمليًا، إذا أُعيدت محاولة عملية الإدراج نفسها — بسبب مهلة أو انقطاع في الشبكة، مثلًا — يمكن لـ ClickHouse تجاهل العملية المكررة بأمان. يساعد ذلك على ضمان عدم التكرار وتجنب كتابة البيانات مرتين. بالنسبة إلى عمليات الإدراج غير المتزامنة، تعمل إزالة التكرار على مستوى استعلام المستخدم، لا على مستوى كل دفعة تم تفريغها؛ لذلك، لا يؤدي الاستعلام المكرر إلى تجاهل الاستعلامات الأخرى المجمّعة معه.
للتفاصيل، وللاطلاع على القيد الوحيد الذي ينطبق على طرق العرض المجسّدة مع عمليات الإدراج غير المتزامنة، راجع إزالة تكرار عمليات الإدراج عند إعادة المحاولة.
تمكين عمليات الإدراج غير المتزامنة
-
تمكين عمليات الإدراج غير المتزامنة على مستوى المستخدم. يستخدم هذا المثال المستخدم
default، فإذا أنشأت مستخدمًا آخر، فاستبدل اسم المستخدم بهذا الاسم: -
يمكنك تحديد إعدادات الإدراج غير المتزامن باستخدام عبارة SETTINGS في استعلامات
INSERT: -
يمكنك أيضًا تحديد إعدادات الإدراج غير المتزامن كمعلمات اتصال عند استخدام عميل ClickHouse لإحدى لغات البرمجة.
على سبيل المثال، إليك كيفية تنفيذ ذلك ضمن سلسلة اتصال JDBC عند استخدام برنامج تشغيل ClickHouse Java JDBC للاتصال بـ ClickHouse Cloud:
لا تنطبق عمليات الإدراج غير المتزامنة على استعلامات
INSERT INTO ... SELECT. وعندما يتضمن الإدراج عبارة SELECT، يُنفَّذ الاستعلام دائمًا بشكل متزامن بغض النظر عن الإعداد async_insert.تفريغ المخازن المؤقتة عند إيقاف التشغيل
async insert المؤقتة قيد الانتظار — على سبيل المثال، أثناء إيقاف تشغيلٍ منظّم أو قبل إجراء الصيانة — شغّل:
مقارنة مع جداول Buffer
- لا حاجة إلى أي تغييرات في DDL. تعمل عمليات الإدراج غير المتزامنة بشفافية — إذ تفعّل إعدادًا، ولا تنشئ جداول إضافية.
- التخزين المؤقت لكل بنية. تحتفظ عمليات الإدراج غير المتزامنة بمخازن مؤقتة منفصلة لكل بنية query فريدة ولكل مجموعة إعدادات، مما يتيح سياسات تفريغ دقيقة. أما جداول Buffer فتستخدم مخزنًا مؤقتًا واحدًا لكل table مستهدفة.
- الاستمرارية. في الوضع الافتراضي (
wait_for_async_insert=1)، يتم تأكيد البيانات على القرص قبل أن يتلقى العميل الإقرار. أما جداول Buffer فتعمل بأسلوب fire-and-forget — وتُفقد البيانات المخزنة مؤقتًا عند حدوث crash. - سلوك cluster. في clusters، تُدار مخازن عمليات الإدراج غير المتزامنة المؤقتة على مستوى كل node. وتتطلب جداول Buffer إنشاءها صراحةً على كل node.
اختر واجهة اتصال — HTTP أم الواجهة الأصلية
الأصلية
HTTP
Content-Encoding: lz4)، فإن الضغط يُطبَّق على payload بالكامل بدلًا من data blocks الفردية. وغالبًا ما تُفضَّل هذه الواجهة في البيئات التي تكون فيها بساطة البروتوكول، أو موازنة التحميل، أو التوافق الواسع مع التنسيقات، أهم من الأداء الخام.
للاطلاع على وصف أكثر تفصيلًا لهذه الواجهات، راجع هنا.