
في بداية العام ، قررنا معرفة كيفية تخزين وقراءة سجلات تصحيح أخطاء VK بشكل أكثر كفاءة من ذي قبل. سجلات التصحيح هي ، على سبيل المثال ، سجلات تحويل الفيديو (بشكل أساسي إخراج الأمر ffmpeg وقائمة خطوات ملفات المعالجة المسبقة) ، والتي نحتاجها أحيانًا فقط بعد 2-3 أشهر من معالجة ملف المشكلة.
في ذلك الوقت ، كان لدينا طريقتان لتخزين ومعالجة السجلات - محرك السجلات الخاص بنا و rsyslog ، اللذين استخدمناهما بالتوازي. بدأنا في النظر في خيارات أخرى وأدركنا أن ClickHouse من Yandex مناسب تمامًا بالنسبة لنا - قررنا تنفيذها.
في هذه المقالة سوف أتحدث عن كيف بدأنا في استخدام ClickHouse على فكونتاكتي ، أي أشعل النار التي خطونا عليها ، وما هي KittenHouse و LightHouse. يتم وضع كلا المنتجين في روابط مفتوحة المصدر ، في نهاية المقالة.
مهمة جمع السجلات
متطلبات النظام:
- تخزين مئات تيرابايت من السجلات.
- التخزين لشهور أو (نادرًا) سنوات.
- سرعة كتابة عالية.
- سرعة قراءة عالية (القراءة نادرة).
- دعم الفهرس
- دعم الأوتار الطويلة (> 4 كيلوبايت).
- بساطة العملية.
- تخزين مدمج.
- القدرة على الإدراج من عشرات الآلاف من الخوادم (سيكون UDP زائدًا).
الحلول الممكنة
دعونا نذكر بإيجاز الخيارات التي أخذناها بعين الاعتبار ، وسلبياتها:
محرك سجلات
خدمتنا الصغيرة مكتوبة ذاتيا للسجلات.
- قادرة على إعطاء فقط خطوط N الأخيرة التي تناسب ذاكرة الوصول العشوائي.
- ليس تخزينًا صغيرًا جدًا (لا يوجد ضغط شفاف).
Hadoop
- لا تحتوي جميع التنسيقات على فهارس.
- يمكن أن تكون سرعة القراءة أعلى (حسب الشكل).
- تعقيد الإعدادات.
- لا توجد إمكانية لإدخال عشرات الآلاف من الخوادم (كافكا أو نظائرها مطلوبة).
ملفات Rsyslog +
- لا توجد فهارس.
- سرعة قراءة منخفضة (grep العادية / zgrep).
- سلاسل غير مدعومة هندسياً> 4 كيلوبايت ، UDP أقل (1.5 كيلوبايت).
يتم تحقيق التخزين المضغوط عن طريق logrotate فوق التاج
استخدمنا rsyslog كعامل احتياطي للتخزين على المدى الطويل ، ولكن تم قطع الخطوط الطويلة ، لذلك بالكاد يمكن تسميتها مثالية.
ملفات LSD +
- لا توجد فهارس.
- سرعة قراءة منخفضة (grep العادية / zgrep).
- غير مصمم خصيصًا للإدخال من عشرات الآلاف من الخوادم.
يتم تحقيق التخزين المضغوط عن طريق logrotate فوق التاج.
الاختلافات من rsyslog في حالتنا هي أن LSD يدعم السلاسل الطويلة ، ولكن يلزم إدخال تغييرات كبيرة على البروتوكول الداخلي للإدراج من عشرات الآلاف من الخوادم ، على الرغم من أنه يمكن القيام بذلك.
بحث
- مشاكل في التشغيل.
- تسجيل غير مستقر.
- لا UDP.
- ضغط سيئ.
مكدس ELK هو بالفعل المعيار الصناعي لتخزين السجل. في تجربتنا ، كل شيء على ما يرام مع سرعة القراءة ، ولكن هناك مشاكل في الكتابة ، على سبيل المثال ، عند دمج المؤشرات.
تم تصميم مرونة البحث بشكل أساسي للبحث عن النص الكامل وطلبات القراءة المتكررة نسبيًا. بالنسبة لنا ، يعد التسجيل المستقر والقدرة على قراءة بياناتنا بسرعة أكبر أو أقل أهمية أكثر ، وبالصدفة الدقيقة. تم شحذ الفهرس في ElasticSearch للبحث عن النص الكامل ، ومساحة القرص كبيرة جدًا مقارنةً بـ gzip للمحتوى الأصلي.
كليك هاوس
- لا UDP.
بشكل عام ، الشيء الوحيد الذي لم يناسبنا في ClickHouse هو عدم وجود اتصال UDP. في الواقع ، من بين الخيارات المذكورة أعلاه ، كان فقط rsyslog لديه ، لكن rsyslog لم يدعم الخطوط الطويلة.
وفقًا لمعايير أخرى ، جاء ClickHouse إلينا ، وقررنا استخدامه ، وتم حل مشاكل النقل في هذه العملية.
لماذا هناك حاجة إلى KittenHouse
كما تعلم على الأرجح ، يعمل VKontakte على PHP / KPHP ، مع "محركات" (خدمات صغيرة) في C / C ++ وقليلًا من Go. ليس لدى PHP مفهوم "الحالة" بين الطلبات ، ربما باستثناء الذاكرة المشتركة والاتصالات المفتوحة.
نظرًا لأن لدينا عشرات الآلاف من الخوادم التي نريد أن نتمكن من إرسال السجلات منها إلى ClickHouse ، سيكون من غير المربح الاحتفاظ بالاتصالات المفتوحة من كل عامل PHP (يمكن أن يكون هناك أكثر من 100 عامل لكل خادم). لذلك ، نحتاج إلى نوع من الوكيل بين ClickHouse و PHP. أطلقنا على هذا الوكيل KittenHouse.
KittenHouse ، v1
أولاً ، قررنا تجربة أبسط مخطط ممكن لفهم ما إذا كان نهجنا سيعمل أم لا. إذا كان كافكا يتبادر إلى ذهنك عند حل هذه المشكلة ، فأنت لست وحدك. ومع ذلك ، لم نرغب في استخدام خوادم وسيطة إضافية - في هذه الحالة ، يمكننا بسهولة الاستناد إلى أداء هذه الخوادم ، وليس ClickHouse نفسها. بالإضافة إلى ذلك ، قمنا بجمع السجلات واحتجنا إلى تأخير صغير يمكن توقعه في إدخال البيانات. المخطط كما يلي:

في كل خادم من الخوادم ، يتم تثبيت وكيلنا المحلي (kittenhouse) ، ويحتفظ كل مثيل باتصال HTTP واحد مع خادم ClickHouse الضروري. يتم اللصق في الجداول المخزنة مؤقتًا ، نظرًا لأنه لا يُنصح غالبًا بإدراج MergeTree.
ميزات KittenHouse ، v1
كانت النسخة الأولى من KittenHouse تعرف القليل جدًا ، ولكن هذا كان كافيًا للاختبار:
- التواصل من خلال RPC (مخطط TL).
- الحفاظ على اتصال TCP / IP واحد لكل خادم.
- التخزين المؤقت في الذاكرة بشكل افتراضي ، مع حجم مخزن مؤقت محدود (يتم التخلص من الباقي).
- القدرة على الكتابة على القرص ، في هذه الحالة هناك ضمان للتسليم (مرة واحدة على الأقل).
- الفاصل الزمني للإدخال مرة واحدة كل ثانيتين.
المشاكل الأولى
واجهنا المشكلة الأولى عندما "قمنا بسداد" خادم ClickHouse لعدة ساعات ثم أعدنا تشغيله. أدناه يمكنك رؤية متوسط التحميل على الخادم بعد أن "يرتفع":

التفسير بسيط للغاية: ClickHouse لديه شبكة لكل نموذج مؤشر ترابط ، لذلك عندما أحاول جعل INSERT من آلاف العقد في نفس الوقت ، كان هناك منافسة قوية للغاية لموارد وحدة المعالجة المركزية بالكاد استجاب الخادم. ومع ذلك ، تم إدراج جميع البيانات في النهاية ولم يسقط شيء.
لحل هذه المشكلة ، وضعنا nginx أمام ClickHouse ، وبشكل عام ، ساعدنا.
مزيد من التطوير
خلال العملية ، واجهنا عددًا من المشاكل ، لا تتعلق بشكل رئيسي بـ ClickHouse ، ولكن بطريقتنا في تشغيله. هنا أشعل النار آخر داسنا عليه:
يؤدي عدد كبير من "أجزاء" من جداول المخزن المؤقت إلى تدفق المخزن المؤقت بشكل متكرر في MergeTree
في حالتنا ، كان هناك 16 قطعة من المخزن المؤقت وفاصل زمني لإعادة التعيين كل ثانيتين ، وكان هناك 20 قطعة من الجداول ، والتي أعطت ما يصل إلى 160 إدراج في الثانية. وقد أثر هذا بشكل دوري على أداء الإدراج بشكل سيء للغاية - كان هناك الكثير من عمليات دمج الخلفية ووصل استخدام القرص إلى 80٪ وأعلى.
الحل: قم بزيادة الفاصل الزمني لإعادة ضبط المخزن المؤقت الافتراضي ، قلل عدد القطع إلى 2.
يُرجع Nginx 502 عند الاتصال بنهاية المنبع
هذا في حد ذاته ليس مشكلة ، ولكن بالاقتران مع التنظيف المتكرر للمخزن المؤقت ، أعطى هذا خلفية عالية إلى حد ما من أخطاء 502 عند محاولة الإدراج في أي من الجداول ، وكذلك عند محاولة تنفيذ SELECT.
الحل: لقد كتبوا وكيلهم العكسي باستخدام مكتبة
fasthttp ، التي
تجمع الإدخال في الجداول وتستهلك الاتصالات بشكل اقتصادي للغاية. كما أنه يميز بين SELECT و INSERT ولديه تجمعات اتصال منفصلة للإدخال والقراءة.

نفاد الذاكرة مع إدخال مكثف
مكتبة fasthttp لها مزاياها وعيوبها. أحد العوائق هو أن الطلب والاستجابة يتم تخزينهما بشكل كامل في الذاكرة قبل منح التحكم لمعالج الطلب. بالنسبة لنا ، نتج عن ذلك حقيقة أنه إذا لم يكن الإدخال في ClickHouse "لديه وقت" ، فإن المخازن المؤقتة بدأت في النمو وفي نهاية المطاف نفدت جميع الذاكرة على الخادم ، مما أدى إلى قتل الوكيل العكسي بواسطة OOM. رسم الزملاء محرضًا:
الحل: بات تصحيح fasthttp لدعم تدفق نص طلب POST مهمة شاقة ، لذلك قررنا استخدام اتصالات Hijack () وترقية الاتصال ببروتوكولنا إذا جاء الطلب مع طريقة HTTP KITTEN. نظرًا لأنه يجب على الخادم الرد على MEOW ردًا ، إذا كان يفهم هذا البروتوكول ، فإن المخطط بأكمله يسمى بروتوكول KITTEN / MEOW.
نحن نقرأ فقط من 50 اتصالًا عشوائيًا في المرة الواحدة ، وبفضل TCP / IP ، فإن بقية العملاء "ينتظرون" ولا ننفق ذاكرة على المخازن المؤقتة حتى تصل قائمة الانتظار إلى العملاء المعنيين. قلل هذا من استهلاك الذاكرة 20 مرة على الأقل ، ولم يعد لدينا مثل هذه المشاكل.
يمكن أن تستمر جداول ALTER في حالة وجود استعلامات طويلة
يحتوي ClickHouse على ALTER غير مانع ، بمعنى أنه لا يتداخل مع كل من استعلامات SELECT و INSERT. ولكن لا يمكن أن تبدأ ALTER حتى تنتهي من تنفيذ الاستعلامات إلى هذا الجدول المرسل قبل ALTER.
إذا كان لخادمك خلفية من الاستعلامات "الطويلة" لبعض الجداول ، فقد تواجه موقفًا حيث لن يكون لدى ALTER على هذا الجدول وقت للتنفيذ في مهلة افتراضية مدتها 60 ثانية. ولكن هذا لا يعني أن ALTER ستفشل: سيتم تنفيذه بمجرد الانتهاء من استعلامات SELECT هذه.
هذا يعني أنك لا تعرف في أي وقت حدث بالفعل ALTER ، وليس لديك القدرة على إعادة إنشاء جداول المخزن المؤقت تلقائيًا بحيث يكون تخطيطها هو نفسه دائمًا. هذا يمكن أن يؤدي إلى مشاكل الإدراج.

الحل: نتيجة لذلك ، نخطط للتخلي تمامًا عن استخدام الجداول العازلة. بشكل عام ، الجداول العازلة لها نطاق ، حتى الآن نستخدمها ولا نواجه مشاكل كبيرة. ولكن الآن وصلنا أخيرًا إلى النقطة التي يكون فيها تنفيذ وظائف الجداول العازلة على جانب الوكيل العكسي أسهل من الاستمرار في تحمل عيوبها. ستبدو دائرة المثال بهذا الشكل (الخط المتقطع يظهر ACK عدم التزامن على INSERT).

قراءة البيانات
لنفترض أننا اكتشفنا الإدخال. كيف تقرأ هذه السجلات من ClickHouse؟ للأسف ، لم نجد أي أدوات ملائمة وسهلة الاستخدام لقراءة البيانات الأولية (بدون الرسوم البيانية وغيرها) من ClickHouse ، لذلك كتبنا الحل الخاص بنا - LightHouse. قدراتها متواضعة إلى حد ما:
- عرض سريع لمحتويات الجدول.
- التصفية والفرز.
- تحرير استعلام SQL.
- عرض هيكل الجدول.
- يعرض العدد التقريبي للخطوط ومساحة القرص المستخدمة.
ومع ذلك ، فإن LightHouse سريع وقادر على القيام بما نحتاج إليه. إليك بضع لقطات شاشة:
عرض هيكل الجدول
تصفية المحتوى
النتائج
ClickHouse هي عمليا قاعدة البيانات مفتوحة المصدر الوحيدة التي تأصلت في فكونتاكتي. نحن سعداء بسرعة عملها ونحن على استعداد لتحمل أوجه القصور ، والتي سيتم مناقشتها أدناه.
صعوبة في العمل
الكل في الكل ، ClickHouse هي قاعدة بيانات مستقرة للغاية وسريعة للغاية. ومع ذلك ، كما هو الحال مع أي منتج ، خاصة الشباب ، هناك ميزات في العمل يجب أخذها في الاعتبار:
- ليست جميع الإصدارات مستقرة بنفس القدر: لا تقم بالترقية مباشرة إلى الإصدار الجديد عند الإنتاج ، فمن الأفضل الانتظار لعدة إصدارات من إصلاح الأخطاء.
- للحصول على الأداء الأمثل ، من المستحسن للغاية تكوين RAID وبعض الأشياء الأخرى وفقًا للتعليمات. تم الإبلاغ عن هذا مؤخرا على حمولة عالية .
- النسخ المتماثل ليس له حدود سرعة مضمنة ويمكن أن يسبب تدهورًا كبيرًا في أداء الخادم إذا لم تحده بنفسك (لكنهم يعدون بإصلاحه).
- يحتوي Linux على ميزة غير سارة في آلية الذاكرة الظاهرية: إذا كنت تكتب بشكل نشط على القرص ولم يكن لديك الوقت الكافي للتدفق ، في مرحلة ما "يذهب الخادم إلى نفسه تمامًا" ، ويبدأ بنشاط في مسح ذاكرة التخزين المؤقت للصفحة على القرص ويحظر تمامًا عملية ClickHouse. يحدث هذا في بعض الأحيان مع عمليات الدمج الكبيرة ، وتحتاج إلى مراقبة ذلك ، على سبيل المثال ، تدفق التخزين المؤقت بشكل دوري بنفسك أو إجراء المزامنة.
المصدر المفتوح
KittenHouse و LightHouse متاحة الآن في مصدر مفتوح في مستودع github الخاص بنا:
- KittenHouse: github.com/vkcom/kittenhouse
- LightHouse: github.com/vkcom/lighthouse
شكرا لك
يوري نصرتدينوف ، المطور في قسم البنية التحتية الخلفية في فكونتاكتي