نوصي باستخدام دالة جدول Iceberg للعمل مع بيانات Iceberg في ClickHouse. يوفّر دالة جدول Iceberg حاليًا إمكانات كافية، مع تقديم واجهة جزئية للقراءة فقط لجداول Iceberg.يتوفر Iceberg محرك الجدول، لكنه قد يواجه بعض القيود. لم يُصمَّم ClickHouse في الأصل لدعم الجداول ذات المخططات التي تتغير خارجيًا، ما قد يؤثر في وظائف Iceberg محرك الجدول. ونتيجة لذلك، قد لا تتوفر بعض الميزات التي تعمل مع الجداول العادية، أو قد لا تعمل على نحو صحيح، خاصة عند استخدام المُحلِّل القديم.للحصول على أفضل توافق، نقترح استخدام دالة جدول Iceberg بينما نواصل تحسين دعم Iceberg محرك الجدول.
يوفّر هذا المحرك تكاملًا للقراءة فقط مع جداول Apache Iceberg الحالية في Amazon S3 وAzure وHDFS والجداول المخزّنة محليًا.
لاحظ أن جدول Iceberg يجب أن يكون موجودًا بالفعل في التخزين، فهذا الأمر لا يأخذ معاملات DDL لإنشاء جدول جديد.
CREATE TABLE iceberg_table_s3
ENGINE = IcebergS3(url, [, NOSIGN | access_key_id, secret_access_key, [session_token]], format, [,compression], [,extra_credentials])
CREATE TABLE iceberg_table_azure
ENGINE = IcebergAzure(connection_string|storage_account_url, container_name, blobpath, [account_name, account_key, format, compression])
CREATE TABLE iceberg_table_hdfs
ENGINE = IcebergHDFS(path_to_table, [,format] [,compression_method])
CREATE TABLE iceberg_table_local
ENGINE = IcebergLocal(path_to_table, [,format] [,compression_method])
وصف هذه الوسيطات مطابق لوصف الوسيطات في المحرّكات S3 وAzureBlobStorage وHDFS وFile، على الترتيب.
يشير format إلى تنسيق ملفات البيانات في جدول Iceberg.
بالنسبة إلى IcebergS3، يمكن استخدام المعامل الاختياري extra_credentials لتمرير role_arn من أجل الوصول المستند إلى الأدوار في ClickHouse Cloud. راجع تأمين S3 للاطلاع على خطوات الإعداد.
يمكن تحديد معاملات المحرك باستخدام المجموعات المُسمّاة
CREATE TABLE iceberg_table ENGINE=IcebergS3('http://test.s3.amazonaws.com/clickhouse-bucket/test_table', 'test', 'test')
باستخدام المجموعات المُسمّاة:
<clickhouse>
<named_collections>
<iceberg_conf>
<url>http://test.s3.amazonaws.com/clickhouse-bucket/</url>
<access_key_id>test</access_key_id>
<secret_access_key>test</secret_access_key>
</iceberg_conf>
</named_collections>
</clickhouse>
CREATE TABLE iceberg_table ENGINE=IcebergS3(iceberg_conf, filename = 'test_table')
يكتشف محرك الجدول Iceberg خلفية التخزين تلقائيًا من إعداد disk، ثم يوجّه إلى IcebergS3 أو IcebergAzure أو IcebergLocal وفقًا لذلك. وعند عدم تحديد disk، يستخدم تنفيذ IcebergS3 افتراضيًا.
يوضح الجدول التالي كيفية مطابقة أنواع بيانات Iceberg مع أنواع بيانات ClickHouse أثناء استنتاج المخطط (لأغراض القراءة).
| نوع Iceberg | نوع ClickHouse | ملاحظات |
|---|
boolean | Bool | |
int | Int32 | |
long, bigint | Int64 | |
float | Float32 | |
double | Float64 | |
date | Date32 | |
time | Int64 | ميكروثانية منذ منتصف الليل |
timestamp | DateTime64(6) | ميكروثانية، من دون منطقة زمنية |
timestamptz | DateTime64(6, 'UTC') | ميكروثانية، بتوقيت UTC |
timestamp_ns | DateTime64(9) | نانوثانية، من دون منطقة زمنية (بدءًا من Iceberg v3 فقط) |
timestamptz_ns | DateTime64(9, 'UTC') | نانوثانية، بتوقيت UTC (بدءًا من Iceberg v3 فقط) |
string, binary | String | |
uuid | UUID | |
fixed(N) | FixedString(N) | |
decimal(P, S) | Decimal(P, S) | |
| نوع Iceberg | نوع ClickHouse |
|---|
list | Array |
map | Map |
struct | Tuple |
يدعم ClickHouse قراءة جداول Iceberg التي تطور مخططها بمرور الوقت. ويشمل ذلك الجداول التي أُضيفت إليها أعمدة أو أُزيلت منها أو أُعيد ترتيبها، وكذلك الأعمدة التي تغيّرت من required إلى Nullable. بالإضافة إلى ذلك، تكون تحويلات الأنواع التالية مدعومة:
- int -> long
- float -> double
- decimal(P, S) -> decimal(P’, S) where P’ > P.
حاليًا، لا يمكن تغيير البُنى المتداخلة أو أنواع العناصر داخل Array وMap.
لقراءة جدول تغيّر مخططه بعد إنشائه باستخدام الاستدلال الديناميكي على المخطط، اضبط allow_dynamic_metadata_for_data_lakes = true عند إنشاء الجدول.
يدعم ClickHouse تشذيب الأقسام أثناء استعلامات SELECT على جداول Iceberg، مما يساعد على تحسين أداء الاستعلام من خلال تخطي ملفات البيانات غير ذات الصلة. لتمكين تشذيب الأقسام، اضبط use_iceberg_partition_pruning = 1. لمزيد من المعلومات حول تشذيب أقسام Iceberg، راجع https://iceberg.apache.org/spec/#partitioning
يدعم ClickHouse ميزة السفر عبر الزمن في جداول Iceberg، ما يتيح لك الاستعلام عن البيانات التاريخية باستخدام طابع زمني محدد أو معرّف لقطة.
معالجة الجداول ذات الصفوف المحذوفة
يدعم ClickHouse قراءة جداول Iceberg التي تستخدم طرق الحذف التالية:
طريقة الحذف التالية غير مدعومة:
SELECT * FROM example_table ORDER BY 1
SETTINGS iceberg_timestamp_ms = 1714636800000
SELECT * FROM example_table ORDER BY 1
SETTINGS iceberg_snapshot_id = 3547395809148285433
ملاحظة: لا يمكنك تحديد كلٍ من iceberg_timestamp_ms وiceberg_snapshot_id كمعاملَين في الاستعلام نفسه.
-
تُنشأ اللقطات عادةً في الحالات التالية:
- عند كتابة بيانات جديدة إلى الجدول
- عند إجراء نوع من دمج البيانات
-
لا تؤدي تغييرات المخطط عادةً إلى إنشاء لقطات - وهذا يترتب عليه سلوكيات مهمة عند استخدام السفر عبر الزمن مع الجداول التي خضعت لتطور المخطط.
جميع هذه السيناريوهات مكتوبة باستخدام Spark لأن CH لا يدعم الكتابة إلى جداول Iceberg حتى الآن.
السيناريو 1: تغييرات المخطط دون لقطات جديدة
ضع في اعتبارك تسلسل العمليات التالي:
-- Create a table with two columns
CREATE TABLE IF NOT EXISTS spark_catalog.db.time_travel_example (
order_number int,
product_code string
)
USING iceberg
OPTIONS ('format-version'='2')
-- Insert data into the table
INSERT INTO spark_catalog.db.time_travel_example VALUES
(1, 'Mars')
ts1 = now() // A piece of pseudo code
-- Alter table to add a new column
ALTER TABLE spark_catalog.db.time_travel_example ADD COLUMN (price double)
ts2 = now()
-- Insert data into the table
INSERT INTO spark_catalog.db.time_travel_example VALUES (2, 'Venus', 100)
ts3 = now()
-- Query the table at each timestamp
SELECT * FROM spark_catalog.db.time_travel_example TIMESTAMP AS OF ts1;
+------------+------------+
|order_number|product_code|
+------------+------------+
| 1| Mars|
+------------+------------+
SELECT * FROM spark_catalog.db.time_travel_example TIMESTAMP AS OF ts2;
+------------+------------+
|order_number|product_code|
+------------+------------+
| 1| Mars|
+------------+------------+
SELECT * FROM spark_catalog.db.time_travel_example TIMESTAMP AS OF ts3;
+------------+------------+-----+
|order_number|product_code|price|
+------------+------------+-----+
| 1| Mars| NULL|
| 2| Venus|100.0|
+------------+------------+-----+
نتائج الاستعلام عند طوابع زمنية مختلفة:
- عند ts1 وts2: لا يظهر سوى العمودين الأصليين
- عند ts3: تظهر الأعمدة الثلاثة كلها، وتكون قيمة السعر في الصف الأول NULL
السيناريو 2: الاختلافات بين المخطط التاريخي والمخطط الحالي
قد يُظهر استعلام السفر عبر الزمن في اللحظة الحالية مخططًا يختلف عن مخطط الجدول الحالي:
-- Create a table
CREATE TABLE IF NOT EXISTS spark_catalog.db.time_travel_example_2 (
order_number int,
product_code string
)
USING iceberg
OPTIONS ('format-version'='2')
-- Insert initial data into the table
INSERT INTO spark_catalog.db.time_travel_example_2 VALUES (2, 'Venus');
-- Alter table to add a new column
ALTER TABLE spark_catalog.db.time_travel_example_2 ADD COLUMN (price double);
ts = now();
-- Query the table at a current moment but using timestamp syntax
SELECT * FROM spark_catalog.db.time_travel_example_2 TIMESTAMP AS OF ts;
+------------+------------+
|order_number|product_code|
+------------+------------+
| 2| Venus|
+------------+------------+
-- Query the table at a current moment
SELECT * FROM spark_catalog.db.time_travel_example_2;
+------------+------------+-----+
|order_number|product_code|price|
+------------+------------+-----+
| 2| Venus| NULL|
+------------+------------+-----+
يحدث هذا لأن ALTER TABLE لا يُنشئ لقطة جديدة، لكن بالنسبة إلى الجدول الحالي، يأخذ Spark قيمة schema_id من أحدث ملف metadata، وليس من لقطة.
السيناريو 3: اختلافات المخطط التاريخي مقارنةً بالحالي
أما الحالة الثانية فهي أنه عند استخدام السفر عبر الزمن، لا يمكنك الوصول إلى حالة الجدول قبل كتابة أي بيانات فيه:
-- Create a table
CREATE TABLE IF NOT EXISTS spark_catalog.db.time_travel_example_3 (
order_number int,
product_code string
)
USING iceberg
OPTIONS ('format-version'='2');
ts = now();
-- Query the table at a specific timestamp
SELECT * FROM spark_catalog.db.time_travel_example_3 TIMESTAMP AS OF ts; -- Finises with error: Cannot find a snapshot older than ts.
في ClickHouse، يكون السلوك متوافقًا مع Spark. يمكنك ببساطة اعتبار استعلامات Select في Spark بمثابة استعلامات Select في ClickHouse، وسيعمل الأمر بالطريقة نفسها.
عند استخدام محرك الجدول Iceberg في ClickHouse، يحتاج النظام إلى تحديد موقع ملف metadata.json الصحيح الذي يصف بنية جدول Iceberg. وتعمل عملية التحديد هذه على النحو التالي:
- تحديد المسار مباشرةً:
- إذا قمت بتعيين
iceberg_metadata_file_path، فسيستخدم النظام هذا المسار كما هو بدمجه مع مسار دليل جدول Iceberg.
- عند توفير هذا الإعداد، يتم تجاهل جميع إعدادات الاستدلال الأخرى.
- مطابقة UUID الجدول:
- إذا تم تحديد
iceberg_metadata_table_uuid، فسيقوم النظام بما يلي:
- النظر فقط في ملفات
.metadata.json داخل دليل metadata
- تصفية الملفات التي تحتوي على حقل
table-uuid يطابق UUID الذي حددته (غير حساس لحالة الأحرف)
- البحث الافتراضي:
- إذا لم يتم توفير أيٍّ من الإعدادين أعلاه، فستصبح جميع ملفات
.metadata.json داخل دليل metadata ملفات مرشحة
بعد تحديد الملفات المرشحة وفقًا للقواعد المذكورة أعلاه، يحدّد النظام الملف الأحدث بينها:
-
إذا كان
iceberg_recent_metadata_file_by_last_updated_ms_field مفعّلًا:
- يُختار الملف الذي يحمل أكبر قيمة لـ
last-updated-ms
-
بخلاف ذلك:
- يُختار الملف ذو أعلى رقم إصدار
- (يظهر الإصدار على شكل
V في أسماء الملفات المنسّقة بالشكل V.metadata.json أو V-uuid.metadata.json)
ملاحظة: جميع الإعدادات المذكورة (ما لم يُنصّ صراحةً على خلاف ذلك) هي إعدادات على مستوى المحرّك، ويجب تحديدها أثناء إنشاء الجدول كما هو موضّح أدناه:
CREATE TABLE example_table ENGINE = Iceberg(
's3://bucket/path/to/iceberg_table'
) SETTINGS iceberg_metadata_table_uuid = '6f6f6407-c6a5-465f-a808-ea8900e35a38';
ملاحظة: بينما تتولى كتالوجات Iceberg عادةً تحديد البيانات الوصفية، فإن محرك الجداول Iceberg في ClickHouse يفسّر مباشرةً الملفات المخزّنة في S3 باعتبارها جداول Iceberg، لذلك من المهم فهم قواعد التحديد هذه.
ذاكرة التخزين المؤقت للبيانات
يدعم محرك الجدول Iceberg ودالة الجدول التخزين المؤقت للبيانات، تمامًا مثل أنظمة التخزين S3 وAzureBlobStorage وHDFS. راجع هنا.
يدعم كلٌّ من محرك الجدول ودالة الجدول Iceberg ذاكرةً مؤقتة للبيانات الوصفية تخزّن معلومات ملفات البيان وقائمة البيان وملف JSON للبيانات الوصفية. تُخزَّن هذه الذاكرة المؤقتة في الذاكرة. ويجري التحكّم في هذه الميزة عبر الإعداد use_iceberg_metadata_files_cache، وهو مفعّل افتراضيًا.
يمكن تمكين الجلب المسبق غير المتزامن للبيانات الوصفية عند إنشاء جدول Iceberg من خلال ضبط iceberg_metadata_async_prefetch_period_ms. إذا ضُبطت هذه القيمة على 0 (الافتراضي)، أو إذا لم تكن ذاكرة التخزين المؤقت للبيانات الوصفية مُمكّنة، فسيُعطَّل الجلب المسبق غير المتزامن.
ولتمكين هذه الميزة، يجب تحديد قيمة غير صفرية بالميلي ثانية. وتمثل هذه القيمة الفاصل الزمني بين دورات الجلب المسبق.
إذا كانت هذه الميزة مُمكّنة، فسيشغّل الخادم عملية دورية في الخلفية لعرض الكتالوج البعيد واكتشاف إصدار جديد من البيانات الوصفية. ثم سيُحلّلها ويجتاز اللقطة بشكل تكراري، جالبًا ملفات manifest list النشطة وملفات manifest.
أما الملفات المتوفرة بالفعل في ذاكرة التخزين المؤقت للبيانات الوصفية، فلن يُعاد تنزيلها. وفي نهاية كل دورة جلب مسبق، تصبح أحدث لقطة للبيانات الوصفية متاحة في ذاكرة التخزين المؤقت للبيانات الوصفية.
CREATE TABLE example_table ENGINE = Iceberg(
's3://bucket/path/to/iceberg_table'
) SETTINGS
iceberg_metadata_async_prefetch_period_ms = 60000;
للاستفادة إلى أقصى حد من الجلب المسبق غير المتزامن للبيانات الوصفية في عمليات القراءة، يجب تحديد المعلمة iceberg_metadata_staleness_ms كمعلمة على مستوى الاستعلام أو الجلسة. افتراضيًا (0 - غير محددة)، وفي سياق كل استعلام، سيجلب الخادم أحدث البيانات الوصفية من الكتالوج البعيد.
ومن خلال تحديد مقدار التحمّل لتقادم البيانات الوصفية، يُسمح للخادم باستخدام النسخة المخزنة مؤقتًا من لقطة البيانات الوصفية من دون استدعاء الكتالوج البعيد. إذا توفرت نسخة من البيانات الوصفية في الذاكرة المؤقتة، وكانت قد نُزِّلت ضمن نافذة التقادم المحددة، فستُستخدم لمعالجة الاستعلام.
وإلا فسيتم جلب أحدث نسخة من الكتالوج البعيد.
SELECT count() FROM icebench_table WHERE ...
SETTINGS iceberg_metadata_staleness_ms=120000
ملاحظة: يعمل الجلب المسبق غير المتزامن للبيانات الوصفية ضمن ICEBERG_SCEDULE_POOL، وهي مجموعة مؤشرات ترابط على مستوى الخادم لعمليات الخلفية على جداول Iceberg النشطة. ويُتحكَّم في حجم مجموعة مؤشرات الترابط هذه بواسطة معلمة إعداد الخادم iceberg_background_schedule_pool_size (القيمة الافتراضية هي 10).
ملاحظة: التوقع الحالي هو أن يكون حجم ذاكرة التخزين المؤقت للبيانات الوصفية كافيًا للاحتفاظ بالكامل بأحدث لقطة للبيانات الوصفية لجميع الجداول النشطة، إذا كان الجلب المسبق غير المتزامن مُمكّنًا.
آخر تعديل في ٢٩ يونيو ٢٠٢٦