ClickHouse + Graphite: كيفية تقليل استهلاك مساحة القرص بشكل كبير


تحيات هابر.


إذا كان شخص ما يدير نظام الجرافيت على شبكة الإنترنت ويواجه مشكلة في أداء التخزين الهمس (IO ، استهلاك مساحة القرص) ، فعندئذ يجب أن تهدف فرصة ClickHouse كبديل إلى واحدة. يعني هذا البيان أن تطبيق الجهة الخارجية ، مثل carbonwriter أو go-carbon ، يستخدم بالفعل كمقياس استقبال لل daemon.


ClickHouse يحل المشاكل المذكورة بشكل جيد. على سبيل المثال ، بعد نقل بيانات 2TiB من الهمس ، فإنها تناسب 300 جيجابت. لن أتناول بالتفصيل المقارنة ؛ فهناك مقالات كافية حول هذا الموضوع. بالإضافة إلى ذلك ، حتى وقت قريب ، كان كل شيء مثاليًا من خلال التخزين ClickHouse.


مشاكل الاستهلاك


للوهلة الأولى ، كل شيء يجب أن تعمل بشكل جيد. باتباع الوثائق ، نقوم بإنشاء تكوين لنظام تخزين المقاييس (يُشار إليه فيما يلي retention ) ، ثم ننشئ جدولًا وفقًا لتوصية الخلفية المحددة في موقع graphite-web: carbon-clickhouse + graphite-clickhouse أو graphouse ، بناءً على المكدس المستخدم. و ... تأتي القنبلة الموقوتة.


من أجل فهم أي منها ، تحتاج إلى معرفة كيفية إدراج ومسار الحياة الإضافي للبيانات في جداول مجموعة المحركات * MergeTree ClickHouse (الرسوم البيانية مأخوذة من عرض Alexei Zatelepin):


  • إدراج البيانات. في حالتنا ، وصلت المقاييس.
  • يتم فرز كل كتلة من هذه المجموعات قبل الكتابة إلى القرص وفقًا لمفتاح ORDER BY المحدد عند إنشاء الجدول.
  • بعد الفرز ، تتم كتابة البيانات على القرص.
  • يراقب الخادم في الخلفية أنه لا يوجد الكثير من هذه القطع ، ويبدأ الخلفية ( merge ، فيما يلي دمج).

  • توقف الخادم عن تشغيل الدمج من تلقاء نفسه بمجرد أن لا partition بشكل نشط ، ولكن يمكنك بدء العملية يدويًا باستخدام أمر OPTIMIZE .
  • إذا كان هناك جزء واحد فقط في القسم ، فلا يمكنك بدء الدمج بالأمر المعتاد ، يجب عليك استخدام OPTIMIZE ... FINAL

لذلك ، وصول المقاييس الأولى. ويشغلون مساحة معينة. قد تختلف الأحداث اللاحقة بشكل طفيف وفقًا للعديد من العوامل:


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

وكل شيء ينتهي دائما نفسه. ينمو المكان الذي تشغله المقاييس في ClickHouse فقط إذا:


  • لا تطبق OPTIMIZE ... FINAL يدويًا أو
  • لا تقم بإدراج البيانات في جميع الأقسام بشكل مستمر من أجل بدء دمج الخلفية عاجلاً أو آجلاً

الطريقة الثانية تبدو أسهل في التنفيذ وبالتالي ، فهو مخطئ وتم اختباره أولاً.
كتبت نصًا بسيطًا نوعًا ما من الثعبان أرسل مقاييس وهمية لكل يوم خلال السنوات الأربع الماضية وركض التاج كل ساعة.
نظرًا لأن كل عمل ClickHouse DBMS يعتمد على حقيقة أن هذا النظام سوف يقوم بجميع الأعمال الأساسية عاجلاً أم آجلاً ، لكن من غير المعروف متى ، لم أستطع الانتظار حتى تتأجل القطع القديمة الضخمة للبدء في الدمج مع القطع الصغيرة الجديدة. أصبح من الواضح أنه كان علينا البحث عن طريقة لأتمتة التحسينات القسرية.



المعلومات في جداول نظام ClickHouse


نلقي نظرة على هيكل الجدول system.parts . هذه هي معلومات شاملة حول كل قطعة من جميع الجداول على خادم ClickHouse. يحتوي ، من بين أشياء أخرى ، على الأعمدة التالية:


  • اسم database ( database ) ؛
  • اسم الجدول ( table ) ؛
  • اسم القسم ومعرفه ( partition & partition_id ) ؛
  • عندما تم إنشاء القطعة ( modification_time ) ؛
  • الحد الأدنى والحد الأقصى للتاريخ في قطعة (يتم التقسيم حسب اليوم) ( min_date & max_date ) ؛

يوجد أيضًا جدول system.graphite_retentions ، مع الحقول المهمة التالية:


  • اسم DB ( Tables.database ) ؛
  • اسم الجدول ( Tables.table ) ؛
  • عمر المقياس عند تطبيق التجميع التالي ( age ) ؛

لذلك:


  1. لدينا جدول القطع وجدول قواعد التجميع.
  2. الجمع بين تقاطعها والحصول على جميع الجداول * GraphiteMergeTree.
  3. نحن نبحث عن جميع الأقسام التي:
    • أكثر من قطعة واحدة
    • أو حان الوقت لتطبيق قاعدة التجميع التالية ، modification_time أقدم من تلك اللحظة.

تطبيق


هذا الطلب
 SELECT concat(p.database, '.', p.table) AS table, p.partition_id AS partition_id, p.partition AS partition, --  "" ,      -- ,    ,  (*) max(g.age) AS age, --     countDistinct(p.name) AS parts, --        00:00:00   toDateTime(max(p.max_date + 1)) AS max_time, --      max_time + age AS rollup_time, --         min(p.modification_time) AS modified_at FROM system.parts AS p INNER JOIN ( --      *GraphiteMergeTree SELECT Tables.database AS database, Tables.table AS table, age FROM system.graphite_retentions ARRAY JOIN Tables GROUP BY database, table, age ) AS g ON (p.table = g.table) AND (p.database = g.database) WHERE --    p.active -- (*)   ,        AND ((toDateTime(p.max_date + 1) + g.age) < now()) GROUP BY table, partition HAVING --  ,     (modified_at < rollup_time) --     OR (parts > 1) ORDER BY table ASC, partition ASC, age ASC 

تقوم بإرجاع كل جزء من الجداول * GraphiteMergeTree الجداول التي يجب دمج تحرير مساحة القرص. كل ما تبقى هو الشيء القليل: OPTIMIZE ... FINAL جميعًا مع الطلب OPTIMIZE ... FINAL . أخذ التنفيذ النهائي في الاعتبار أيضًا اللحظة التي لا توجد فيها حاجة إلى لمس الأقسام بسجل نشط.


هذا هو بالضبط ما يفعله مشروع محسن الجرافيت . قام زملاء سابقون من Yandex.Market باختباره في المنتج ، ويمكن رؤية نتيجة العمل أدناه.


يؤدي


إذا قمت بتشغيل البرنامج على الخادم باستخدام ClickHouse ، فسيبدأ العمل فقط في وضع الخفي. مرة واحدة في الساعة ، سيتم تنفيذ الطلب ، والتحقق مما إذا كانت هناك أقسام جديدة أقدم من ثلاثة أيام يمكن تحسينها.


في المستقبل القريب - لتقديم حزم deb على الأقل ، وإذا أمكن - دورة في الدقيقة أيضًا.


UPD: تتوفر الحزم في إصدارات github ، ويمكن العثور على الصور العاملة على عامل إرساء في مستودع innogames / graphite-ch-optimizer.


بدلا من الاستنتاج


على مدار التسعة أشهر الماضية ، قضيت الكثير من الوقت داخل شركة InnoGames الخاصة بي عند تقاطع ClickHouse و graphite-web. لقد كانت تجربة جيدة ، والتي مكنت من التبديل بسرعة من الهمس إلى ClickHouse كمستودع للمقاييس. آمل أن يكون هذا المقال بمثابة بداية دورة حول التحسينات التي أجريناها على أجزاء مختلفة من هذه المجموعة ، وما الذي سيتم القيام به في المستقبل.


قضيت عدة لترات من أيام البيرة والمشرف على تطوير الطلب مع v0devil ، والذي أريد أن أعرب عن امتناني له. وأيضا لمراجعة هذه المقالة.


صفحة المشروع على جيثب

Source: https://habr.com/ru/post/ar479564/


All Articles