يوفّر واجهة شبيهة بالجداول للقراءة فقط لجداول Apache Iceberg في Amazon S3 أو Azure أو HDFS أو المخزّنة محليًا.
icebergS3(url [, NOSIGN | access_key_id, secret_access_key, [session_token]] [,format] [,compression_method] [,extra_credentials])
icebergS3(named_collection[, option=value [,..]])
icebergAzure(connection_string|storage_account_url, container_name, blobpath, [,account_name], [,account_key] [,format] [,compression_method])
icebergAzure(named_collection[, option=value [,..]])
icebergHDFS(path_to_table, [,format] [,compression_method])
icebergHDFS(named_collection[, option=value [,..]])
icebergLocal(path_to_table, [,format] [,compression_method])
icebergLocal(named_collection[, option=value [,..]])
يتطابق وصف المعاملات مع وصفها في دوال الجداول s3 وazureBlobStorage وHDFS وfile، على التوالي.
يشير format إلى تنسيق ملفات البيانات في جدول Iceberg.
بالنسبة إلى icebergS3، يمكن استخدام المعامل الاختياري extra_credentials لتمرير role_arn بغرض الوصول المستند إلى الدور في ClickHouse Cloud. راجع Secure S3 للاطلاع على خطوات الإعداد.
جدول ذو البنية المحددة لقراءة البيانات من جدول Iceberg المحدد.
SELECT * FROM icebergS3('http://test.s3.amazonaws.com/clickhouse-bucket/test_table', 'test', 'test')
يدعم ClickHouse حاليًا قراءة الإصدارين v1 وv2 من صيغة Iceberg عبر دوال الجداول icebergS3 وicebergAzure وicebergHDFS وicebergLocal، ومحركات الجداول IcebergS3 وicebergAzure وIcebergHDFS وIcebergLocal.
فيما يلي مثال على تهيئة مجموعة مسماة لتخزين URL وبيانات الاعتماد:
<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>
<format>auto</format>
<structure>auto</structure>
</iceberg_conf>
</named_collections>
</clickhouse>
SELECT * FROM icebergS3(iceberg_conf, filename = 'test_table')
DESCRIBE icebergS3(iceberg_conf, filename = 'test_table')
يمكن أيضًا استخدام جداول Iceberg مع كتالوجات بيانات متعددة، مثل REST Catalog، وAWS Glue Data Catalog، وUnity Catalog.
عند استخدام catalog، سيحتاج معظم المستخدمين إلى استخدام محرك قاعدة البيانات DataLakeCatalog، إذ يربط ClickHouse بالcatalog لاكتشاف الجداول. ويمكنك استخدام محرك قاعدة البيانات هذا بدلًا من إنشاء جداول منفصلة يدويًا باستخدام محرك الجدول IcebergS3.
لاستخدام ذلك، أنشئ جدولًا باستخدام المحرك IcebergS3 ووفّر الإعدادات اللازمة.
على سبيل المثال، عند استخدام REST Catalog مع وحدة التخزين MinIO:
CREATE TABLE `database_name.table_name`
ENGINE = IcebergS3(
'http://minio:9000/warehouse-rest/table_name/',
'minio_access_key',
'minio_secret_key'
)
أو باستخدام AWS Glue Data Catalog مع S3:
CREATE TABLE `my_database.my_table`
ENGINE = IcebergS3(
's3://my-data-bucket/warehouse/my_database/my_table/',
'aws_access_key',
'aws_secret_key'
)
في الوقت الحالي، وبمساعدة CH، يمكنك قراءة جداول Iceberg التي تغيّر مخططها بمرور الوقت. ونحن ندعم حاليًا قراءة الجداول التي أُضيفت إليها أعمدة وأُزيلت منها أعمدة، أو تغيّر ترتيب أعمدتها. ويمكنك أيضًا تحويل عمود تكون فيه القيمة إلزامية إلى عمود يُسمح فيه بالقيمة NULL. بالإضافة إلى ذلك، ندعم تحويل الأنواع المسموح به للأنواع البسيطة، وهي:
- int -> long
- float -> double
- decimal(P, S) -> decimal(P’, S) where P’ > P.
حاليًا، لا يمكن تغيير البُنى المتداخلة أو أنواع العناصر داخل Array وMap.
يدعم ClickHouse استبعاد الأقسام أثناء استعلامات SELECT على جداول Iceberg، مما يساعد على تحسين أداء الاستعلامات عبر تخطي ملفات البيانات غير ذات الصلة. لتمكين استبعاد الأقسام، اضبط use_iceberg_partition_pruning = 1. لمزيد من المعلومات حول استبعاد أقسام Iceberg، راجع https://iceberg.apache.org/spec/#partitioning
يدعم ClickHouse ميزة السفر عبر الزمن في جداول Iceberg، ما يتيح لك الاستعلام عن البيانات التاريخية استنادًا إلى طابع زمني محدد أو معرّف لقطة محدد.
معالجة الجداول ذات الصفوف المحذوفة
حاليًا، لا تُدعَم إلا جداول 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 bigint,
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 bigint,
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 لا يُنشئ snapshot جديدة، ولكن بالنسبة إلى الجدول الحالي، يأخذ Spark قيمة schema_id من أحدث ملف metadata، وليس من snapshot.
السيناريو 3: اختلافات المخطط التاريخي والحالي
والنقطة الثانية هي أنه عند استخدام السفر عبر الزمن، لا يمكنك الحصول على حالة الجدول قبل كتابة أي بيانات فيه:
-- Create a table
CREATE TABLE IF NOT EXISTS spark_catalog.db.time_travel_example_3 (
order_number bigint,
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 في ClickHouse بدلًا من استعلامات Select في Spark، وسيعمل الأمر بالطريقة نفسها.
عند استخدام الدالة الجدولية iceberg في ClickHouse، يحتاج النظام إلى تحديد ملف metadata.json الصحيح الذي يصف بنية جدول Iceberg. إليك كيف تتم عملية التحديد هذه:
البحث عن المرشحين (حسب ترتيب الأولوية)
- تحديد المسار مباشرةً:
*إذا عيّنت
iceberg_metadata_file_path، فسيستخدم النظام هذا المسار المحدد كما هو، من خلال دمجه مع مسار دليل جدول Iceberg.
- عند توفير هذا الإعداد، يتم تجاهل جميع إعدادات resolution الأخرى.
-
مطابقة 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)
ملاحظة: جميع الإعدادات المذكورة هي إعدادات الدالة الجدولية (وليست إعدادات عامة أو على مستوى الاستعلام) ويجب تحديدها كما هو موضح أدناه:
SELECT * FROM iceberg('s3://bucket/path/to/iceberg_table',
SETTINGS iceberg_metadata_table_uuid = 'a90eed4c-f74b-4e5b-b630-096fb9d09021');
ملاحظة: رغم أن كتالوجات Iceberg تتولى عادةً تحديد البيانات الوصفية، فإن دالة الجدول iceberg في ClickHouse تفسّر مباشرةً الملفات المخزّنة في S3 على أنها جداول Iceberg، ولذلك من المهم فهم قواعد التحديد هذه.
يدعم محرك الجدول Iceberg والدالة الجدولية ذاكرة تخزين مؤقت للبيانات الوصفية تُخزّن معلومات ملفات manifest وmanifest list وملف metadata json. تُخزَّن ذاكرة التخزين المؤقت هذه في الذاكرة. ويجري التحكم في هذه الميزة عبر الإعداد use_iceberg_metadata_files_cache، وهي مفعّلة افتراضيًا.
أصبحت الدالة الجدولية iceberg اسمًا بديلًا لـ icebergS3.
_path — مسار الملف. النوع: LowCardinality(String).
_file — اسم الملف. النوع: LowCardinality(String).
_size — حجم الملف بالبايت. النوع: Nullable(UInt64). إذا كان حجم الملف غير معروف، فستكون القيمة NULL.
_time — وقت آخر تعديل للملف. النوع: Nullable(DateTime). إذا كان الوقت غير معروف، فستكون القيمة NULL.
_etag — قيمة etag للملف. النوع: LowCardinality(String). إذا كانت قيمة etag غير معروفة، فستكون القيمة NULL.
بدءًا من الإصدار 25.7، يدعم ClickHouse تعديل جداول Iceberg الخاصة بالمستخدم.
حاليًا، هذه ميزة تجريبية، لذا عليك أولًا تمكينها:
SET allow_insert_into_iceberg = 1;
لإنشاء جدول Iceberg فارغ خاص بك، استخدم الأوامر نفسها المستخدَمة للقراءة، ولكن حدِّد المخطط صراحةً.
تدعم عمليات الكتابة جميع تنسيقات البيانات وفقًا لمواصفة Iceberg، مثل Parquet وAvro وORC.
CREATE TABLE iceberg_writes_example
(
x Nullable(String),
y Nullable(Int32)
)
ENGINE = IcebergLocal('/home/scanhex12/iceberg_example/')
ملاحظة: لإنشاء ملف تلميح الإصدار، فعِّل الإعداد iceberg_use_version_hint.
إذا أردت ضغط ملف metadata.json، فحدِّد اسم خوارزمية الضغط في الإعداد iceberg_metadata_compression_method.
بعد إنشاء جدول جديد، يمكنك إدراج البيانات باستخدام صيغة ClickHouse المعتادة.
INSERT INTO iceberg_writes_example VALUES ('Pavel', 777), ('Ivanov', 993);
SELECT *
FROM iceberg_writes_example
FORMAT VERTICAL;
Row 1:
──────
x: Pavel
y: 777
Row 2:
──────
x: Ivanov
y: 993
يدعم ClickHouse أيضًا حذف الصفوف الإضافية في تنسيق merge-on-read.
سينشئ هذا الاستعلام لقطة جديدة تتضمّن ملفات حذف حسب الموضع.
ALTER TABLE iceberg_writes_example DELETE WHERE x != 'Ivanov';
SELECT *
FROM iceberg_writes_example
FORMAT VERTICAL;
Row 1:
──────
x: Ivanov
y: 993
يتيح ClickHouse إضافة الأعمدة ذات الأنواع البسيطة (غير tuple وغير array وغير map) أو حذفها أو تعديلها أو إعادة تسميتها.
ALTER TABLE iceberg_writes_example MODIFY COLUMN y Nullable(Int64);
SHOW CREATE TABLE iceberg_writes_example;
┌─statement─────────────────────────────────────────────────┐
1. │ CREATE TABLE default.iceberg_writes_example ↴│
│↳( ↴│
│↳ `x` Nullable(String), ↴│
│↳ `y` Nullable(Int64) ↴│
│↳) ↴│
│↳ENGINE = IcebergLocal('/home/scanhex12/iceberg_example/') │
└───────────────────────────────────────────────────────────┘
ALTER TABLE iceberg_writes_example ADD COLUMN z Nullable(Int32);
SHOW CREATE TABLE iceberg_writes_example;
┌─statement─────────────────────────────────────────────────┐
1. │ CREATE TABLE default.iceberg_writes_example ↴│
│↳( ↴│
│↳ `x` Nullable(String), ↴│
│↳ `y` Nullable(Int64), ↴│
│↳ `z` Nullable(Int32) ↴│
│↳) ↴│
│↳ENGINE = IcebergLocal('/home/scanhex12/iceberg_example/') │
└───────────────────────────────────────────────────────────┘
SELECT *
FROM iceberg_writes_example
FORMAT VERTICAL;
Row 1:
──────
x: Ivanov
y: 993
z: ᴺᵁᴸᴸ
ALTER TABLE iceberg_writes_example DROP COLUMN z;
SHOW CREATE TABLE iceberg_writes_example;
┌─statement─────────────────────────────────────────────────┐
1. │ CREATE TABLE default.iceberg_writes_example ↴│
│↳( ↴│
│↳ `x` Nullable(String), ↴│
│↳ `y` Nullable(Int64) ↴│
│↳) ↴│
│↳ENGINE = IcebergLocal('/home/scanhex12/iceberg_example/') │
└───────────────────────────────────────────────────────────┘
SELECT *
FROM iceberg_writes_example
FORMAT VERTICAL;
Row 1:
──────
x: Ivanov
y: 993
ALTER TABLE iceberg_writes_example RENAME COLUMN y TO value;
SHOW CREATE TABLE iceberg_writes_example;
┌─statement─────────────────────────────────────────────────┐
1. │ CREATE TABLE default.iceberg_writes_example ↴│
│↳( ↴│
│↳ `x` Nullable(String), ↴│
│↳ `value` Nullable(Int64) ↴│
│↳) ↴│
│↳ENGINE = IcebergLocal('/home/scanhex12/iceberg_example/') │
└───────────────────────────────────────────────────────────┘
SELECT *
FROM iceberg_writes_example
FORMAT VERTICAL;
Row 1:
──────
x: Ivanov
value: 993
يدعم ClickHouse دمج جداول Iceberg. حاليًا، يمكنه دمج ملفات الحذف حسب الموضع في ملفات البيانات مع تحديث البيانات الوصفية. وتبقى معرّفات اللقطات السابقة والطوابع الزمنية من دون تغيير، لذا يظل بالإمكان استخدام ميزة السفر عبر الزمن بالمعرّفات والقيم الزمنية نفسها.
كيفية استخدامه:
SET allow_experimental_iceberg_compaction = 1
OPTIMIZE TABLE iceberg_writes_example;
SELECT *
FROM iceberg_writes_example
FORMAT VERTICAL;
Row 1:
──────
x: Ivanov
y: 993
حذف اللقطات منتهية الصلاحية
تتراكم اللقطات في جداول Iceberg مع كل عملية INSERT أو DELETE أو UPDATE. ومع مرور الوقت، قد يؤدي ذلك إلى زيادة كبيرة في عدد اللقطات وملفات البيانات المرتبطة بها. يزيل الأمر expire_snapshots اللقطات القديمة وينظّف ملفات البيانات التي لم تعد أي لقطة محتفَظ بها تُشير إليها.
الصياغة:
ALTER TABLE iceberg_table EXECUTE expire_snapshots(
['timestamp']
[, expire_before = 'timestamp']
[, retention_period = '3d']
[, retain_last = 100]
[, snapshot_ids = [1, 2, 3, 4]]
[, dry_run = 1]
);
بشكل افتراضي، يحدِّد سياسة الاستبقاء اللقطات التي يجب الاحتفاظ بها (خصائص الجدول min-snapshots-to-keep وmax-snapshot-age-ms، وعمليات التجاوز لكل مرجع). عند تحديد snapshot_ids، يتم تخطي سياسة الاستبقاء ولا تُؤخذ في الاعتبار لانتهاء الصلاحية إلا اللقطات المُدرجة.
الوسيطات:
'timestamp' (موضعي) أو expire_before = 'timestamp' — سلسلة DateTime (مثل '2024-06-01 00:00:00') تُفسَّر وفق المنطقة الزمنية للخادم. تعمل كآلية أمان: اللقطات التي تكون قيمة timestamp-ms الخاصة بها مساوية لهذه القيمة أو أحدث منها تكون محمية من انتهاء الصلاحية، حتى لو كانت سياسة الاستبقاء ستُنهي صلاحيتها بخلاف ذلك. ويمكن دمجها مع snapshot_ids، وفي هذه الحالة لا تنتهي صلاحية اللقطات المُدرجة التي تقع عند هذا الطابع الزمني أو بعده.
retention_period = '<duration>' — يتجاوز قيمة history.expire.max-snapshot-age-ms على مستوى الجدول لهذا الاستدعاء فقط. تصبح اللقطات الأقدم من هذه المدة (مقاسةً من الوقت الحالي) مرشحة لانتهاء الصلاحية. وتكون القيمة سلسلة مدة تتكوّن من زوج واحد أو أكثر من {number}{unit} متصلة معًا. الوحدات المدعومة: y (365 يومًا)، w (7 أيام)، d (24 ساعة)، h (60 دقيقة)، m (60 ثانية)، s (ثانية واحدة)، ms (1 ميلي ثانية). ويمكن دمج الوحدات، مثل '3d' و'12h' و'1d12h30m' و'500ms'.
retain_last = N — يتجاوز قيمة history.expire.min-snapshots-to-keep على مستوى الجدول لهذا الاستدعاء فقط. ويُحتفَظ دائمًا بما لا يقل عن N من اللقطات بغضّ النظر عن عمرها.
snapshot_ids = [id1, id2, ...] — يُنهي صلاحية معرّفات اللقطات المُدرجة فقط (باستثناء اللقطات المشار إليها بواسطة اللقطة الحالية أو الفروع أو الوسوم). يتخطى هذا الوضع سياسة الاستبقاء بالكامل، ولا يمكن دمجه مع retention_period أو retain_last.
dry_run = 1 — يحسب ما الذي كان سينتهي صلاحيته ويُرجع المقاييس دون كتابة بيانات وصفية جديدة أو حذف ملفات.
يتجاوز retention_period وretain_last فقط قيم الاستبقاء الافتراضية على مستوى الجدول. أما عمليات تجاوز الاستبقاء لكل مرجع (فرع/وسم) المُعدّة في خصائص جدول Iceberg (مثل refs.<branch>.min-snapshots-to-keep) فلا يتم تجاوزها مطلقًا — بل تسري دائمًا كما هي محددة في البيانات الوصفية للجدول.
مثال:
SET allow_insert_into_iceberg = 1;
-- Create some snapshots by inserting data
INSERT INTO iceberg_table VALUES (1);
INSERT INTO iceberg_table VALUES (2);
INSERT INTO iceberg_table VALUES (3);
-- Expire using retention policy only
ALTER TABLE iceberg_table EXECUTE expire_snapshots();
-- Expire with a safety fuse: protect snapshots newer than the timestamp (positional syntax)
ALTER TABLE iceberg_table EXECUTE expire_snapshots('2025-01-01 00:00:00');
-- Same using the named argument form
ALTER TABLE iceberg_table EXECUTE expire_snapshots(expire_before = '2025-01-01 00:00:00');
-- Override retention parameters for one execution
ALTER TABLE iceberg_table EXECUTE expire_snapshots(retention_period = '3d', retain_last = 10);
-- Expire explicit snapshots
ALTER TABLE iceberg_table EXECUTE expire_snapshots(snapshot_ids = [101, 102, 103]);
-- Dry-run preview (no metadata updates, no file deletes)
ALTER TABLE iceberg_table EXECUTE expire_snapshots(retention_period = '1d', dry_run = 1);
الناتج:
يعيد الأمر جدولًا بعمودين (metric_name String, metric_value Int64) ويضم صفًا واحدًا لكل مقياس. وتتبع أسماء المقاييس مواصفة Iceberg:
| metric_name | الوصف |
|---|
deleted_data_files_count | عدد ملفات البيانات المحذوفة |
deleted_position_delete_files_count | عدد ملفات الحذف الموضعي المحذوفة |
deleted_equality_delete_files_count | عدد ملفات حذف المساواة المحذوفة |
deleted_manifest_files_count | عدد ملفات البيان المحذوفة |
deleted_manifest_lists_count | عدد ملفات قوائم البيان المحذوفة |
deleted_statistics_files_count | عدد ملفات الإحصاءات المحذوفة (0 دائمًا حاليًا) |
dry_run | 1 لوضع التشغيل التجريبي، و0 للتنفيذ العادي |
ينفّذ الأمر الخطوات التالية:
- يقيّم سياسة الاحتفاظ (انظر أدناه) لتحديد اللقطات التي يجب الإبقاء عليها
- إذا تم توفير وسيطة طابع زمني، فإنه يحمي أيضًا جميع اللقطات عند ذلك الطابع الزمني أو الأحدث منه
- يُنهي صلاحية اللقطات التي لا تحتفظ بها السياسة ولا تشملها الحماية بالطابع الزمني
- يحسب الملفات المرتبطة حصريًا باللقطات منتهية الصلاحية
- في الوضع العادي: ينشئ بيانات وصفية جديدة من دون اللقطات منتهية الصلاحية
- في الوضع العادي: يحذف فعليًا قوائم البيان وملفات البيان وملفات البيانات التي لم يعد من الممكن الوصول إليها
- في وضع
dry_run = 1: يتخطى الخطوتين 5 و6 ويُرجع فقط المقاييس المحسوبة
يراعي الأمر expire_snapshots سياسة الاحتفاظ بلقطات Iceberg. يُضبط الاحتفاظ عبر خصائص جدول Iceberg وعمليات التجاوز على مستوى كل مرجع:
| Property | Scope | Default | Description |
|---|
history.expire.min-snapshots-to-keep | Table | iceberg_expire_default_min_snapshots_to_keep (الافتراضي 1) | الحد الأدنى لعدد اللقطات التي يجب الاحتفاظ بها في سلسلة الأسلاف لكل فرع |
history.expire.max-snapshot-age-ms | Table | iceberg_expire_default_max_snapshot_age_ms (الافتراضي 432000000، 5 أيام) | الحد الأقصى لعمر اللقطات (بالملي ثانية) التي يجب الاحتفاظ بها في فرع |
history.expire.max-ref-age-ms | Table | iceberg_expire_default_max_ref_age_ms (الافتراضي ∞) | الحد الأقصى لعمر مرجع اللقطة (فرع أو tag) بالملي ثانية قبل إزالة المرجع نفسه |
يمكن لكل مرجع لقطة (refs في بيانات Iceberg الوصفية) تجاوز هذه القيم من خلال حقول خاصة بكل مرجع: min-snapshots-to-keep وmax-snapshot-age-ms وmax-ref-age-ms.
تقييم الاحتفاظ:
- لكل فرع (بما في ذلك
main): يُتتبَّع تسلسل الأسلاف بدءًا من رأس الفرع. ويُحتفَظ باللقطات ما دام أحد الشرطين التاليين متحققًا:
- أن تكون اللقطة من أول
min-snapshots-to-keep في التسلسل
- أن يكون عمر اللقطة ضمن
max-snapshot-age-ms (أي now - timestamp-ms <= max-snapshot-age-ms)
- بالنسبة إلى
tags: يُحتفَظ باللقطة المشار إليها ما لم يكن tag قد تجاوز max-ref-age-ms الخاص به، وعندها يُزال مرجع tag
- المراجع غير
main التي يتجاوز عمرها max-ref-age-ms تُزال بالكامل (أما فرع main فلا يُزال أبدًا)
- المراجع المعلّقة التي تشير إلى لقطات غير موجودة تُزال مع تحذير
- تُحفَظ اللقطة الحالية دائمًا، بغض النظر عن إعدادات الاحتفاظ
الامتيازات المطلوبة:
امتياز ALTER TABLE EXECUTE مطلوب، وهو امتياز فرعي من ALTER TABLE في التسلسل الهرمي للتحكم في الوصول في ClickHouse. يمكنك منحه مباشرةً أو عبر الامتياز الأصل:
-- Grant only EXECUTE permission
GRANT ALTER TABLE EXECUTE ON my_iceberg_table TO my_user;
-- Or grant all ALTER TABLE permissions (includes ALTER TABLE EXECUTE)
GRANT ALTER TABLE ON my_iceberg_table TO my_user;
- لا تُدعَم إلا جداول Iceberg ذات تنسيق الإصدار 2 (إذ لا تضمن لقطات v1 وجود
manifest-list، وهو مطلوب لتحديد الملفات المراد تنظيفها بأمان)
- تُحفَظ اللقطة الحالية دائمًا، حتى إذا كانت أقدم من الطابع الزمني المحدد
- يتطلب تمكين الإعداد
allow_insert_into_iceberg
- يتطلب تمكين الإعداد
allow_experimental_expire_snapshots
- يُطبَّق التفويض الخاص بـ catalog نفسه (مثل مصادقة REST catalog وAWS Glue IAM وغيرها) بشكل مستقل عندما يحدّث ClickHouse البيانات الوصفية
الملفات اليتيمة هي ملفات موجودة في التخزين لا تشير إليها أي لقطة في البيانات الوصفية لجدول Iceberg. وتتراكم بسبب عمليات الكتابة الفاشلة، والتنظيف الجزئي بعد الدمج، والعمليات المنقطعة، مما يؤدي إلى نمو غير محدود في مساحة التخزين. يحدِّد الأمر remove_orphan_files هذه الملفات اليتيمة ويزيلها.
الصيغة:
-- Positional form: single unnamed older_than argument
ALTER TABLE iceberg_table EXECUTE remove_orphan_files('timestamp')
-- Named form
ALTER TABLE iceberg_table EXECUTE remove_orphan_files(
older_than = 'timestamp',
location = 'path',
dry_run = 0|1
)
-- No arguments: use all defaults (older_than = 3 days ago)
ALTER TABLE iceberg_table EXECUTE remove_orphan_files()
المعلمات:
| المعلمة | النوع | الافتراضي | الوصف |
|---|
older_than | String (طابع زمني) | قبل 3 أيام (قابل للتهيئة عبر iceberg_orphan_files_older_than_seconds) | لا تُعدّ ملفات يتيمة محتملة إلا الملفات التي يعود وقت آخر تعديل لها إلى ما قبل هذا الطابع الزمني. هذا إجراء احترازي لتجنّب حذف الملفات الناتجة عن عمليات كتابة لا تزال قيد التنفيذ. |
location | String | موقع الجدول | يقيّد الفحص بدليل فرعي محدد ضمن موقع الجدول (على سبيل المثال، 'data/' أو 'metadata/'). |
dry_run | UInt64 | 0 | عند ضبطه على 1، يحدّد الملفات اليتيمة ويُرجع ملخصًا بالنتائج دون حذف أي شيء فعليًا. |
أمثلة:
-- Remove orphan files older than a specific timestamp
ALTER TABLE iceberg_table EXECUTE remove_orphan_files('2026-03-01 00:00:00');
-- Dry run: preview which files would be deleted
ALTER TABLE iceberg_table EXECUTE remove_orphan_files(dry_run = 1);
-- Scan only the data directory
ALTER TABLE iceberg_table EXECUTE remove_orphan_files(
older_than = '2026-03-01 00:00:00',
location = 'data/'
);
-- Combine positional older_than with named arguments
ALTER TABLE iceberg_table EXECUTE remove_orphan_files(
'2026-03-01 00:00:00',
dry_run = 1
);
المخرجات:
يعيد الأمر جدولًا يحتوي على العمودين metric_name وmetric_value، ويعرض عدد الملفات المحذوفة (أو التي كان سيُحذفها في وضع dry_run) حسب الفئة. تُصنَّف فئات الملفات باستخدام أساليب استدلالية بأفضل جهد استنادًا إلى اصطلاحات تسمية الملفات؛ أما الملفات التي لا تطابق أي نمط محدد فتُدرج افتراضيًا ضمن deleted_data_files_count:
| metric_name | metric_value |
|---|
| deleted_data_files_count | 5 |
| deleted_position_delete_files_count | 2 |
| deleted_equality_delete_files_count | 0 |
| deleted_manifest_files_count | 3 |
| deleted_manifest_lists_count | 1 |
| deleted_metadata_files_count | 0 |
| deleted_statistics_files_count | 0 |
| skipped_missing_metadata_count | 0 |
| failed_deletions_count | 0 |
الإعدادات:
| الإعداد | النوع | الافتراضي | الوصف |
|---|
allow_iceberg_remove_orphan_files | Bool | false | إعداد تحكّم لتمكين الميزة (تجريبية). |
iceberg_orphan_files_older_than_seconds | UInt64 | 259200 (3 أيام) | عتبة older_than الافتراضية بالثواني عند عدم تمرير الوسيط. |
- يتطلب Iceberg format version 2 (أو أعلى). تُرفض جداول الإصدار 1 لأنها تفتقر إلى مؤشرات
manifest-list في لقطات، وهي مطلوبة لتحديد مجموعة الملفات القابلة للوصول بأمان. ويؤدي تشغيل الأمر على جدول v1 إلى إرجاع خطأ BAD_ARGUMENTS.
- يتطلب تمكين كلٍّ من الإعدادين
allow_insert_into_iceberg وallow_iceberg_remove_orphan_files
- يُوصى بتشغيل
expire_snapshots قبل remove_orphan_files حتى تُنظَّف أولًا الملفات المشار إليها بشكل فريد من خلال لقطات منتهية الصلاحية
- استخدم
dry_run = 1 لمعاينة orphan files قبل حذفها
- تحمي عتبة
older_than من حذف الملفات الناتجة عن عمليات كتابة لا تزال قيد التنفيذ — وتوفّر العتبة الافتراضية البالغة 3 أيام هامش أمان مريحًا