نكتب في PostgreSQL على sublight: مضيف واحد ، يوم واحد ، 1 تيرابايت

أخبرتك مؤخرًا كيف تستخدم الوصفات القياسية زيادة أداء استعلامات قراءة SQL من قاعدة بيانات PostgreSQL. اليوم سنتحدث عن كيف يمكن أن يتم التسجيل بشكل أكثر كفاءة في قاعدة البيانات دون استخدام أي "تقلبات" في التكوين - وذلك ببساطة عن طريق تنظيم تدفقات البيانات بشكل صحيح.

نكتب في PostgreSQL على sublight: مضيف واحد ، يوم واحد ، 1 تيرابايت

#1. التقسيم

مقال عن كيف ولماذا يستحق التنظيم التقسيم التطبيقي "نظريا" لقد تم بالفعل، وهنا سنتحدث عن ممارسة تطبيق بعض الأساليب داخل منطقتنا خدمة مراقبة لمئات من خوادم PostgreSQL.

"أشياء من الأيام الماضية..."

في البداية، مثل أي MVP، بدأ مشروعنا تحت حمل خفيف إلى حد ما - تم تنفيذ المراقبة فقط للخوادم العشرة الأكثر أهمية، وكانت جميع الجداول مضغوطة نسبيًا... ولكن مع مرور الوقت، أصبح عدد المضيفين الخاضعين للمراقبة أكثر فأكثر ، ومرة ​​أخرى حاولنا أن نفعل شيئًا مع أحد حجم الجداول 1.5 تيرابايتأدركنا أنه على الرغم من أنه كان من الممكن الاستمرار في العيش بهذه الطريقة، إلا أنه كان غير مريح للغاية.

كانت الأوقات تقريبًا مثل الأوقات الملحمية، وكانت الإصدارات المختلفة من PostgreSQL 9.x ذات صلة، لذلك كان يجب إجراء جميع عمليات التقسيم "يدويًا" - من خلال وراثة الجدول والمشغلات التوجيه مع ديناميكية EXECUTE.

نكتب في PostgreSQL على sublight: مضيف واحد ، يوم واحد ، 1 تيرابايت
تبين أن الحل الناتج عالمي بدرجة كافية بحيث يمكن ترجمته إلى جميع الجداول:

  • تم الإعلان عن جدول أصلي "رأسي" فارغ، والذي يصف الكل الفهارس والمشغلات الضرورية.
  • تم التسجيل من وجهة نظر العميل في الجدول "الجذر"، وباستخدام داخليًا مشغل التوجيه BEFORE INSERT تم إدراج السجل "فعليًا" في القسم المطلوب. إذا لم يكن هناك شيء من هذا القبيل بعد، فقد حصلنا على استثناء و...
  • … باستخدام CREATE TABLE ... (LIKE ... INCLUDING ...) تم إنشاؤه بناءً على قالب الجدول الأصلي القسم مع تقييد في التاريخ المطلوببحيث أنه عند استرجاع البيانات تتم القراءة فيها فقط.

PG10: المحاولة الأولى

لكن التقسيم عبر الوراثة لم يكن تاريخيًا مناسبًا للتعامل مع تدفق الكتابة النشط أو عدد كبير من الأقسام الفرعية. على سبيل المثال، يمكنك أن تتذكر أن الخوارزمية الخاصة باختيار القسم المطلوب كانت موجودة التعقيد التربيعيأنه يعمل مع أكثر من 100 قسم، أنت نفسك تفهم كيف...

في PG10 تم تحسين هذا الوضع بشكل كبير من خلال تنفيذ الدعم التقسيم الأصلي. لذلك، حاولنا تطبيقه فورًا بعد ترحيل وحدة التخزين، ولكن...

كما اتضح بعد البحث في الدليل، فإن الجدول المقسم أصلاً في هذا الإصدار هو:

  • لا يدعم أوصاف الفهرس
  • لا يدعم المشغلات عليه
  • لا يمكن أن يكون "سليل" أي شخص
  • لا تدعم INSERT ... ON CONFLICT
  • لا يمكن إنشاء قسم تلقائيًا

بعد أن تلقينا ضربة مؤلمة على الجبهة مع أشعل النار، أدركنا أنه سيكون من المستحيل القيام به دون تعديل التطبيق، وتأجيل مزيد من البحث لمدة ستة أشهر.

PG10: الفرصة الثانية

لذلك، بدأنا في حل المشاكل التي ظهرت واحدة تلو الأخرى:

  1. لأن المشغلات و ON CONFLICT ووجدنا أننا لا نزال بحاجة إليها هنا وهناك، لذلك قمنا بمرحلة متوسطة للعمل عليها جدول الوكيل.
  2. تخلصت من "التوجيه" في المشغلات - أي من EXECUTE.
  3. لقد أخرجوها بشكل منفصل جدول القالب مع كافة الفهارسبحيث لا تكون موجودة حتى في جدول الوكيل.

نكتب في PostgreSQL على sublight: مضيف واحد ، يوم واحد ، 1 تيرابايت
أخيرًا، بعد كل هذا، قمنا بتقسيم الجدول الرئيسي محليًا. لا يزال إنشاء قسم جديد متروكًا لضمير التطبيق.

قواميس "النشر".

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

لقد تم بالفعل تقسيم "الحقائق" يوميًا لفترة طويلة، لذلك قمنا بحذف الأقسام القديمة بهدوء، ولم تزعجنا (السجلات!). لكن كانت هناك مشكلة في القواميس..

لا يعني أن هناك الكثير منهم، ولكن تقريبا 100 تيرابايت من "الحقائق" نتج عنها قاموس سعة 2.5 تيرابايت. لا يمكنك حذف أي شيء من هذا الجدول بسهولة، ولا يمكنك ضغطه في الوقت المناسب، وأصبحت الكتابة فيه أبطأ تدريجيًا.

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

  • الكتابة / القراءة بشكل أسرع بسبب حجم القسم الأصغر
  • تستهلك ذاكرة أقل من خلال العمل مع فهارس أكثر إحكاما
  • تخزين بيانات أقل بسبب القدرة على إزالة التي عفا عليها الزمن بسرعة

نتيجة لمجموعة كاملة من التدابير انخفض حمل وحدة المعالجة المركزية بنسبة ~30%، وتحميل القرص بنسبة ~50%:

نكتب في PostgreSQL على sublight: مضيف واحد ، يوم واحد ، 1 تيرابايت
وفي الوقت نفسه، واصلنا كتابة نفس الشيء تمامًا في قاعدة البيانات، ولكن مع تحميل أقل.

#2. تطور قاعدة البيانات وإعادة البناء

لذلك استقرنا على ما لدينا كل يوم له قسم خاص به مع البيانات. في الحقيقة، CHECK (dt = '2018-10-12'::date) - ويوجد مفتاح التقسيم وشرط سقوط السجل في قسم معين.

وبما أن جميع التقارير في خدمتنا مبنية في سياق تاريخ محدد، فإن الفهارس الخاصة بها منذ "الأوقات غير المقسمة" كانت بجميع أنواعها (الخادم، تاريخ، نموذج الخطة), (الخادم، تاريخ، عقدة الخطة), (تاريخ، فئة الخطأ، الخادم)، ...

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

نكتب في PostgreSQL على sublight: مضيف واحد ، يوم واحد ، 1 تيرابايت
اتجاه التحسين واضح - بسيط قم بإزالة حقل التاريخ من كافة الفهارس على جداول مقسمة. وبالنظر إلى أحجامنا، فإن المكاسب تكون على وشك 1 تيرابايت/أسبوع!

الآن دعونا نلاحظ أنه لا يزال يتعين تسجيل هذا التيرابايت بطريقة ما. وهذا هو، نحن أيضا يجب الآن تحميل القرص بشكل أقل! وهذه الصورة توضح بوضوح التأثير الذي تم الحصول عليه من عملية التنظيف التي خصصنا لها أسبوعا:

نكتب في PostgreSQL على sublight: مضيف واحد ، يوم واحد ، 1 تيرابايت

#3. "توزيع" حمل الذروة

واحدة من المشاكل الكبيرة للأنظمة المحملة هي مزامنة زائدة عن الحاجة بعض العمليات التي لا تتطلب ذلك. في بعض الأحيان "لأنهم لم يلاحظوا ذلك"، وأحيانًا "كان الأمر أسهل بهذه الطريقة"، ولكن عليك التخلص منه عاجلاً أم آجلاً.

لنقم بتكبير الصورة السابقة ونرى أن لدينا قرصًا "المضخات" تحت الحمل بسعة مضاعفة بين العينات المتجاورة، وهو ما لا ينبغي أن يحدث "إحصائيًا" مع مثل هذا العدد من العمليات:

نكتب في PostgreSQL على sublight: مضيف واحد ، يوم واحد ، 1 تيرابايت

من السهل جدًا تحقيق ذلك. لقد بدأنا بالفعل في المراقبة ما يقرب من 1000 خادم، تتم معالجة كل منها بواسطة مؤشر ترابط منطقي منفصل، ويقوم كل مؤشر ترابط بإعادة تعيين المعلومات المتراكمة ليتم إرسالها إلى قاعدة البيانات بتردد معين، شيء من هذا القبيل:

setInterval(sendToDB, interval)

المشكلة هنا تكمن بالتحديد في حقيقة ذلك تبدأ جميع المواضيع في نفس الوقت تقريبًا، لذا فإن أوقات إرسالها تتطابق دائمًا تقريبًا "في صلب الموضوع". عفوا رقم 2...

ولحسن الحظ، من السهل جدًا إصلاح هذا الأمر، إضافة تشغيل "عشوائي". حسب الوقت:

setInterval(sendToDB, interval * (1 + 0.1 * (Math.random() - 0.5)))

#4. نقوم بتخزين ما نحتاجه

مشكلة التحميل العالي التقليدية الثالثة هي لا يوجد ذاكرة تخزين مؤقت أين هو استطاع أن تكون.

على سبيل المثال، جعلنا من الممكن التحليل من حيث عقد الخطة (كل هذه Seq Scan on users) ، لكنهم يعتقدون على الفور أنهم متماثلون في الغالب - لقد نسوا.

لا، بالطبع، لم تتم كتابة أي شيء في قاعدة البيانات مرة أخرى، وهذا يقطع الزناد INSERT ... ON CONFLICT DO NOTHING. لكن هذه البيانات لا تزال تصل إلى قاعدة البيانات، وهي غير ضرورية القراءة للتحقق من الصراع يجب عمله. عفوا رقم 3...

الفرق في عدد السجلات المرسلة إلى قاعدة البيانات قبل/بعد تمكين التخزين المؤقت واضح:

نكتب في PostgreSQL على sublight: مضيف واحد ، يوم واحد ، 1 تيرابايت

وهذا هو الانخفاض المصاحب في حمل التخزين:

نكتب في PostgreSQL على sublight: مضيف واحد ، يوم واحد ، 1 تيرابايت

في المجموع

"تيرابايت في اليوم" يبدو مخيفًا. إذا فعلت كل شيء بشكل صحيح، فهذا عادل 2^40 بايت / 86400 ثانية = ~12.5 ميجابايت/ثانيةأنه حتى مسامير IDE سطح المكتب عقدت. 🙂

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

نكتب في PostgreSQL على sublight: مضيف واحد ، يوم واحد ، 1 تيرابايت

المصدر: www.habr.com

إضافة تعليق