الانتقال إلى المحتوى الرئيسي
توفر عمليات الإدراج غير المتزامنة في ClickHouse بديلاً فعّالاً عندما لا يكون التجميع على جانب العميل ممكنًا. وتبرز أهمية ذلك خصوصًا في أحمال عمل observability، حيث يرسل مئات أو آلاف الوكلاء البيانات باستمرار—سجلات ومقاييس وآثار تتبع—وغالبًا ضمن حمولات صغيرة ولحظية. ويؤدي تخزين البيانات مؤقتًا على جانب العميل في هذه البيئات إلى زيادة التعقيد، إذ يتطلب وجود queue مركزية لضمان إرسال دفعات كبيرة بما يكفي.
لا يُنصح بإرسال عدد كبير من الدفعات الصغيرة في الوضع المتزامن، لأن ذلك يؤدي إلى إنشاء عدد كبير من الأجزاء. وينتج عن ذلك ضعف في أداء الاستعلامات وظهور أخطاء “عدد كبير جدًا من الأجزاء”.
تنقل عمليات الإدراج غير المتزامنة مسؤولية التجميع من العميل إلى الخادم، وذلك بكتابة البيانات الواردة إلى مخزن مؤقت في الذاكرة ثم تفريغها إلى التخزين وفقًا لحدود قابلة للتهيئة. ويقلل هذا النهج بدرجة كبيرة من الكلفة الإضافية الناتجة عن إنشاء الأجزاء، ويخفض استخدام CPU، ويضمن بقاء ingestion فعّالة حتى عند ارتفاع مستويات التزامن. يُتحكَّم في السلوك الأساسي عبر الإعداد async_insert. تدعم عمليات الإدراج غير المتزامنة كلًّا من واجهتَي HTTP وnative TCP. عند التفعيل (async_insert = 1)، تُخزَّن عمليات الإدراج مؤقتًا ولا تُكتب إلى القرص إلا عند تحقق أحد شروط التفريغ التالية: وأي حد يتم بلوغه أولًا يؤدي إلى تشغيل عملية التفريغ. تكون عملية التجميع هذه غير مرئية للعملاء، وتساعد ClickHouse على دمج حركة insert بكفاءة من مصادر متعددة. ومع ذلك، وحتى حدوث التفريغ، لا يمكن الاستعلام عن البيانات. ومن المهم ملاحظة وجود عدة مخازن مؤقتة لكل تركيبة من شكل insert والإعدادات، وفي clusters، تُحفَظ هذه المخازن لكل عقدة على حدة—مما يتيح تحكمًا دقيقًا في البيئات متعددة المستأجرين. وبخلاف ذلك، تبقى آليات insert مطابقة لما هو موصوف في عمليات الإدراج المتزامنة.

اختيار وضع الإرجاع

يمكن ضبط سلوك عمليات الإدراج غير المتزامنة بمزيد من الدقة باستخدام الإعداد wait_for_async_insert. عند ضبطه على 1 (وهو الإعداد الافتراضي)، لا يؤكد ClickHouse عملية insert إلا بعد تفريغ البيانات إلى disk بنجاح. ويضمن ذلك مستوىً قويًا من استمرارية البيانات، كما يجعل التعامل مع 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 الفاشلة — ولذلك يتطلب تتبّع حالات الفشل فحص server logs و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.

عمليات الإدراج غير المتزامنة التكيفية

اعتبارًا من الإصدار 24.2، يستخدم ClickHouse افتراضيًا مهلات تفريغ تكيفية (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)، إذ إن المهلة الثابتة المرتفعة ستجبر العميل على الانتظار طوال الفاصل الزمني الكامل حتى عندما تكون البيانات جاهزة للتفريغ.

معالجة الأخطاء

يحدث التحقق من المخطط وتحليل البيانات أثناء تفريغ المخزن المؤقت، وليس عند استلام عملية insert. إذا احتوى أي صف في استعلام insert على خطأ في التحليل أو في النوع، فلن يُفرَّغ أيٌّ من بيانات ذلك الاستعلام — بل تُرفَض حمولة الاستعلام بالكامل. في الوضع الافتراضي (wait_for_async_insert=1)، يُعاد الخطأ إلى العميل. أما في وضع fire-and-forget، فتُسجَّل الأخطاء في سجلات الخادم وفي جدول system.asynchronous_inserts. ينشئ كل تفريغ جزءًا واحدًا على الأقل لكل قيمة مميزة لمفتاح partition في المخزن المؤقت. وحتى في الجداول التي لا تحتوي على مفتاح partition، يمكن لعملية تفريغ واحدة أن تُنتج عدة أجزاء إذا تجاوزت البيانات المخزنة مؤقتًا max_insert_block_size (الافتراضي نحو مليون صف).
على الرغم من استخدام async inserts، فقد تظل تواجه أخطاء “too many parts” إذا كان مفتاح partitioning ذا كاردينالية عالية.

إزالة التكرار والموثوقية

بشكل افتراضي، ينفّذ ClickHouse إزالة التكرار تلقائيًا لعمليات الإدراج المتزامنة، مما يجعل إعادة المحاولة آمنة في حالات الفشل. ومع ذلك، تكون هذه الميزة معطّلة لعمليات الإدراج غير المتزامنة ما لم يتم تمكينها صراحةً (ويجب عدم تمكينها إذا كانت لديك عروض مادية تابعة — راجع المشكلة). عمليًا، إذا كانت إزالة التكرار مفعّلة وأُعيدت محاولة عملية الإدراج نفسها — بسبب، على سبيل المثال، انتهاء المهلة أو انقطاع الشبكة — يمكن لـ ClickHouse تجاهل التكرار بأمان. يساعد ذلك في الحفاظ على خاصية idempotency وتجنّب كتابة البيانات مرتين.

تمكين عمليات الإدراج غير المتزامنة

يمكن تمكين عمليات الإدراج غير المتزامنة لمستخدم معيّن أو لاستعلام محدد:
  • تمكين عمليات الإدراج غير المتزامنة على مستوى المستخدم. يستخدم هذا المثال المستخدم default، فإذا أنشأت مستخدمًا آخر، فاستبدل اسم المستخدم بهذا الاسم:
    ALTER USER default SETTINGS async_insert = 1
    
  • يمكنك تحديد إعدادات الإدراج غير المتزامن باستخدام عبارة SETTINGS في استعلامات INSERT:
    INSERT INTO YourTable SETTINGS async_insert=1, wait_for_async_insert=1 VALUES (...)
    
  • يمكنك أيضًا تحديد إعدادات الإدراج غير المتزامن كمعلمات اتصال عند استخدام عميل ClickHouse لإحدى لغات البرمجة. على سبيل المثال، إليك كيفية تنفيذ ذلك ضمن سلسلة اتصال JDBC عند استخدام برنامج تشغيل ClickHouse Java JDBC للاتصال بـ ClickHouse Cloud:
    "jdbc:ch://HOST.clickhouse.cloud:8443/?user=default&password=PASSWORD&ssl=true&custom_http_params=async_insert=1,wait_for_async_insert=1"
    
لا تنطبق عمليات الإدراج غير المتزامنة على استعلامات INSERT INTO ... SELECT. وعندما يتضمن الإدراج عبارة SELECT، يُنفَّذ الاستعلام دائمًا بشكل متزامن بغض النظر عن الإعداد async_insert.

تفريغ المخازن المؤقتة عند إيقاف التشغيل

لتفريغ جميع مخازن async insert المؤقتة قيد الانتظار — على سبيل المثال، أثناء إيقاف تشغيلٍ منظّم أو قبل إجراء الصيانة — شغّل:
SYSTEM FLUSH ASYNC INSERT QUEUE
يضمن ذلك كتابة أي بيانات موجودة في الذاكرة المؤقتة إلى التخزين قبل توقف الخادم.

مقارنة مع جداول Buffer

تُعد عمليات الإدراج غير المتزامنة البديل الحديث لـجداول Buffer. الاختلافات الرئيسية:
  • لا حاجة إلى أي تغييرات في DDL. تعمل عمليات الإدراج غير المتزامنة بشفافية — إذ تفعّل إعدادًا، ولا تنشئ جداول إضافية.
  • التخزين المؤقت لكل بنية. تحتفظ عمليات الإدراج غير المتزامنة بمخازن مؤقتة منفصلة لكل بنية query فريدة ولكل مجموعة إعدادات، مما يتيح سياسات تفريغ دقيقة. أما جداول Buffer فتستخدم مخزنًا مؤقتًا واحدًا لكل table مستهدفة.
  • الاستمرارية. في الوضع الافتراضي (wait_for_async_insert=1)، يتم تأكيد البيانات على القرص قبل أن يتلقى العميل الإقرار. أما جداول Buffer فتعمل بأسلوب fire-and-forget — وتُفقد البيانات المخزنة مؤقتًا عند حدوث crash.
  • سلوك cluster. في clusters، تُدار مخازن عمليات الإدراج غير المتزامنة المؤقتة على مستوى كل node. وتتطلب جداول Buffer إنشاءها صراحةً على كل node.
آخر تعديل في ٢٥ يونيو ٢٠٢٦