ما در PostgreSQL در زیر نور می نویسیم: 1 میزبان، 1 روز، 1 ترابایت

اخیراً با استفاده از دستور العمل های استاندارد به شما گفتم چگونه افزایش عملکرد پرس و جوهای خواندنی SQL از پایگاه داده PostgreSQL. امروز در مورد چگونگی صحبت خواهیم کرد ضبط را می توان کارآمدتر انجام داد در پایگاه داده بدون استفاده از "پیچ و خم" در پیکربندی - به سادگی با سازماندهی صحیح جریان های داده.

ما در PostgreSQL در زیر نور می نویسیم: 1 میزبان، 1 روز، 1 ترابایت

#1. بخش بندی

مقاله ای در مورد اینکه چگونه و چرا ارزش سازماندهی دارد پارتیشن بندی کاربردی "در تئوری" قبلاً بوده است، در اینجا ما در مورد تمرین اعمال برخی رویکردها در داخل خود صحبت خواهیم کرد سرویس نظارت برای صدها سرور PostgreSQL.

"چیزهای روزهای گذشته..."

در ابتدا، مانند هر MVP، پروژه ما تحت یک بار نسبتاً سبک شروع شد - نظارت فقط برای ده سرور مهم انجام شد، همه جداول نسبتا فشرده بودند ... اما با گذشت زمان، تعداد میزبان های نظارت شده بیشتر و بیشتر شد. ، و یک بار دیگر سعی کردیم با یکی از آنها کاری انجام دهیم میزهای 1.5 ترابایتی، متوجه شدیم که اگرچه امکان ادامه زندگی به این شکل وجود دارد، اما بسیار ناخوشایند است.

زمان‌ها تقریباً مانند زمان‌های حماسی بود، نسخه‌های مختلف PostgreSQL 9.x مرتبط بودند، بنابراین تمام پارتیشن‌بندی‌ها باید به صورت دستی انجام می‌شد - از طریق وراثت جدول و محرک ها مسیریابی با پویا EXECUTE.

ما در PostgreSQL در زیر نور می نویسیم: 1 میزبان، 1 روز، 1 ترابایت
راه حل به دست آمده به اندازه کافی جهانی است که می تواند به همه جداول ترجمه شود:

  • یک جدول والد «هدر» خالی اعلام شد که همه را توضیح می داد شاخص ها و محرک های لازم.
  • رکورد از دیدگاه مشتری در جدول "ریشه" و با استفاده داخلی ساخته شده است ماشه مسیریابی BEFORE INSERT رکورد "فیزیکی" در بخش مورد نیاز درج شد. اگر هنوز چنین چیزی وجود نداشت، یک استثنا گرفتیم و ...
  • … با استفاده از CREATE TABLE ... (LIKE ... INCLUDING ...) بر اساس الگوی جدول والد ایجاد شد بخش با محدودیت در تاریخ مورد نظربه طوری که هنگام بازیابی داده ها، خواندن فقط در آن انجام می شود.

PG10: اولین تلاش

اما پارتیشن بندی از طریق وراثت از نظر تاریخی برای مقابله با یک جریان نوشتن فعال یا تعداد زیادی پارتیشن فرزند مناسب نبوده است. به عنوان مثال، می توانید به یاد بیاورید که الگوریتم انتخاب بخش مورد نیاز بود پیچیدگی درجه دوم، که با 100+ بخش کار می کند، خود شما می فهمید که چگونه ...

در PG10 این وضعیت تا حد زیادی با اجرای پشتیبانی بهینه شد پارتیشن بندی بومی. بنابراین بلافاصله پس از مهاجرت ذخیره سازی بلافاصله سعی کردیم آن را اعمال کنیم، اما ...

همانطور که پس از بررسی کتابچه راهنمای کاربر مشخص شد، جدول پارتیشن بندی شده بومی در این نسخه به صورت زیر است:

  • توضیحات شاخص را پشتیبانی نمی کند
  • از محرک های روی آن پشتیبانی نمی کند
  • نمی تواند "از نسل" کسی باشد
  • پشتیبانی نمی کند INSERT ... ON CONFLICT
  • نمی تواند به طور خودکار یک بخش ایجاد کند

پس از دریافت ضربه دردناک به پیشانی با چنگک، متوجه شدیم که انجام آن بدون اصلاح برنامه غیرممکن است و تحقیقات بیشتر را به مدت شش ماه به تعویق انداختیم.

PG10: شانس دوم

بنابراین، ما شروع به حل مشکلات پیش آمده یکی یکی کردیم:

  1. زیرا محرک ها و ON CONFLICT ما متوجه شدیم که هنوز اینجا و آنجا به آنها نیاز داریم، بنابراین یک مرحله میانی برای کار کردن آنها ایجاد کردیم جدول پروکسی.
  2. خلاص شدن از شر "مسیریابی" در محرک ها - یعنی از EXECUTE.
  3. جداگانه بیرون آوردند جدول قالب با تمام نمایه هابه طوری که حتی در جدول پروکسی هم حضور ندارند.

ما در PostgreSQL در زیر نور می نویسیم: 1 میزبان، 1 روز، 1 ترابایت
بالاخره بعد از همه اینها جدول اصلی را به صورت بومی پارتیشن بندی کردیم. ایجاد یک بخش جدید هنوز به وجدان برنامه واگذار شده است.

فرهنگ لغت "اره".

همانطور که در هر سیستم تحلیلی، ما نیز داشتیم "حقایق" و "برش" (لغت نامه ها). در مورد ما، آنها در این مقام عمل کردند، به عنوان مثال، بدنه قالب پرس و جوهای کند مشابه یا متن خود پرس و جو.

"حقایق" برای مدت طولانی در روز تقسیم می شد ، بنابراین ما با آرامش بخش های قدیمی را حذف کردیم و آنها ما را اذیت نکردند (لاگ ها!). اما دیکشنری ها مشکل داشت...

نه اینکه بگویم تعدادشان زیاد بود، اما تقریباً 100 ترابایت "حقایق" منجر به یک فرهنگ لغت 2.5 ترابایتی شد. شما نمی توانید به راحتی چیزی را از چنین جدولی حذف کنید، نمی توانید آن را در زمان کافی فشرده کنید و نوشتن روی آن به تدریج کندتر شد.

مثل یک فرهنگ لغت... در آن، هر مدخل دقیقاً یک بار ارائه شود... و این درست است، اما!.. هیچکس مانع از داشتن ما نمی شود. یک فرهنگ لغت جداگانه برای هر روز! بله، این افزونگی خاصی را به همراه دارد، اما اجازه می دهد:

  • سریعتر بنویسید/بخوانید به دلیل اندازه بخش کوچکتر
  • حافظه کمتری مصرف کند با کار با شاخص های فشرده تر
  • داده های کمتری ذخیره کنید به دلیل توانایی حذف سریع قدیمی

در نتیجه کل مجموعه اقدامات بار CPU ~ 30٪ کاهش یافته است، بار دیسک تا ~ 50٪:

ما در PostgreSQL در زیر نور می نویسیم: 1 میزبان، 1 روز، 1 ترابایت
در همان زمان، ما به نوشتن دقیقاً همان چیزی در پایگاه داده ادامه دادیم، فقط با بار کمتر.

#2. تکامل و بازسازی پایگاه داده

بنابراین ما به آنچه که داریم راضی شدیم هر روز بخش مخصوص به خود را دارد با داده ها در حقیقت، CHECK (dt = '2018-10-12'::date) - و یک کلید پارتیشن بندی و شرط قرار گرفتن یک رکورد در یک بخش خاص وجود دارد.

از آنجایی که همه گزارش‌های موجود در سرویس ما بر اساس یک تاریخ خاص ساخته می‌شوند، شاخص‌های مربوط به آن‌ها از «زمان‌های تقسیم‌بندی نشده» همه نوع بوده‌اند. (سرور، تاریخ، الگوی طرح), (سرور، تاریخ، گره پلان), (تاریخ، کلاس خطا، سرور)، ...

اما اکنون آنها در هر بخش زندگی می کنند کپی های شما هر یک از این شاخص ها... و در هر بخش تاریخ ثابت است... معلوم می شود که اکنون در هر یک از این شاخص ها هستیم به سادگی یک ثابت را وارد کنید به عنوان یکی از فیلدها که هم حجم آن را افزایش می دهد و هم زمان جستجوی آن را افزایش می دهد اما نتیجه ای در بر ندارد. چنگک را به حال خود رها کردند، اوف...

ما در PostgreSQL در زیر نور می نویسیم: 1 میزبان، 1 روز، 1 ترابایت
جهت بهینه سازی واضح است - ساده فیلد تاریخ را از همه فهرست ها حذف کنید روی جداول پارتیشن بندی شده با توجه به حجم ما، سود در حدود است 1 ترابایت در هفته!

حال توجه داشته باشیم که این ترابایت هنوز باید به نحوی ضبط می شد. یعنی ما هم دیسک اکنون باید کمتر بارگیری شود! این تصویر به وضوح تأثیر به دست آمده از تمیز کردن را نشان می دهد که ما یک هفته را به آن اختصاص دادیم:

ما در PostgreSQL در زیر نور می نویسیم: 1 میزبان، 1 روز، 1 ترابایت

#3. "گسترش" اوج بار

یکی از مشکلات بزرگ سیستم های بارگذاری شده است همگام سازی اضافی برخی عملیات که نیازی به آن ندارند. گاهی اوقات "چون آنها متوجه نشدند"، گاهی اوقات "اینطور راحت تر بود"، اما دیر یا زود باید از شر آن خلاص شوید.

بیایید روی تصویر قبلی زوم کنیم و ببینیم که یک دیسک داریم "پمپ" در زیر بار با دامنه دو برابر بین نمونه های مجاور، که به وضوح "آماری" نباید با چنین تعدادی از عملیات اتفاق بیفتد:

ما در PostgreSQL در زیر نور می نویسیم: 1 میزبان، 1 روز، 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 در زیر نور می نویسیم: 1 میزبان، 1 روز، 1 ترابایت

و این کاهش بار ذخیره سازی همراه است:

ما در PostgreSQL در زیر نور می نویسیم: 1 میزبان، 1 روز، 1 ترابایت

در کل

"ترابایت در روز" فقط ترسناک به نظر می رسد. اگر همه چیز را درست انجام دهید، پس این درست است 2^40 بایت / 86400 ثانیه = ~ 12.5 مگابایت بر ثانیهکه حتی پیچ های IDE دسکتاپ را نگه می داشتند. 🙂

اما به طور جدی، حتی با ده برابر "انحراف" بار در طول روز، می توانید به راحتی توانایی های SSD های مدرن را برآورده کنید.

ما در PostgreSQL در زیر نور می نویسیم: 1 میزبان، 1 روز، 1 ترابایت

منبع: www.habr.com

اضافه کردن نظر