Messenger ma'lumotlar bazasi (2-qism): "foyda uchun" bo'linish

Biz yozishmalarni saqlash uchun PostgreSQL ma'lumotlar bazasi tuzilmasini muvaffaqiyatli loyihalashtirdik, bir yil o'tdi, foydalanuvchilar uni faol ravishda to'ldirishmoqda va endi u o'z ichiga oladi millionlab yozuvlar, va... nimadir sekinlasha boshladi.

Messenger ma'lumotlar bazasi (2-qism): "foyda uchun" bo'linish
Gap shundaki Jadval hajmi oshgani sayin indekslarning "chuqurligi" oshadi. - logarifmik bo'lsa ham. Ammo vaqt o'tishi bilan bu serverni bir xil o'qish/yozish vazifalarini bajarishga majbur qiladi ko'p marta ko'proq ma'lumotlarni qayta ishlashboshidan ko'ra.

Bu erda qutqarish kerak qismlarga ajratish.

Shuni ta'kidlashim kerakki, biz sharding, ya'ni turli ma'lumotlar bazalari yoki serverlar o'rtasida ma'lumotlarni tarqatish haqida gapirmayapmiz. Chunki hatto ma'lumotlarni bo'lish bir nechta serverlarda vaqt o'tishi bilan indekslarning "shishishi" muammosidan xalos bo'lmaysiz. Agar siz har kuni yangi serverni ishga tushirish imkoniga ega bo'lsangiz, muammolaringiz endi ma'lum bir ma'lumotlar bazasi tekisligida yotmasligi aniq.

Biz "apparatda" bo'linishni amalga oshirish uchun maxsus skriptlarni emas, balki yondashuvning o'zi - nima va qanday qilib "bo'laklarga bo'linish" kerakligini va bunday istak nimaga olib kelishini ko'rib chiqamiz.

Kontseptsiya

Maqsadimizni yana bir bor aniqlaymiz: biz bugun, ertaga va bir yil ichida PostgreSQL tomonidan har qanday o'qish/yozish operatsiyasi davomida o'qilgan ma'lumotlar miqdori taxminan bir xil bo'lib qolishiga ishonch hosil qilmoqchimiz.

Har qanday uchun xronologik tarzda to'plangan ma'lumotlar (xabarlar, hujjatlar, jurnallar, arxivlar, ...) bo'lim kaliti sifatida tabiiy tanlovdir voqea sanasi/vaqti. Bizning holatda, bunday hodisa xabar yuborish vaqti.

E'tibor bering, foydalanuvchilar deyarli har doim faqat "oxirgi"lar bilan ishlang bunday ma'lumotlar - ular eng so'nggi xabarlarni o'qiydilar, so'nggi jurnallarni tahlil qiladilar, ... Yo'q, albatta, ular o'z vaqtida orqaga qaytishlari mumkin, lekin ular buni juda kamdan-kam qiladilar.

Ushbu cheklovlardan ko'rinib turibdiki, eng maqbul xabar echimi bo'ladi "kundalik" bo'limlari - axir, bizning foydalanuvchi deyarli har doim unga "bugun" yoki "kecha" kelganini o'qiydi.

Agar biz kun davomida deyarli bir qismda yozsak va o'qisak, bu bizga ham beradi xotira va diskdan yanada samarali foydalanish - chunki barcha bo'lim indekslari jadvaldagi "katta va yog'li"lardan farqli o'laroq, RAMga osongina mos keladi.

bosqichma-bosqich

Umuman olganda, yuqorida aytilganlarning barchasi doimiy daromadga o'xshaydi. Va bunga erishish mumkin, lekin buning uchun biz ko'p harakat qilishimiz kerak - chunki ob'ektlardan birini bo'lish to'g'risidagi qaror bog'langanlarni "ko'rish" zaruratiga olib keladi.

Xabar, uning xossalari va proyeksiyalari

Biz xabarlarni sanalar bo'yicha qisqartirishga qaror qilganimiz sababli, ularga bog'liq bo'lgan ob'ektlarni-xususiyatlarni (biriktirilgan fayllar, qabul qiluvchilar ro'yxati) va shuningdek xabarning sanasi bo'yicha.

Bizning odatiy vazifalarimizdan biri xabarlar registrlarini (o'qilmagan, kiruvchi, barchasi) aniq ko'rish bo'lganligi sababli, ularni xabar sanalari bo'yicha bo'lish uchun "tortish" ham mantiqan to'g'ri keladi.

Messenger ma'lumotlar bazasi (2-qism): "foyda uchun" bo'linish

Biz bo'lim kalitini (xabar sanasi) barcha jadvallarga qo'shamiz: qabul qiluvchilar, fayl, registrlar. Siz uni xabarning o'ziga qo'shishingiz shart emas, lekin mavjud DateTime-dan foydalaning.

mavzular

Bir nechta xabarlar uchun faqat bitta mavzu mavjud bo'lganligi sababli, uni bir xil modelda "kesish" mumkin emas, siz boshqa narsaga tayanishingiz kerak. Bizning holatlarimizda bu ideal yozishmalardagi birinchi xabarning sanasi β€” ya’ni, mavzuning yaratilish momenti, aslida.

Messenger ma'lumotlar bazasi (2-qism): "foyda uchun" bo'linish

Barcha jadvallarga bo'linish kalitini (mavzu sanasi) qo'shing: mavzu, ishtirokchi.

Ammo endi bizda bir vaqtning o'zida ikkita muammo bor:

  • Mavzu bo'yicha xabarlarni qaysi bo'limda izlashim kerak?
  • Xabardagi mavzuni qaysi bo'limda izlashim kerak?

Biz, albatta, barcha bo'limlarda qidirishni davom ettirishimiz mumkin, ammo bu juda achinarli bo'ladi va barcha yutuqlarimizni inkor etadi. Shuning uchun, aniq qaerga qarash kerakligini bilish uchun biz bo'limlarga mantiqiy havolalar/ko'rsatkichlar qilamiz:

  • xabarga qo'shamiz mavzu sana maydoni
  • mavzuga qo'shamiz xabar sanasi belgilandi bu yozishmalar (alohida jadval yoki sanalar massivi bo'lishi mumkin)

Messenger ma'lumotlar bazasi (2-qism): "foyda uchun" bo'linish

Har bir shaxsiy yozishmalar uchun xabarlar sanalari ro'yxatiga bir nechta o'zgartirishlar kiritilishi sababli (oxir-oqibat, deyarli barcha xabarlar 1-2 qo'shni kunga to'g'ri keladi), men ushbu variantga e'tibor qarataman.

Umuman olganda, ma'lumotlar bazasining tuzilishi bo'linishni hisobga olgan holda quyidagi shaklni oldi:

Jadvallar: RU, agar siz jadval/maydon nomlarida kirill alifbosidan nafratlansangiz, qaramaslik yaxshiroqdir.

-- сСкции ΠΏΠΎ Π΄Π°Ρ‚Π΅ сообщСния
CREATE TABLE "Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅_YYYYMMDD"(
  "Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅"
    uuid
      PRIMARY KEY
, "Π’Π΅ΠΌΠ°"
    uuid
, "Π”Π°Ρ‚Π°Π’Π΅ΠΌΡ‹"
    date
, "Автор"
    uuid
, "ДатаВрСмя" -- ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ ΠΊΠ°ΠΊ Π΄Π°Ρ‚Ρƒ
    timestamp
, "ВСкст"
    text
);

CREATE TABLE "АдрСсат_YYYYMMDD"(
  "ДатаБообщСния"
    date
, "Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅"
    uuid
, "ΠŸΠ΅Ρ€ΡΠΎΠ½Π°"
    uuid
, PRIMARY KEY("Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅", "ΠŸΠ΅Ρ€ΡΠΎΠ½Π°")
);

CREATE TABLE "Π€Π°ΠΉΠ»_YYYYMMDD"(
  "ДатаБообщСния"
    date
, "Π€Π°ΠΉΠ»"
    uuid
      PRIMARY KEY
, "Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅"
    uuid
, "BLOB"
    uuid
, "Имя"
    text
);

CREATE TABLE "РССстрБообщСний_YYYYMMDD"(
  "ДатаБообщСния"
    date
, "Π’Π»Π°Π΄Π΅Π»Π΅Ρ†"
    uuid
, "ВипРССстра"
    smallint
, "ДатаВрСмя"
    timestamp
, "Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅"
    uuid
, PRIMARY KEY("Π’Π»Π°Π΄Π΅Π»Π΅Ρ†", "ВипРССстра", "Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅")
);
CREATE INDEX ON "РССстрБообщСний_YYYYMMDD"("Π’Π»Π°Π΄Π΅Π»Π΅Ρ†", "ВипРССстра", "ДатаВрСмя" DESC);

-- сСкции ΠΏΠΎ Π΄Π°Ρ‚Π΅ Ρ‚Π΅ΠΌΡ‹
CREATE TABLE "Π’Π΅ΠΌΠ°_YYYYMMDD"(
  "Π”Π°Ρ‚Π°Π’Π΅ΠΌΡ‹"
    date
, "Π’Π΅ΠΌΠ°"
    uuid
      PRIMARY KEY
, "Π”ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚"
    uuid
, "НазваниС"
    text
);

CREATE TABLE "УчастникВСмы_YYYYMMDD"(
  "Π”Π°Ρ‚Π°Π’Π΅ΠΌΡ‹"
    date
, "Π’Π΅ΠΌΠ°"
    uuid
, "ΠŸΠ΅Ρ€ΡΠΎΠ½Π°"
    uuid
, PRIMARY KEY("Π’Π΅ΠΌΠ°", "ΠŸΠ΅Ρ€ΡΠΎΠ½Π°")
);

CREATE TABLE "Π”Π°Ρ‚Ρ‹Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠΉΠ’Π΅ΠΌΡ‹_YYYYMMDD"(
  "Π”Π°Ρ‚Π°Π’Π΅ΠΌΡ‹"
    date
, "Π’Π΅ΠΌΠ°"
    uuid
      PRIMARY KEY
, "Π”Π°Ρ‚Π°"
    date
);

Yaxshi tiyin tejang

Xo'sh, agar foydalanmasangiz nima bo'ladi klassik bo'linish varianti maydon qiymatlarini taqsimlash asosida (triggerlar va meros orqali yoki PARTITION BY orqali) va dastur darajasida "qo'lda" bo'lish kalitining qiymati allaqachon jadval nomida saqlanganligini sezasiz.

Shunday ekan, agar shunday bo'lsangiz Siz saqlangan ma'lumotlar miqdori haqida juda xavotirdamisiz?, keyin siz ushbu "qo'shimcha" maydonlardan xalos bo'lishingiz va maxsus jadvallarga murojaat qilishingiz mumkin. To'g'ri, bu holda bir nechta bo'limlardan barcha tanlovlar dastur tomoniga o'tkazilishi kerak.

Manba: www.habr.com

a Izoh qo'shish