Rishkruajeni bazën e të dhënave të mesazheve VKontakte nga e para dhe mbijetoni

Përdoruesit tanë i shkruajnë mesazhe njëri-tjetrit pa e ditur lodhjen.
Rishkruajeni bazën e të dhënave të mesazheve VKontakte nga e para dhe mbijetoni
Kjo është shumë. Nëse vendosni të lexoni të gjitha mesazhet e të gjithë përdoruesve, do të duheshin më shumë se 150 mijë vjet. Me kusht që të jeni një lexues mjaft i avancuar dhe të shpenzoni jo më shumë se një sekondë për çdo mesazh.

Me një vëllim të tillë të dhënash, është kritike që logjika për ruajtjen dhe aksesin në to të ndërtohet në mënyrë optimale. Përndryshe, në një moment jo shumë të mrekullueshëm, mund të bëhet e qartë se çdo gjë së shpejti do të shkojë keq.

Për ne ky moment erdhi një vit e gjysmë më parë. Si arritëm në këtë dhe çfarë ndodhi në fund - ju tregojmë me radhë.

epikrizë

Në zbatimin e parë, mesazhet VKontakte funksionuan në një kombinim të backend PHP dhe MySQL. Kjo është një zgjidhje krejtësisht normale për një uebsajt të vogël studentor. Sidoqoftë, kjo faqe u rrit në mënyrë të pakontrolluar dhe filloi të kërkojë optimizimin e strukturave të të dhënave për vete.

Në fund të vitit 2009, u shkrua depoja e parë e motorit të tekstit, dhe në 2010 mesazhet u transferuan në të.

Në motorin e tekstit, mesazhet ruheshin në lista - një lloj "kuti postare". Çdo listë e tillë përcaktohet nga një uid - përdoruesi që zotëron të gjitha këto mesazhe. Një mesazh ka një sërë atributesh: identifikues i bashkëbiseduesit, tekst, bashkëngjitje etj. Identifikuesi i mesazhit brenda "kutisë" është local_id, ai nuk ndryshon kurrë dhe caktohet në mënyrë sekuenciale për mesazhe të reja. "Kutitë" janë të pavarura dhe nuk janë të sinkronizuara me njëra-tjetrën brenda motorit; komunikimi midis tyre ndodh në nivelin PHP. Ju mund të shikoni strukturën e të dhënave dhe aftësitë e motorit të tekstit nga brenda këtu.
Rishkruajeni bazën e të dhënave të mesazheve VKontakte nga e para dhe mbijetoni
Kjo ishte mjaft e mjaftueshme për korrespondencën midis dy përdoruesve. Mendoni se çfarë ndodhi më pas?

Në maj 2011, VKontakte prezantoi biseda me disa pjesëmarrës - multi-chat. Për të punuar me ta, ne ngritëm dy grupime të reja - anëtarë-chats dhe chat-anëtarë. E para ruan të dhëna rreth bisedave nga përdoruesit, e dyta ruan të dhëna për përdoruesit sipas bisedave. Përveç vetë listave, kjo përfshin, për shembull, përdoruesin ftues dhe kohën kur u shtuan në bisedë.

"PHP, le të dërgojmë një mesazh në chat," thotë përdoruesi.
"Hajde, {username}", thotë PHP.
Rishkruajeni bazën e të dhënave të mesazheve VKontakte nga e para dhe mbijetoni
Ka disavantazhe në këtë skemë. Sinkronizimi është ende përgjegjësi e PHP. Bisedat e mëdha dhe përdoruesit që u dërgojnë mesazhe njëkohësisht janë një histori e rrezikshme. Meqenëse shembulli i motorit të tekstit varet nga uid, pjesëmarrësit e bisedës mund të marrin të njëjtin mesazh në kohë të ndryshme. Dikush mund të jetojë me këtë nëse përparimi do të ndalej. Por kjo nuk do të ndodhë.

Në fund të vitit 2015, ne lëshuam mesazhet e komunitetit dhe në fillim të 2016, lançuam një API për ta. Me ardhjen e chatbot-eve të mëdha në komunitete, ishte e mundur të harronim shpërndarjen e ngarkesës.

Një bot i mirë gjeneron disa milionë mesazhe në ditë - edhe përdoruesit më llafazan nuk mund të mburren me këtë. Kjo do të thotë se disa raste të motorit të tekstit, në të cilin kanë jetuar robotë të tillë, filluan të vuajnë në maksimum.

Motorët e mesazheve në 2016 janë 100 raste të anëtarëve të bisedës dhe bisedave të anëtarëve dhe 8000 motorë teksti. Ata u strehuan në një mijë serverë, secili me 64 GB memorie. Si masë e parë emergjente, e rritëm memorien me 32 GB të tjera. Ne vlerësuam parashikimet. Pa ndryshime drastike, kjo do të mjaftonte edhe për një vit tjetër. Ju duhet ose të kapni harduerin ose të optimizoni vetë bazat e të dhënave.

Për shkak të natyrës së arkitekturës, ka kuptim vetëm të rritet hardueri në shumëfish. Kjo do të thotë, të paktën dyfishimi i numrit të makinave - padyshim, kjo është një rrugë mjaft e shtrenjtë. Ne do të optimizojmë.

Koncept i ri

Thelbi qendror i qasjes së re është biseda. Një bisedë ka një listë të mesazheve që lidhen me të. Përdoruesi ka një listë të bisedave.

Minimumi i kërkuar është dy baza të reja të dhënash:

  • chat-motor. Ky është një depo e vektorëve të bisedës. Çdo bisedë ka një vektor mesazhesh që lidhen me të. Çdo mesazh ka një tekst dhe një identifikues unik të mesazhit brenda bisedës - chat_local_id.
  • motor-përdorues. Ky është një ruajtje e vektorëve të përdoruesve - lidhje me përdoruesit. Çdo përdorues ka një vektor të peer_id (bashkëbiseduesit - përdorues të tjerë, multi-chat ose komunitete) dhe një vektor mesazhesh. Çdo peer_id ka një vektor mesazhesh që lidhen me të. Çdo mesazh ka një chat_local_id dhe një ID unike mesazhi për atë përdorues - user_local_id.

Rishkruajeni bazën e të dhënave të mesazheve VKontakte nga e para dhe mbijetoni
Grupet e reja komunikojnë me njëri-tjetrin duke përdorur TCP - kjo siguron që rendi i kërkesave të mos ndryshojë. Vetë kërkesat dhe konfirmimet për to regjistrohen në hard disk - kështu që ne mund të rivendosim gjendjen e radhës në çdo kohë pas një dështimi ose rifillimi të motorit. Meqenëse motori i përdoruesit dhe motori i bisedës janë 4 mijë copë secila, radha e kërkesave midis grupeve do të shpërndahet në mënyrë të barabartë (por në realitet nuk ka fare - dhe funksionon shumë shpejt).

Puna me diskun në bazat tona të të dhënave në shumicën e rasteve bazohet në një kombinim të një regjistri binar të ndryshimeve (binlog), fotografi statike dhe një imazh të pjesshëm në memorie. Ndryshimet gjatë ditës shkruhen në një bilog dhe krijohet periodikisht një pamje e gjendjes aktuale. Një fotografi është një koleksion i strukturave të të dhënave të optimizuara për qëllimet tona. Ai përbëhet nga një kokë (metaindeksi i imazhit) dhe një grup metafilash. Titulli ruhet përgjithmonë në RAM dhe tregon se ku të kërkohen të dhënat nga fotografia. Çdo metafile përfshin të dhëna që ka të ngjarë të nevojiten në momente të ngushta në kohë - për shembull, të lidhura me një përdorues të vetëm. Kur kërkoni bazën e të dhënave duke përdorur kokën e fotografisë së çastit, lexohet metasfili i kërkuar dhe më pas merren parasysh ndryshimet në binlog që ndodhën pas krijimit të fotografisë. Ju mund të lexoni më shumë rreth përfitimeve të kësaj qasjeje këtu.

Në të njëjtën kohë, të dhënat në vetë hard diskun ndryshojnë vetëm një herë në ditë - natën vonë në Moskë, kur ngarkesa është minimale. Falë kësaj (duke ditur që struktura në disk është konstante gjatë gjithë ditës), ne mund të përballojmë zëvendësimin e vektorëve me grupe të një madhësie fikse - dhe për shkak të kësaj, të fitojmë memorie.

Dërgimi i një mesazhi në skemën e re duket si kjo:

  1. Backend-i PHP kontakton motorin e përdoruesit me një kërkesë për të dërguar një mesazh.
  2. motor-user proxies kërkesën për shembullin e dëshiruar chat-motor, i cili kthehet në user-engine chat_local_id - një identifikues unik i një mesazhi të ri brenda këtij chat. chat_engine më pas transmeton mesazhin te të gjithë marrësit në chat.
  3. user-engine merr chat_local_id nga chat-engine dhe kthen user_local_id në PHP - një identifikues unik mesazhi për këtë përdorues. Ky identifikues përdoret më pas, për shembull, për të punuar me mesazhe nëpërmjet API-së.

Rishkruajeni bazën e të dhënave të mesazheve VKontakte nga e para dhe mbijetoni
Por përveç dërgimit të mesazheve, ju duhet të zbatoni disa gjëra më të rëndësishme:

  • Nënlistat janë, për shembull, mesazhet më të fundit që shihni kur hapni listën e bisedave. Mesazhe të palexuara, mesazhe me etiketa ("E rëndësishme", "Spam", etj.).
  • Kompresimi i mesazheve në motorin e bisedës
  • Memoria e mesazheve në motorin e përdoruesit
  • Kërko (nëpër të gjitha dialogët dhe brenda një të caktuar).
  • Përditësimi në kohë reale (Longpolling).
  • Ruajtja e historisë për të zbatuar caching në klientët celularë.

Të gjitha nënlistat janë struktura që ndryshojnë me shpejtësi. Për të punuar me ta ne përdorim Spërkatni pemët. Kjo zgjedhje shpjegohet me faktin se në krye të pemës ne ndonjëherë ruajmë një segment të tërë mesazhesh nga një fotografi - për shembull, pas riindeksimit të natës, pema përbëhet nga një majë, e cila përmban të gjitha mesazhet e nënlistës. Pema Splay e bën të lehtë futjen në mes të një kulmi të tillë pa pasur nevojë të mendosh për balancimin. Përveç kësaj, Splay nuk ruan të dhëna të panevojshme, gjë që na kursen kujtesën.

Mesazhet përfshijnë një sasi të madhe informacioni, kryesisht tekst, i cili është i dobishëm për t'u kompresuar. Është e rëndësishme që ne të mund të çarkivojmë me saktësi qoftë edhe një mesazh individual. Përdoret për të kompresuar mesazhet Algoritmi Huffman me heuristikat tona - për shembull, ne e dimë se në mesazhe fjalët alternohen me "jo fjalë" - hapësira, shenja pikësimi - dhe ne gjithashtu kujtojmë disa nga veçoritë e përdorimit të simboleve për gjuhën ruse.

Meqenëse ka shumë më pak përdorues sesa bisedat, për të ruajtur kërkesat e diskut me akses të rastësishëm në motorin e bisedës, ne i ruajmë mesazhet në "motorin e përdoruesit".

Kërkimi i mesazheve zbatohet si një pyetje diagonale nga motori i përdoruesit në të gjitha rastet e motorit të bisedës që përmbajnë biseda të këtij përdoruesi. Rezultatet kombinohen në vetë motorin e përdoruesit.

Epo, të gjitha detajet janë marrë parasysh, gjithçka që mbetet është të kaloni në një skemë të re - dhe mundësisht pa e vënë re përdoruesit.

Migrimi i të dhënave

Pra, ne kemi një motor teksti që ruan mesazhet sipas përdoruesve, dhe dy grupe anëtarësh të bisedave dhe bisedave të anëtarëve që ruajnë të dhëna për dhomat me shumë biseda dhe përdoruesit në to. Si të kaloni nga kjo në motorin e ri të përdoruesit dhe motorin e bisedës?

bisedat e anëtarëve në skemën e vjetër u përdorën kryesisht për optimizim. Ne transferuam shpejt të dhënat e nevojshme prej tij tek anëtarët e bisedës dhe më pas ai nuk mori pjesë më në procesin e migrimit.

Radhë për anëtarët e bisedës. Ai përfshin 100 raste, ndërsa chat-engine ka 4 mijë. Për të transferuar të dhënat, duhet t'i sillni ato në përputhje - për këtë, anëtarët e bisedës u ndanë në të njëjtat 4 mijë kopje, dhe më pas leximi i binlogut të anëtarëve të bisedës u aktivizua në motorin e bisedës.
Rishkruajeni bazën e të dhënave të mesazheve VKontakte nga e para dhe mbijetoni
Tani chat-engine di për multi-chat nga anëtarët e chat-it, por ende nuk di asgjë për dialogët me dy bashkëbisedues. Dialogë të tillë janë të vendosur në motorin e tekstit duke iu referuar përdoruesve. Këtu ne i morëm të dhënat "me kokë": çdo shembull i motorit të bisedës pyeti të gjitha rastet e motorit të tekstit nëse kishin dialogun që i nevojitej.

E shkëlqyeshme - motori i bisedës e di se çfarë bisedash me shumë biseda ka dhe e di se çfarë dialogësh ka.
Ju duhet të kombinoni mesazhet në bisedat me shumë biseda në mënyrë që të përfundoni me një listë mesazhesh në çdo bisedë. Së pari, chat-engine merr nga motori i tekstit të gjitha mesazhet e përdoruesit nga kjo bisedë. Në disa raste ka mjaft prej tyre (deri në qindra miliona), por me përjashtime shumë të rralla, biseda përshtatet plotësisht në RAM. Ne kemi mesazhe të pa renditura, secila në disa kopje - në fund të fundit, të gjitha ato janë nxjerrë nga shembuj të ndryshëm të motorëve të tekstit që korrespondojnë me përdoruesit. Qëllimi është të renditni mesazhet dhe të hiqni qafe kopjet që zënë hapësirë ​​të panevojshme.

Çdo mesazh ka një vulë kohore që përmban kohën e dërgimit dhe tekstin. Ne përdorim kohën për renditje - vendosim tregues për mesazhet më të vjetra të pjesëmarrësve në multichat dhe krahasojmë hash-et nga teksti i kopjeve të synuara, duke lëvizur drejt rritjes së vulës kohore. Është logjike që kopjet të kenë të njëjtin hash dhe vulën kohore, por në praktikë nuk është gjithmonë kështu. Siç e mbani mend, sinkronizimi në skemën e vjetër u krye nga PHP - dhe në raste të rralla, koha e dërgimit të të njëjtit mesazh ndryshonte midis përdoruesve të ndryshëm. Në këto raste, ne i lejuam vetes të modifikonim vulën kohore - zakonisht brenda një sekonde. Problemi i dytë është renditja e ndryshme e mesazheve për marrës të ndryshëm. Në raste të tilla, ne lejuam të krijohej një kopje shtesë, me opsione të ndryshme porositjeje për përdorues të ndryshëm.

Pas kësaj, të dhënat për mesazhet në multichat dërgohen në motorin e përdoruesit. Dhe këtu vjen një veçori e pakëndshme e mesazheve të importuara. Në funksionimin normal, mesazhet që vijnë në motor renditen në mënyrë rigoroze në rend rritës nga user_local_id. Mesazhet e importuara nga motori i vjetër në motorin e përdoruesit e humbën këtë veçori të dobishme. Në të njëjtën kohë, për lehtësinë e testimit, duhet të jeni në gjendje t'i qaseni shpejt, të kërkoni diçka në to dhe të shtoni të reja.

Ne përdorim një strukturë të veçantë të dhënash për të ruajtur mesazhet e importuara.

Ai përfaqëson një vektor të madhësisë Rishkruajeni bazën e të dhënave të mesazheve VKontakte nga e para dhe mbijetoniku janë të gjithë Rishkruajeni bazën e të dhënave të mesazheve VKontakte nga e para dhe mbijetoni - janë të ndryshëm dhe të renditur në rend zbritës, me një renditje të veçantë elementësh. Në çdo segment me indekse Rishkruajeni bazën e të dhënave të mesazheve VKontakte nga e para dhe mbijetoni elementet janë të renditura. Kërkimi i një elementi në një strukturë të tillë kërkon kohë Rishkruajeni bazën e të dhënave të mesazheve VKontakte nga e para dhe mbijetoni përmes Rishkruajeni bazën e të dhënave të mesazheve VKontakte nga e para dhe mbijetoni kërkimet binare. Shtimi i një elementi amortizohet Rishkruajeni bazën e të dhënave të mesazheve VKontakte nga e para dhe mbijetoni.

Pra, ne kuptuam se si të transferojmë të dhënat nga motorët e vjetër në ato të reja. Por ky proces zgjat disa ditë - dhe nuk ka gjasa që gjatë këtyre ditëve përdoruesit tanë të heqin dorë nga zakoni për t'i shkruar njëri-tjetrit. Për të mos humbur mesazhet gjatë kësaj kohe, kalojmë në një skemë pune që përdor grupe të vjetra dhe të reja.

Të dhënat shkruhen për anëtarët e bisedës dhe motorin e përdoruesit (dhe jo në motorin e tekstit, si në funksionimin normal sipas skemës së vjetër). motori i përdoruesit përcakton kërkesën për motorin e bisedës - dhe këtu sjellja varet nga fakti nëse kjo bisedë tashmë është bashkuar apo jo. Nëse biseda nuk është bashkuar ende, motori i bisedës nuk e shkruan mesazhin në vetvete dhe përpunimi i tij ndodh vetëm në motorin e tekstit. Nëse chat-i është bashkuar tashmë në chat-engine, ai kthen chat_local_id në motor-user dhe dërgon mesazhin te të gjithë marrësit. motori i përdoruesit i transferon të gjitha të dhënat në motorin e tekstit - në mënyrë që nëse ndodh diçka, ne gjithmonë mund të kthehemi prapa, duke pasur të gjitha të dhënat aktuale në motorin e vjetër. text-engine kthen user_local_id, të cilin motor-user e ruan dhe e kthen në backend.
Rishkruajeni bazën e të dhënave të mesazheve VKontakte nga e para dhe mbijetoni
Si rezultat, procesi i tranzicionit duket si ky: ne lidhim grupe boshe përdorues-motor dhe chat-motor. chat-engine lexon të gjithë binlogun e anëtarëve të bisedës, më pas fillon proxying sipas skemës së përshkruar më sipër. Ne transferojmë të dhënat e vjetra dhe marrim dy grupime të sinkronizuara (të vjetra dhe të reja). Gjithçka që mbetet është të kaloni leximin nga motori i tekstit në motorin e përdoruesit dhe të çaktivizoni proxying.

Gjetjet

Falë qasjes së re, të gjitha matjet e performancës së motorëve janë përmirësuar dhe problemet me konsistencën e të dhënave janë zgjidhur. Tani mund të implementojmë shpejt veçori të reja në mesazhe (dhe tashmë kemi filluar ta bëjmë këtë - kemi rritur numrin maksimal të pjesëmarrësve në bisedë, kemi zbatuar një kërkim për mesazhet e përcjella, kemi nisur mesazhet e gozhduara dhe kemi rritur kufirin në numrin total të mesazheve për përdorues) .

Ndryshimet në logjikë janë vërtet të mëdha. Dhe dua të vërej se kjo nuk nënkupton gjithmonë vite të tëra zhvillimi nga një ekip i madh dhe një mori linjash kodi. motori i bisedës dhe motori i përdoruesit, së bashku me të gjitha historitë shtesë si Huffman për kompresimin e mesazheve, pemët Splay dhe struktura për mesazhet e importuara është më pak se 20 mijë rreshta kodi. Dhe ato u shkruan nga 3 zhvillues në vetëm 10 muaj (megjithatë, ia vlen të kihet parasysh të gjithë tre zhvillues - kampionët e botës në programet sportive).

Për më tepër, në vend që të dyfishonim numrin e serverëve, ne përgjysmuam numrin e tyre - tani motori i përdoruesit dhe motori i bisedës jetojnë në 500 makina fizike, ndërsa skema e re ka një hapësirë ​​të madhe për ngarkesë. Kemi kursyer shumë para në pajisje - rreth 5 milion dollarë + 750 mijë dollarë në vit në shpenzime operative.

Ne përpiqemi të gjejmë zgjidhjet më të mira për problemet më komplekse dhe në shkallë të gjerë. Ne kemi shumë prej tyre - dhe kjo është arsyeja pse ne po kërkojmë zhvillues të talentuar në departamentin e bazës së të dhënave. Nëse ju pëlqen dhe dini të zgjidhni probleme të tilla, keni njohuri të shkëlqyera të algoritmeve dhe strukturave të të dhënave, ju ftojmë të bashkoheni me ekipin. Na kontaktoni HRper detaje.

Edhe nëse kjo histori nuk ka të bëjë me ju, ju lutemi vini re se ne i vlerësojmë rekomandimet. Tregoji një shoku për vendet e lira të zhvilluesve, dhe nëse ai përfundon me sukses periudhën e provës, do të merrni një bonus prej 100 mijë rubla.

Burimi: www.habr.com

Shto një koment