الانتقال إلى المحتوى الرئيسي
هذا هو موصل sink الرسمي لـ Apache Flink والمدعوم من ClickHouse. وقد بُني باستخدام AsyncSinkBase في Flink وJava client الرسمي لـ ClickHouse. يدعم هذا الموصل واجهة DataStream API في Apache Flink. ومن المخطط دعم Table API في إصدار لاحق.

المتطلبات

  • Java 11+ (لـ Flink 1.17+) أو 17+ (لـ Flink 2.0+)
  • Apache Flink 1.17+
ينقسم الموصل إلى حزمتَي artifact لدعم كلٍّ من Flink 1.17+ وFlink 2.0+. اختر الـ artifact المطابق لإصدار Flink الذي تريده:
إصدار FlinkArtifactإصدار عميل Java لـ ClickHouseإصدار Java المطلوب
الأحدثflink-connector-clickhouse-2.0.00.9.5Java 17+
2.0.1flink-connector-clickhouse-2.0.00.9.5Java 17+
2.0.0flink-connector-clickhouse-2.0.00.9.5Java 17+
1.20.2flink-connector-clickhouse-1.170.9.5Java 11+
1.19.3flink-connector-clickhouse-1.170.9.5Java 11+
1.18.1flink-connector-clickhouse-1.170.9.5Java 11+
1.17.2flink-connector-clickhouse-1.170.9.5Java 11+
لم يُختبر هذا الموصل مع إصدارات Flink الأقدم من 1.17.2

التثبيت والإعداد

استيراده كتبعية

<dependency>
    <groupId>com.clickhouse.flink</groupId>
    <artifactId>flink-connector-clickhouse-2.0.0</artifactId>
    <version>{{ stable_version }}</version>
    <classifier>all</classifier>
</dependency>
<dependency>
    <groupId>com.clickhouse.flink</groupId>
    <artifactId>flink-connector-clickhouse-1.17</artifactId>
    <version>{{ stable_version }}</version>
    <classifier>all</classifier>
</dependency>

تنزيل ملف JAR الثنائي

نمط تسمية ملف JAR الثنائي هو:
flink-connector-clickhouse-${flink_version}-${stable_version}-all.jar
حيث: يمكنك العثور على جميع ملفات JAR المُتاحة والصادرة في Maven Central Repository.

استخدام واجهة برمجة تطبيقات DataStream

مقتطف

لنفترض أنك تريد إدراج بيانات CSV خام في ClickHouse:
public static void main(String[] args) {
    // قم بتهيئة ClickHouseClient
    ClickHouseClientConfig clickHouseClientConfig = new ClickHouseClientConfig(url, username, password, database, tableName);

    // أنشئ ElementConverter
    ElementConverter<String, ClickHousePayload> convertorString = new ClickHouseConvertor<>(String.class);

    // أنشئ sink واضبط التنسيق باستخدام `setClickHouseFormat`
    ClickHouseAsyncSink<String> csvSink = new ClickHouseAsyncSink<>(
            convertorString,
            MAX_BATCH_SIZE,
            MAX_IN_FLIGHT_REQUESTS,
            MAX_BUFFERED_REQUESTS,
            MAX_BATCH_SIZE_IN_BYTES,
            MAX_TIME_IN_BUFFER_MS,
            MAX_RECORD_SIZE_IN_BYTES,
            clickHouseClientConfig
    );

    csvSink.setClickHouseFormat(ClickHouseFormat.CSV);

    // أخيرًا، صِل DataStream الخاصة بك بـ sink.
    final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

    Path csvFilePath = new Path(fileFullName);
    FileSource<String> csvSource = FileSource
            .forRecordStreamFormat(new TextLineInputFormat(), csvFilePath)
            .build();

    env.fromSource(
            csvSource,
            WatermarkStrategy.noWatermarks(),
            "GzipCsvSource"
    ).sinkTo(csvSink);
}
يمكنك العثور على المزيد من الأمثلة والمقتطفات في اختباراتنا:

مثال للبدء السريع

أنشأنا مثالًا قائمًا على Maven لتسهيل البدء باستخدام ClickHouse Sink: للحصول على إرشادات أكثر تفصيلًا، راجع دليل الأمثلة

خيارات الاتصال بواجهة برمجة تطبيقات DataStream

خيارات عميل ClickHouse

المعلماتالوصفالقيمة الافتراضيةمطلوب
urlعنوان URL المؤهل بالكامل لـ ClickHouseغير متاحنعم
usernameاسم مستخدم قاعدة بيانات ClickHouseغير متاحنعم
passwordكلمة مرور قاعدة بيانات ClickHouseغير متاحنعم
databaseاسم قاعدة بيانات ClickHouseغير متاحنعم
tableاسم جدول ClickHouseغير متاحنعم
optionsخريطة خيارات تهيئة عميل Javaخريطة فارغةلا
serverSettingsخريطة إعدادات جلسة خادم ClickHouseخريطة فارغةلا
enableJsonSupportAsStringإعداد في خادم ClickHouse لتوقّع قيمة String منسّقة بتنسيق JSON من أجل نوع بيانات JSONtrueلا
يجب تمرير options وserverSettings إلى العميل بصيغة Map<String, String>. وسيؤدي استخدام خريطة فارغة لأيٍّ منهما إلى استخدام الإعدادات الافتراضية للعميل أو الخادم، على الترتيب.
جميع خيارات عميل Java المتاحة مُدرجة في ClientConfigProperties.java وصفحة التوثيق هذه.جميع إعدادات جلسة الخادم المتاحة مُدرجة في صفحة التوثيق هذه.
على سبيل المثال:
Map<String, String> javaClientOptions = Map.of(
    ClientConfigProperties.CA_CERTIFICATE.getKey(), "<my_CA_cert>",
    ClientConfigProperties.SSL_CERTIFICATE.getKey(), "<my_SSL_cert>",
    ClientConfigProperties.CLIENT_NETWORK_BUFFER_SIZE.getKey(), "30000",
    ClientConfigProperties.HTTP_MAX_OPEN_CONNECTIONS.getKey(), "5"
);

Map<String, String> serverSettings = Map.of(
    "insert_deduplicate", "1"
);

ClickHouseClientConfig clickHouseClientConfig = new ClickHouseClientConfig(
    url,
    username,
    password,
    database,
    tableName,
    javaClientOptions,
    serverSettings,
    false // تمكين enableJsonSupportAsString
);

خيارات الـ sink

تأتي الخيارات التالية مباشرةً من AsyncSinkBase في Flink:
المعلماتالوصفالقيمة الافتراضيةمطلوب
maxBatchSizeالحد الأقصى لعدد السجلات التي يمكن إدراجها في دفعة واحدةN/Aنعم
maxInFlightRequestsالحد الأقصى لعدد الطلبات قيد التنفيذ المسموح بها قبل أن يطبّق الـ sink ضغطًا عكسيًاN/Aنعم
maxBufferedRequestsالحد الأقصى لعدد السجلات التي يمكن تخزينها مؤقتًا في الـ sink قبل تطبيق الضغط العكسيN/Aنعم
maxBatchSizeInBytesالحد الأقصى لحجم الدفعة (بالبايت). ستكون جميع الدفعات المرسلة أصغر من هذا الحجم أو مساوية لهN/Aنعم
maxTimeInBufferMSالحد الأقصى للمدة التي يمكن أن يبقى فيها السجل في الـ sink قبل تفريغهN/Aنعم
maxRecordSizeInBytesالحد الأقصى لحجم السجل الذي يقبله الـ sink، وستُرفض تلقائيًا أي سجلات أكبر من ذلكN/Aنعم

أنواع البيانات المدعومة

يوفّر الجدول أدناه مرجعًا سريعًا لتحويل أنواع البيانات عند الإدراج من Flink إلى ClickHouse.
نوع Javaنوع ClickHouseمدعومطريقة التسلسل
byte/ByteInt8DataWriter.writeInt8
short/ShortInt16DataWriter.writeInt16
int/IntegerInt32DataWriter.writeInt32
long/LongInt64DataWriter.writeInt64
BigIntegerInt128DataWriter.writeInt128
BigIntegerInt256DataWriter.writeInt256
short/ShortUInt8DataWriter.writeUInt8
int/IntegerUInt8DataWriter.writeUInt8
int/IntegerUInt16DataWriter.writeUInt16
long/LongUInt32DataWriter.writeUInt32
long/LongUInt64DataWriter.writeUInt64
BigIntegerUInt64DataWriter.writeUInt64
BigIntegerUInt128DataWriter.writeUInt128
BigIntegerUInt256DataWriter.writeUInt256
BigDecimalDecimalDataWriter.writeDecimal
BigDecimalDecimal32DataWriter.writeDecimal
BigDecimalDecimal64DataWriter.writeDecimal
BigDecimalDecimal128DataWriter.writeDecimal
BigDecimalDecimal256DataWriter.writeDecimal
float/FloatFloatDataWriter.writeFloat32
double/DoubleDoubleDataWriter.writeFloat64
boolean/BooleanBooleanDataWriter.writeBoolean
StringStringDataWriter.writeString
StringFixedStringDataWriter.writeFixedString
LocalDateDateDataWriter.writeDate
LocalDateDate32DataWriter.writeDate32
LocalDateTimeDateTimeDataWriter.writeDateTime
ZonedDateTimeDateTimeDataWriter.writeDateTime
LocalDateTimeDateTime64DataWriter.writeDateTime64
ZonedDateTimeDateTime64DataWriter.writeDateTime64
int/IntegerTimeغير متاح
long/LongTime64غير متاح
byte/ByteEnum8DataWriter.writeInt8
int/IntegerEnum16DataWriter.writeInt16
java.util.UUIDUUIDDataWriter.writeIntUUID
StringJSONDataWriter.writeJSON
Array<Type>Array<Type>DataWriter.writeArray
Map<K,V>Map<K,V>DataWriter.writeMap
Tuple<Type,..>Tuple<T1,T2,..>DataWriter.writeTuple
ObjectVariantغير متاح
ملاحظات:
  • يجب توفير ZoneId عند إجراء عمليات على التاريخ.
  • يجب توفير الدقة والمقياس عند إجراء عمليات على القيم العشرية.
  • لكي يتمكن ClickHouse من تحليل String في Java على أنه JSON، يجب تمكين enableJsonSupportAsString في ClickHouseClientConfig.
  • يتطلب الموصّل ElementConvertor لربط العناصر في DataStream المدخل بحمولات ClickHouse. ولهذا الغرض، يوفّر الموصّل ClickHouseConvertor وPOJOConvertor، ويمكنك استخدامهما لتنفيذ هذا الربط باستخدام طرق التسلسل الخاصة بـ DataWriter المذكورة أعلاه.

تنسيقات الإدخال المدعومة

يمكنك العثور على قائمة تنسيقات الإدخال المتاحة في ClickHouse في صفحة التوثيق هذه وClickHouseFormat.java. لتحديد التنسيق الذي ينبغي أن يستخدمه الموصل لتحويل DataStream إلى ClickHouse حمولات، استخدم الدالة setClickHouseFormat. على سبيل المثال:
ClickHouseAsyncSink<String> csvSink = new ClickHouseAsyncSink<>(
        convertorString,
        MAX_BATCH_SIZE,
        MAX_IN_FLIGHT_REQUESTS,
        MAX_BUFFERED_REQUESTS,
        MAX_BATCH_SIZE_IN_BYTES,
        MAX_TIME_IN_BUFFER_MS,
        MAX_RECORD_SIZE_IN_BYTES,
        clickHouseClientConfig
);
csvSink.setClickHouseFormat(ClickHouseFormat.CSV);
افتراضيًا، سيستخدم الموصل تنسيق RowBinaryWithDefaults أو RowBinary إذا ضُبطت القيمة setSupportDefault في ClickHouseClientConfig صراحةً على true أو false، على الترتيب.

المقاييس

يوفّر الموصل المقاييس الإضافية التالية بالإضافة إلى مقاييس Flink الحالية:
المقياسالوصفالنوعالحالة
numBytesSendالعدد الإجمالي للبايتات المُرسلة إلى ClickHouse ضمن حمولة الطلب. ملاحظة: يقيس هذا المقياس حجم البيانات المُسلسلة المُرسلة عبر الشبكة، وقد يختلف عن written_bytes في system.query_log في ClickHouse، الذي يعكس عدد البايتات الفعلي المكتوب إلى وحدة التخزين بعد المعالجةعداد
numRecordSendالعدد الإجمالي للسجلات المُرسلة إلى ClickHouseعداد
numRequestSubmittedالعدد الإجمالي للطلبات المُرسلة (العدد الفعلي لعمليات التفريغ المنفَّذة)عداد
numOfDroppedBatchesالعدد الإجمالي للدفعات التي أُسقطت بسبب إخفاقات غير قابلة لإعادة المحاولةعداد
numOfDroppedRecordsالعدد الإجمالي للسجلات التي أُسقطت بسبب إخفاقات غير قابلة لإعادة المحاولةعداد
totalBatchRetriesالعدد الإجمالي لمحاولات إعادة الدفعات بسبب إخفاقات قابلة لإعادة المحاولةعداد
writeLatencyHistogramمُدرَّج تكراري لتوزيع زمن استجابة عمليات الكتابة الناجحة (مللي ثانية)مُدرَّج تكراري
writeFailureLatencyHistogramمُدرَّج تكراري لتوزيع زمن استجابة عمليات الكتابة الفاشلة (مللي ثانية)مُدرَّج تكراري
triggeredByMaxBatchSizeCounterالعدد الإجمالي لعمليات التفريغ الناتجة عن بلوغ maxBatchSizeعداد
triggeredByMaxBatchSizeInBytesCounterالعدد الإجمالي لعمليات التفريغ الناتجة عن بلوغ maxBatchSizeInBytesعداد
triggeredByMaxTimeInBufferMSCounterالعدد الإجمالي لعمليات التفريغ الناتجة عن بلوغ maxTimeInBufferMSعداد
actualRecordsPerBatchمُدرَّج تكراري لتوزيع العدد الفعلي للسجلات في كل دفعةمُدرَّج تكراري
actualBytesPerBatchمُدرَّج تكراري لتوزيع العدد الفعلي للبايتات في كل دفعةمُدرَّج تكراري

القيود

  • يوفّر sink حاليًا ضمان تسليم مرة واحدة على الأقل. ويجري تتبّع العمل لتحقيق exactly-once semantics هنا.
  • لا يدعم sink بعدُ قائمة انتظار الرسائل الميتة (DLQ) لتخزين السجلات غير القابلة للمعالجة مؤقتًا. وحتى ذلك الحين، سيحاول الموصل إعادة إدراج السجلات التي تفشل، وسيتخلّص منها إذا لم ينجح ذلك. ويجري تتبّع هذه الميزة هنا.
  • لا يدعم sink بعدُ الإنشاء عبر Table API الخاصة بـ Flink أو Flink SQL. ويجري تتبّع هذه الميزة هنا.

توافق إصدارات ClickHouse والأمان

  • يُختبَر موصل مع مجموعة من إصدارات ClickHouse الحديثة، بما في ذلك latest وhead، عبر سير عمل CI يومي. وتُحدَّث الإصدارات المختبَرة دوريًا مع اعتماد إصدارات ClickHouse الجديدة. اطّلع هنا على الإصدارات التي يُختبَر موصل عليها يوميًا.
  • راجع سياسة أمان ClickHouse للتعرّف على الثغرات الأمنية المعروفة وكيفية الإبلاغ عن أي ثغرة.
  • نوصي بترقية موصل باستمرار حتى لا تفوتك الإصلاحات الأمنية والتحسينات الجديدة.
  • إذا واجهت مشكلة في الترحيل، فيُرجى إنشاء issue على GitHub وسنرد عليك!
  • للحصول على أفضل أداء، تأكد من أن نوع العنصر في DataStream لديك ليس من النوع Generic - راجع هذا الشرح لتمييز الأنواع في Flink. فالعناصر غير العامة تتجنب كلفة التسلسل الإضافية التي يفرضها Kryo وتُحسّن معدل النقل إلى ClickHouse.
  • نوصي بضبط maxBatchSize على 1000 كحد أدنى، ويفضَّل أن يكون بين 10,000 و100,000. راجع هذا الدليل حول عمليات الإدراج المجمّعة لمزيد من المعلومات.
  • لإجراء إزالة التكرار أو upsert إلى ClickHouse بأسلوب OLTP، راجع صفحة التوثيق هذه. ملاحظة: لا تخلط بين هذا وبين إزالة التكرار على مستوى الدُفعات التي تحدث عند إعادة المحاولة.

استكشاف الأخطاء وإصلاحها

CANNOT_READ_ALL_DATA

قد يظهر الخطأ التالي:
com.clickhouse.client.api.ServerException: Code: 33. DB::Exception: Cannot read all data. Bytes read: 9205. Bytes expected: 1100022.: (at row 9) : While executing BinaryRowInputFormat. (CANNOT_READ_ALL_DATA)
السبب: في معظم الحالات، يعني الخطأ CANNOT_READ_ALL_DATA أن مخطط جدول ClickHouse لديك لم يعد متطابقًا مع مخطط سجل Flink. وقد يحدث ذلك عندما يُعدَّل أحدهما أو كلاهما بطريقة غير متوافقة مع الإصدارات السابقة. الحل: حدِّث المخطط في جدول ClickHouse لديك أو نوع بيانات إدخال الموصل (أو كليهما) بحيث يصبحان متوافقين. وإذا لزم الأمر، فارجع إلى تعيين الأنواع لمعرفة كيفية ربط أنواع Java بأنواع ClickHouse. ملاحظة: إذا كانت لا تزال هناك سجلات قيد النقل، فستحتاج إلى إعادة تعيين حالة Flink عند إعادة تشغيل الموصل.

انخفاض معدل النقل

قد تلاحظ أن معدل نقل الموصل لا يزداد بما يتناسب مع توازي المهمة (عدد مهام Flink) عند الكتابة إلى ClickHouse. السبب: قد تؤدي عملية دمج الأجزاء في الخلفية في ClickHouse إلى إبطاء عمليات الإدراج. يمكن أن يحدث ذلك عندما يكون حجم الدفعة المُعدّ صغيرًا جدًا، أو عندما يقوم الموصل بعملية التفريغ بشكل متكرر جدًا، أو بسبب الجمع بين الأمرين. الحل: راقب المقياسين numRequestSubmitted وactualRecordsPerBatch للمساعدة في تحديد كيفية ضبط حجم الدفعة (maxBatchSize) وعدد مرات التفريغ. راجع أيضًا الاستخدام المتقدم والموصى به للاطلاع على توصيات بشأن حجم الدفعات.

هناك صفوف مفقودة في جدول ClickHouse الخاص بي

السبب: أُسقِطت الدفعة (أو الدفعات) إما بسبب فشل غير قابل لإعادة المحاولة، أو لعدم إمكانية إدراجها ضمن عدد محاولات إعادة المحاولة المُعدّ (يمكن ضبطه عبر ClickHouseClientConfig.setNumberOfRetries()). ملاحظة: يحاول الموصل، افتراضيًا، إعادة إدراج الدفعة حتى 3 مرات قبل إسقاطها. الحل: افحص سجلات TaskManager و/أو تتبّع المكدس لتحديد السبب الجذري.

المساهمة والدعم

إذا كنت ترغب في المساهمة في المشروع أو الإبلاغ عن أي مشكلات، فنحن نرحّب بمشاركتك! تفضّل بزيارة مستودع GitHub الخاص بنا لفتح issue أو اقتراح تحسينات أو إرسال pull request. نرحّب بالمساهمات! يُرجى الاطلاع على دليل المساهمة في المستودع قبل البدء. شكرًا لمساعدتك في تحسين موصل ClickHouse لـ Flink!
آخر تعديل في ٢٩ يونيو ٢٠٢٦