الانتقال إلى المحتوى الرئيسي

المشكلة

لا يدعم ClickHouse Cloud المعاملات متعددة العبارات بالمعنى التقليدي المتعارف عليه في أنظمة RDBMS. ويؤدي ذلك إلى تحديين شائعين:
  1. الذرّية على مستوى جدول واحد في التحميلات المجمّعة: يتمثل أحد الأساليب الشائعة في الإدراج في مفاتيح جزئية مؤقتة، ثم نسخ السجلات إلى المفتاح الفعلي وحذف السجلات المؤقتة. لكن هذا الأسلوب ضعيف الأداء — ولا سيما خطوة الحذف، التي قد تستهلك أكثر من 90% من إجمالي وقت العملية.
  2. الاتساق عبر جداول متعددة: عندما يحمّل خط المعالجة Table A بنجاح لكنه يفشل في Table B، تكون تغييرات Table A قد اعتُمِدت بالفعل ولا يمكن التراجع عنها. وعند الاستعلام عبر الجدولين، يرى المحللون بيانات غير متزامنة.

الخلفية

يضمن ClickHouse الخاصية الذرية على مستوى عملية INSERT واحدة وقسم واحد: فإذا نجحت عملية INSERT، تصبح جميع الصفوف في تلك الكتلة مرئية؛ وإذا فشلت، فلا يصبح أيٌّ منها مرئيًا. ومع ذلك، لا توجد آلية مضمّنة لاعتماد البيانات ذريًا عبر عدة عمليات إدراج أو عدة جداول. تعمل أوامر معالجة الأقسام (MOVE PARTITION TO TABLE، REPLACE PARTITION، ATTACH PARTITION FROM) على مستوى البيانات الوصفية عندما يشترك الجدولان المصدر والوجهة في سياسة التخزين نفسها. وهذا يعني أنها تُنفَّذ بشكل شبه فوري بغض النظر عن حجم البيانات، مما يجعلها لبنات بناء مثالية لأنماط التبديل الذري. بدلًا من الإدراج مباشرةً في جداول الإنتاج ومحاولة المعالجة عند الإخفاق، استخدم جداول مرحلية مخصصة كمنطقة استقبال. بعد التحقق من صحة البيانات، استخدم عمليات على مستوى الأقسام لنقل البيانات إلى الإنتاج نقلاً ذريًا.

شرح خطوة بخطوة لتحقيق الذرية لجدول واحد

1

أنشئ جدولًا مرحليًا

استخدم المخطط نفسه، ومفتاح التقسيم، وORDER BY، وسياسة التخزين نفسها كما في جدول الإنتاج.
CREATE TABLE my_table_staging AS my_table_prod;
2

أدرِج البيانات في الجدول المرحلي

نفّذ عملية insert على الجدول المرحلي بدلًا من جدول الإنتاج.
INSERT INTO my_table_staging SELECT ... FROM source;
3

إذا فشل الإدراج، فنفّذ TRUNCATE وأعد المحاولة

نفّذ TRUNCATE للجدول المرحلي ثم أعد تشغيل التحميل. لن تتأثر أي بيانات في الإنتاج.
TRUNCATE TABLE my_table_staging;
4

إذا نجح الإدراج، فانقل الأقسام إلى الإنتاج

لنقل البيانات إلى الإنتاج وإزالتها من الجدول المرحلي، استخدم MOVE PARTITION.
ALTER TABLE my_table_staging MOVE PARTITION <partition_expr> TO TABLE my_table_prod;
بدلًا من ذلك، انسخ البيانات إلى قسم موجود في الإنتاج باستخدام ATTACH PARTITION.
ALTER TABLE my_table_prod ATTACH PARTITION tuple() FROM my_table_staging;
كلتا العمليتين هما تغييران على مستوى البيانات الوصفية ضمن سياسة التخزين نفسها، ويكتملان بشكل شبه فوري.
5

نظّف الجدول المرحلي

بعد نقل جميع الأقسام، نفّذ TRUNCATE للجدول المرحلي ليبقى فارغًا لعملية التحميل التالية.
TRUNCATE TABLE my_table_staging;

الاتساق عبر جداول متعددة

يعالج النهج نفسه مسارات المعالجة التي تتطلب تحميل جدولين أو أكثر بالكامل قبل أن يصبح أيٌّ منها مرئيًا للمحللين. حمِّل بيانات كل جدول إلى جدول مرحلي خاص به، وتحقّق منها جميعًا، ثم نفِّذ عمليات نقل الأقسام معًا. ولأن كل عملية نقل هي عملية بيانات وصفية شبه فورية، فإن الفترة التي تكون فيها الجداول غير متسقة تتقلص من مدة التحميل الكامل إلى الوقت اللازم لتبديل الأقسام.

المتطلبات والقيود

بالنسبة إلى MOVE PARTITION TO TABLE وREPLACE PARTITION وATTACH PARTITION FROM، يجب أن يتطابق جدولَا المصدر والوجهة في ما يلي:
  • بنية الأعمدة نفسها
  • مفتاح قسم نفسه، ومفتاح ORDER BY نفسه، والمفتاح الأساسي نفسه
  • سياسة التخزين نفسها
  • يجب أن يتضمن جدول الوجهة جميع الفهارس والإسقاطات الموجودة في جدول المصدر

مثال

يمكنك تجربة المثال أدناه بشكل تفاعلي باستخدام fiddle:
CREATE TABLE prod
(
  uid Int16,
  name String,
  age Int16
)
ENGINE=MergeTree
ORDER BY ();

CREATE TABLE staging
(
  uid Int16,
  name String,
  age Int16
)
ENGINE=MergeTree
ORDER BY ();

-- Initial data
INSERT INTO prod VALUES (123, 'John', 33);
INSERT INTO prod VALUES (456, 'Ksenia', 48);
-- Load data
INSERT INTO staging VALUES (8811, 'Alice', 50);
INSERT INTO staging VALUES (8812, 'Bob', 23);

-- Validate import
SELECT 'Staging count:', COUNT() FROM staging;
-- Move partition
ALTER TABLE staging MOVE PARTITION tuple() TO TABLE prod; -- atomic op

-- Check data
SELECT 'Prod count:', COUNT() FROM prod;
SELECT * FROM prod;

المراجع

آخر تعديل في ٢٥ يونيو ٢٠٢٦