Gure erabiltzaileek elkarri mezuak idazten dizkiote nekea ezagutu gabe.

Hori asko da. Erabiltzaile guztien mezu guztiak irakurtzeari ekinez gero, 150 mila urte baino gehiago beharko lirateke. Betiere, irakurle nahiko aurreratua bazara eta mezu bakoitzean segundo bat baino gehiago igaro gabe.
Datu-bolumen horrekin, funtsezkoa da horiek gordetzeko eta sartzeko logika modu egokian eraikitzea. Bestela, une ez hain zoragarri batean, argi geratuko da dena laster gaizki joango dela.
Guretzat, momentu hau duela urte eta erdi iritsi zen. Nola iritsi garen horretara eta zer gertatu zen azkenean - ordenan kontatzen dizuegu.
Gaiaren historia
Lehen inplementazioan, VKontakte mezuek PHP backend eta MySQL konbinazio batean lan egin zuten. Ikasleen webgune txiki baten irtenbide guztiz normala da. Hala ere, gune hau kontrolik gabe hazi zen eta datu-egituren optimizazioa eskatzen hasi zen.
2009. urte amaieran lehen testu-motor biltegia idatzi zen, eta 2010ean mezuak bertara eraman zituzten.
Testu-motorrean, mezuak zerrendetan gordetzen ziren - "postontzi" moduko bat. Horrelako zerrenda bakoitza uid batek zehazten du - mezu horien guztien jabe den erabiltzaileak. Mezu batek atributu multzo bat du: solaskidearen identifikatzailea, testua, eranskinak, etab. "Kutxa" barruko mezu-identifikatzailea local_id da, ez da inoiz aldatzen eta sekuentzialki esleitzen zaie mezu berriei. "Kutxak" independenteak dira eta ez daude elkarren artean sinkronizatzen motorren barruan; haien arteko komunikazioa PHP mailan gertatzen da. Testu-motorearen datuen egitura eta gaitasunak barrutik begiratu ditzakezu .

Hau nahikoa zen bi erabiltzaileren arteko korrespondentziarako. Asmatu zer gertatu zen gero?
2011ko maiatzean, VKontaktek hainbat parte-hartzailerekin elkarrizketak aurkeztu zituen —multi-txat—. Haiekin lan egiteko, bi kluster berri sortu genituen: kide-txatak eta txat-kideak. Lehenengoak erabiltzaileen txatei buruzko datuak gordetzen ditu, bigarrenak txatei buruzko erabiltzaileei buruzko datuak gordetzen ditu. Zerrendez gain, besteak beste, gonbidatzen duen erabiltzailea eta txatean gehitu ziren ordua sartzen dira.
"PHP, bidal dezagun mezu bat txatera", dio erabiltzaileak.
"Ea, {username}", dio PHP-k.

Eskema honek desabantailak ditu. Sinkronizazioa PHPren ardura da oraindik. Txat handiak eta aldi berean mezuak bidaltzen dizkieten erabiltzaileak istorio arriskutsuak dira. Testu-motorearen instantzia uid-aren araberakoa denez, txat-parte-hartzaileek mezu bera jaso dezakete une desberdinetan. Honekin bizi zitekeen aurrerapena geldirik egongo balitz. Baina hori ez da gertatuko.
2015. urtearen amaieran, komunitateko mezuak plazaratu genituen, eta 2016. urtearen hasieran, haientzako API bat jarri genuen martxan. Komunitateetan txat-bot handien etorrerarekin, posible zen karga-banaketa ere ahaztea.
Bot on batek hainbat milioi mezu sortzen ditu egunean - erabiltzailerik berritsuenak ere ezin dira honetaz harrotu. Horrek esan nahi du testu-motorearen kasu batzuk, zeintzuetan bizi ziren bot-ak, oso-osorik sufritzen hasi zirela.
2016ko mezu-motorrak txat-kideen eta kide-txat-en 100 instantzia dira, eta 8000 testu-motor. Mila zerbitzarietan ostatatuta zeuden, bakoitza 64 GBko memoriarekin. Lehen larrialdi neurri gisa, memoria beste 32 GB handitu dugu. Aurreikuspenak kalkulatu ditugu. Aldaketa zorrotzik gabe, nahikoa izango litzateke beste urte batez. Hardwarea eskuratu edo datu-baseak beraiek optimizatu behar dituzu.
Arkitekturaren izaera dela eta, hardwarea anitz handitzea baino ez da zentzuzkoa. Hau da, gutxienez kotxe kopurua bikoiztu - bistan denez, hau nahiko garestia da. Optimizatuko dugu.
Kontzeptu berria
Ikuspegi berriaren funtsa txata da. Txat batek harekin erlazionatutako mezuen zerrenda dauka. Erabiltzaileak txat zerrenda bat dauka.
Beharrezko gutxieneko bi datu-base berri dira:
- txat-motorra. Hau txat-bektoreen biltegia da. Txat bakoitzak harekin erlazionatutako mezuen bektore bat du. Mezu bakoitzak testu bat eta mezu-identifikatzaile esklusibo bat ditu txataren barruan - chat_local_id.
- erabiltzaile-motorra. Hau erabiltzaileen bektoreen biltegiratze bat da - erabiltzaileentzako estekak. Erabiltzaile bakoitzak peer_id bektore bat (solaskideak - beste erabiltzaile batzuk, txat anitzeko edo komunitateak) eta mezuen bektore bat ditu. Peer_id bakoitzak harekin erlazionatutako mezuen bektore bat du. Mezu bakoitzak chat_local_id eta erabiltzaile horren mezu ID esklusibo bat ditu - user_local_id.

Kluster berriak elkarren artean komunikatzen dira TCP erabiliz; honek bermatzen du eskaeren ordena ez dela aldatzen. Eskaerak beraiek eta horien baieztapenak disko gogorrean grabatzen dira, beraz, ilararen egoera edozein unetan leheneratu ahal izango dugu motorra hutsegite edo berrabiarazi ondoren. Erabiltzaile-motorra eta txat-motorra bakoitza 4 mila zati direnez, klusterren arteko eskaera-ilara uniformeki banatuko da (baina errealitatean ez dago batere - eta oso azkar funtzionatzen du).
Gure datu-baseetan diskoarekin lan egitea kasu gehienetan aldaketen erregistro bitar baten (binlog), argazki estatikoen eta memoriako irudi partzial baten konbinazioan oinarritzen da. Eguneko aldaketak binlog batean idazten dira, eta uneko egoeraren argazki bat sortzen da aldian-aldian. Snapshot bat gure helburuetarako optimizatutako datu-egituren bilduma da. Goiburu batek (irudiaren metaindizea) eta metafitxategi multzo batek osatzen du. Goiburua RAM-n gordetzen da betirako eta argazkiko datuak non bilatu behar diren adierazten du. Metafitxategi bakoitzak denbora hurbilean beharrezkoak diren datuak biltzen ditu, adibidez, erabiltzaile bakar bati lotutakoak. Datu-basea argazkiaren goiburua erabiliz kontsultatzen duzunean, beharrezko metafitxategia irakurtzen da, eta, ondoren, argazkia sortu ondoren gertatutako binlog-en aldaketak hartzen dira kontuan. Ikuspegi honen abantailei buruz gehiago irakur dezakezu .
Aldi berean, disko gogorreko datuak egunean behin bakarrik aldatzen dira - Moskun iluntzean, karga minimoa denean. Horri esker (jakinduta diskoaren egitura konstantea dela egunean zehar), bektoreak tamaina finkoko matrizeekin ordezkatu ditzakegu, eta horregatik, memoria irabazi.
Eskema berrian mezu bat bidaltzeak itxura hau du:
- PHP backend-ak erabiltzaile-motorrarekin harremanetan jartzen du mezu bat bidaltzeko eskaerarekin.
- user-engine-k eskaera nahi den txat-motorearen instantziara bideratzen du, eta user-engine chat_local_id-era itzultzen da - txat honetako mezu berri baten identifikatzaile esklusiboa. Chat_engine-k mezua igortzen die txatean dauden hartzaile guztiei.
- user-engine-k chat_local_id jasotzen du chat-enginetik eta user_local_id itzultzen du PHPra - erabiltzaile honentzako mezu-identifikatzaile bakarra. Identifikatzaile hori erabiltzen da, adibidez, API bidez mezuekin lan egiteko.

Baina mezuak benetan bidaltzeaz gain, gauza garrantzitsu batzuk ezarri behar dituzu:
- Azpizerrendak, adibidez, elkarrizketa-zerrenda irekitzean ikusten dituzun azken mezuak dira. Irakurri gabeko mezuak, etiketak dituzten mezuak (“Garrantzitsua”, “Spam”, etab.).
- Mezuak konprimitzen txat-motorrean
- Mezuak cachean gordetzea erabiltzaile-motorean
- Bilatu (elkarrizketa guztien bidez eta zehatz baten barruan).
- Denbora errealeko eguneratzea (Longpolling).
- Historia gordetzea bezero mugikorretan cachea ezartzeko.
Azpizerrenda guztiak azkar aldatzen ari diren egiturak dira. Haiekin lan egiteko erabiltzen dugu . Aukera hau zuhaitzaren goialdean batzuetan argazki bateko mezu-segmentu oso bat gordetzen dugulako azaltzen da; adibidez, gauero berriro indexatu ondoren, zuhaitza goialde batez osatuta dago, azpizerrendako mezu guztiak biltzen dituena. Splay zuhaitzak erraz txertatzen du halako erpin baten erdian orekatzean pentsatu beharrik gabe. Horrez gain, Splay-k ez du alferrikako daturik gordetzen, eta horrek memoria aurrezten digu.
Mezuek informazio kopuru handia dakar, gehienbat testua, eta hori konprimitu ahal izateko erabilgarria da. Garrantzitsua da mezu indibidual bat ere zehaztasunez desartxibatzea. Mezuak konprimitzeko erabiltzen da geure heuristikoekin -adibidez, badakigu mezuetan hitzak "ez-hitzekin" txandakatzen direla -hutsuneak, puntuazio-markak- eta errusiar hizkuntzarako sinboloak erabiltzearen berezitasun batzuk ere gogoratzen ditugu.
Txatak baino erabiltzaile gutxiago daudenez, ausazko sarbideko disko-eskaerak chat-enginen gordetzeko, erabiltzaile-motorean gordetzen ditugu mezuak.
Mezuen bilaketa diagonaleko kontsulta gisa ezartzen da erabiltzaile-motorretik erabiltzaile honen txatak dituzten txat-motorreko instantzia guztietara. Emaitzak erabiltzaile-motorean bertan konbinatzen dira.
Bada, xehetasun guztiak kontuan hartu dira, eskema berri batera aldatzea besterik ez da geratzen -eta hobe, erabiltzaileek ohartu gabe-.
Datuen migrazioa
Beraz, erabiltzaileen araberako mezuak gordetzen dituen testu-motor bat dugu, eta txat-kide eta kide-txat bi multzo, txat-gela anitzeko eta haietako erabiltzaileei buruzko datuak gordetzen dituztenak. Nola pasatu honetatik erabiltzaile-motor eta txat-motor berrira?
eskema zaharreko kide-txatak optimizatzeko erabiltzen zen batez ere. Azkar transferitu genien bertatik beharrezko datuak txat-kideei, eta gero ez zuen migrazio prozesuan parte hartu.
Txat-kideentzako ilara. 100 instantzia biltzen ditu, txat-motorrak 4 mila ditu. Datuak transferitzeko, bete egin behar dituzu; horretarako, txat-kideak 4 mila kopia berdinetan banatu ziren, eta, ondoren, txat-kideen binlog-a irakurtzea gaitu zen txat-motorrean.

Orain txat-motorrak badaki txat-kideen multi-txat-en berri, baina oraindik ez daki ezer bi solaskideekin elkarrizketei buruz. Horrelako elkarrizketak testu-motorean kokatzen dira erabiltzaileei erreferentzia eginez. Hemen datuak "aurrean" hartu genituen: txat-motorreko instantzia bakoitzak testu-motorreko instantzia guztiei galdetu zien ea behar zuten elkarrizketarik zuten.
Bikaina - txat-motorrak badaki zer txat anitzeko txatak dauden eta badaki zer elkarrizketa dauden.
Mezuak txat anitzeko txatetan konbinatu behar dituzu, txat bakoitzean mezuen zerrendarekin amaitzeko. Lehenik eta behin, txat-motorrak testu-motorretik berreskuratzen ditu txat honetako erabiltzaile-mezu guztiak. Zenbait kasutan, asko daude (ehunka milioiraino), baina oso salbuespenak salbuespen, txata RAM-era guztiz sartzen da. Ordenatu gabeko mezuak ditugu, bakoitza hainbat kopiatan; azken finean, erabiltzaileei dagozkien testu-motorreko instantzia ezberdinetatik ateratzen dira. Helburua mezuak ordenatzea eta alferrikako lekua hartzen duten kopiak kentzea da.
Mezu bakoitzak denbora-zigilu bat dauka, bidalitako ordua eta testua dituena. Ordenatzeko denbora erabiltzen dugu: txat anitzeko parte-hartzaileen mezu zaharrenetara erakusleak jartzen ditugu eta nahi diren kopien testuaren hashak konparatzen ditugu, denbora-zigilua handitzeko bidean. Logikoa da kopiek hash eta denbora-zigilu bera izatea, baina praktikan ez da beti horrela gertatzen. Gogoratzen duzunez, eskema zaharrean sinkronizazioa PHPk egiten zuen, eta kasu bakanetan, mezu bera bidaltzeko denbora desberdina zen erabiltzaile ezberdinen artean. Kasu hauetan, denbora-zigilua editatzeko baimena eman genion, normalean segundo batean. Bigarren arazoa hartzaile ezberdinentzako mezuen ordena ezberdina da. Halakoetan, kopia gehigarri bat sortzea baimendu genuen, erabiltzaile ezberdinentzako eskaera-aukera ezberdinekin.
Horren ostean, multitxat-eko mezuei buruzko datuak erabiltzaile-motorra bidaltzen dira. Eta hemen inportatutako mezuen ezaugarri desatsegin bat dator. Funtzionamendu arruntean, motorra iristen diren mezuak goranzko ordenan ordenatzen dira user_local_id arabera. Motor zaharretik erabiltzaile-motorrera inportatutako mezuek galdu egin dute propietate erabilgarria. Aldi berean, probak egiteko erosotasunerako, haietara azkar sartu, haietan zerbait bilatu eta berriak gehitu behar dituzu.
Datu-egitura berezi bat erabiltzen dugu inportatutako mezuak gordetzeko.
Tamaina bektore bat adierazten du
non dago denak
- desberdinak eta beheranzko ordenan ordenatuta daude, elementuen ordena berezi batekin. Segmentu bakoitzean indizeekin
elementuak ordenatzen dira. Horrelako egitura batean elementu bat bilatzeko denbora behar da
bidez
bilaketa bitarrak. Elementu bat gehitzea baino gehiago amortizatzen da
.
Beraz, motor zaharretatik berrietara datuak nola transferitzen asmatu genuen. Baina prozesu honek hainbat egun irauten du, eta nekez utziko dute egun hauetan gure erabiltzaileek elkarri idazteko ohiturari. Denbora horretan mezuak ez galtzeko, kluster zaharrak zein berriak erabiltzen dituen lan-eskema batera aldatzen gara.
Datuak txat-kide eta erabiltzaile-motorrean idazten dira (eta ez testu-motorrean, eskema zaharraren arabera funtzionamendu arruntean bezala). user-engine-k txat-motorrari proxy egiten dio eskaera - eta hemen portaera txat hau dagoeneko batu den ala ezaren araberakoa da. Txata oraindik batu ez bada, txat-motorrak ez du mezua idazten bere buruari, eta prozesatzea testu-motorrean bakarrik gertatzen da. Txata dagoeneko txat-motorra batu bada, chat_local_id itzultzen dio user-enginera eta mezua hartzaile guztiei bidaltzen die. user-engine-k datu guztiak testu-motorra bidaltzen ditu; horrela, zerbait gertatzen bada, beti atzera egin dezakegu, uneko datu guztiak motor zaharrean edukita. text-engine-k user_local_id itzultzen du, erabiltzaile-motorrak gordetzen eta backend-era itzultzen duena.

Ondorioz, trantsizio prozesuak honela dauka: erabiltzaile-motor hutsak eta txat-motorra klusterrak konektatzen ditugu. txat-motorrak txat-kideen binlog osoa irakurtzen du, gero proxy-a goian deskribatutako eskemaren arabera hasten da. Datu zaharrak transferitzen ditugu eta bi kluster sinkronizatu (zaharra eta berria) lortzen ditugu. Irakurketa testu-motorretik erabiltzaile-motorra aldatzea eta proxy-a desgaitzea besterik ez da geratzen.
Findings
Ikuspegi berriari esker, motorren errendimendu-neurri guztiak hobetu dira eta datuen koherentziarekin lotutako arazoak konpondu dira. Orain azkar inplementa ditzakegu eginbide berriak mezuetan (eta dagoeneko hasi gara hau egiten - txat parte-hartzaileen gehienezko kopurua handitu dugu, birbidalitako mezuen bilaketa ezarri dugu, ainguratutako mezuak abiarazi ditugu eta erabiltzaile bakoitzeko mezu kopuru osoaren muga igo dugu) .
Logikaren aldaketak izugarriak dira benetan. Eta ohartu nahi nuke horrek ez duela beti esan nahi talde handi batek eta hamaika kode lerroen garapen urte osoak. txat-motorra eta erabiltzaile-motorra mezuak konprimitzeko Huffman bezalako istorio osagarri guztiekin, Splay zuhaitzak eta inportatutako mezuetarako egitura 20 mila kode-lerro baino gutxiago ditu. Eta 3 garatzailek idatzi zituzten 10 hilabetetan (hala ere, kontuan izan behar da - munduko txapeldunak ).
Gainera, zerbitzari kopurua bikoiztu beharrean, haien kopurua erdira murriztu dugu; orain erabiltzaile-motorra eta txat-motorra 500 makina fisikotan bizi dira, eskema berriak kargarako zabalgune handia duen bitartean. Ekipamenduetan diru asko aurreztu genuen - 5 milioi dolar inguru + 750 mila dolar urtero funtzionamendu gastuetan.
Arazo konplexu eta handienetarako irtenbide onenak bilatzen ahalegintzen gara. Horietako asko ditugu, eta horregatik garatzaile talentutsuak bilatzen ari gara datu-baseen sailean. Horrelako arazoak maite eta nola ebazten jakin, algoritmoen eta datu-egituren ezagutza bikaina baduzu, taldera sartzera gonbidatzen zaitugu. Jarri harremanetan gure xehetasunetarako.
Istorio hau zuri buruzkoa ez bada ere, kontuan izan gomendioak baloratzen ditugula. Esan lagun bati buruz , eta proba-aldia arrakastaz betetzen badu, 100 mila errubloko hobaria jasoko duzu.
Iturria: www.habr.com
