ВКонтакте хабарламалар базасын нөлден қайта жазыңыз және аман болыңыз

Біздің қолданушылар шаршағанын білмей бір-біріне хабарлама жазады.
ВКонтакте хабарламалар базасын нөлден қайта жазыңыз және аман болыңыз
Бұл өте көп. Егер сіз барлық пайдаланушылардың барлық хабарламаларын оқуға кіріссеңіз, бұл 150 мың жылдан астам уақытты алады. Сіз жеткілікті дамыған оқырман болсаңыз және әрбір хабарламаға бір секундтан артық уақыт жұмсамасаңыз.

Деректердің мұндай көлемімен оны сақтау және оған қол жеткізу логикасы оңтайлы түрде құрастырылғаны маңызды. Әйтпесе, соншалықты керемет емес бір сәтте бәрі тез арада бұрмалайтыны анық болуы мүмкін.

Біз үшін бұл сәт бір жарым жыл бұрын келді. Біз бұған қалай келдік және соңында не болды - біз сізге ретімен айтамыз.

Фон

Бірінші іске асыруда ВКонтакте хабарламалары PHP сервері мен MySQL комбинациясында жұмыс істеді. Бұл кішкентай студенттік веб-сайт үшін мүлдем қалыпты шешім. Дегенмен, бұл сайт бақылаусыз өсті және өзі үшін деректер құрылымдарын оңтайландыруды талап ете бастады.

2009 жылдың соңында бірінші мәтіндік репозиторий жазылды, ал 2010 жылы оған хабарламалар жіберілді.

Мәтіндік жүйеде хабарламалар тізімдерде сақталды - «пошта жәшіктерінің» бір түрі. Әрбір осындай тізім uid арқылы анықталады - осы хабарлардың барлығына ие пайдаланушы. Хабарламада атрибуттар жиынтығы болады: әңгімелесушінің идентификаторы, мәтін, қосымшалар және т.б. «Жәшік» ішіндегі хабар идентификаторы local_id болып табылады, ол ешқашан өзгермейді және жаңа хабарламалар үшін ретімен тағайындалады. «Жәшіктер» тәуелсіз және қозғалтқыш ішінде бір-бірімен синхрондалмаған, олардың арасындағы байланыс PHP деңгейінде жүреді. Мәтіндік жүйенің деректер құрылымы мен мүмкіндіктерін ішінен қарауға болады осында.
ВКонтакте хабарламалар базасын нөлден қайта жазыңыз және аман болыңыз
Бұл екі пайдаланушының хат алмасуы үшін жеткілікті болды. Одан әрі не болды?

2011 жылдың мамыр айында ВКонтакте бірнеше қатысушылармен сөйлесуді енгізді - мультичат. Олармен жұмыс істеу үшін біз екі жаңа кластерді құрдық - мүше-чат және чат-мүше. Біріншісі пайдаланушылардың чаттары туралы деректерді сақтайды, екіншісі чаттар арқылы пайдаланушылар туралы деректерді сақтайды. Тізімдерге қоса, бұған, мысалы, шақырушы пайдаланушы және олардың чатқа қосылған уақыты кіреді.

«PHP, чатқа хабарлама жіберейік», - дейді пайдаланушы.
«Келіңіз, {username}», - дейді PHP.
ВКонтакте хабарламалар базасын нөлден қайта жазыңыз және аман болыңыз
Бұл схеманың кемшіліктері бар. Синхрондау әлі де РНР жауапкершілігінде. Үлкен чаттар мен оларға бір уақытта хабарлама жіберетін пайдаланушылар - қауіпті оқиға. Мәтіндік қозғалтқыш данасы пайдаланушы идентификаторына байланысты болғандықтан, чатқа қатысушылар бірдей хабарды әртүрлі уақытта ала алады. Егер прогресс тоқтап қалса, мұнымен өмір сүруге болады. Бірақ бұл болмайды.

2015 жылдың соңында біз қауымдастық хабарламаларын іске қостық, ал 2016 жылдың басында олар үшін API іске қостық. Қауымдастықтарда үлкен чат-боттардың пайда болуымен жүктемені біркелкі бөлуді ұмытуға болады.

Жақсы бот күніне бірнеше миллион хабарлама жасайды - тіпті ең көп сөйлейтін пайдаланушылар мұны мақтана алмайды. Бұл мұндай боттар өмір сүретін мәтіндік қозғалтқыштың кейбір даналары толығымен зардап шеге бастағанын білдіреді.

2016 жылы хабарлама механизмдері чат-мүшелер мен мүше-чаттардың 100 данасы және 8000 мәтіндік қозғалтқыштар болып табылады. Олар әрқайсысы 64 ГБ жады бар мың серверде орналастырылды. Бірінші төтенше шара ретінде біз жадты тағы 32 ГБ-қа арттырдық. Болжамдарды бағаладық. Күрделі өзгерістерсіз бұл тағы бір жылға жетеді. Сізге аппараттық құралдарды алу немесе дерекқорларды оңтайландыру қажет.

Архитектураның сипатына байланысты аппараттық құралдарды бірнеше есе көбейту ғана мағынасы бар. Яғни, көліктер санын кем дегенде екі есеге арттыру – бұл өте қымбат жол екені анық. Біз оңтайландырамыз.

Жаңа тұжырымдама

Жаңа тәсілдің орталық мәні - чат. Чатта оған қатысты хабарлар тізімі бар. Пайдаланушыда чаттар тізімі бар.

Қажетті минимум - екі жаңа дерекқор:

  • чат қозғалтқышы. Бұл чат векторларының репозиторийі. Әрбір чатта оған қатысты хабарламалар векторы болады. Әрбір хабардың мәтіні және чатта бірегей хабар идентификаторы бар - chat_local_id.
  • пайдаланушы қозғалтқышы. Бұл пайдаланушылардың векторларының қоймасы - пайдаланушыларға сілтемелер. Әрбір пайдаланушының peer_id векторы (әңгімелесушілер – басқа пайдаланушылар, көп чат немесе қауымдастықтар) және хабарламалар векторы болады. Әрбір peer_id оған қатысты хабарлардың векторы болады. Әрбір хабарламада chat_local_id және сол пайдаланушы үшін бірегей хабар идентификаторы бар - user_local_id.

ВКонтакте хабарламалар базасын нөлден қайта жазыңыз және аман болыңыз
Жаңа кластерлер TCP көмегімен бір-бірімен байланысады - бұл сұраныстардың реті өзгермейтінін қамтамасыз етеді. Сұраныстардың өзі және олар үшін растаулар қатты дискіде жазылады - осылайша біз кез келген уақытта ақаулықтан немесе қозғалтқыш қайта іске қосылғаннан кейін кезек күйін қалпына келтіре аламыз. Пайдаланушы қозғалтқышы мен сөйлесу қозғалтқышы әрқайсысы 4 мың бөліктен тұратындықтан, кластерлер арасындағы сұрау кезегі біркелкі таратылады (бірақ шын мәнінде олар мүлдем жоқ - және ол өте жылдам жұмыс істейді).

Біздің дерекқорларымызда дискімен жұмыс істеу көп жағдайда екілік өзгерістер журналының (binlog), статикалық суреттердің және жадтағы ішінара кескіннің тіркесіміне негізделген. Күн ішінде жасалған өзгерістер бинлогқа жазылады және ағымдағы күйдің суреті мерзімді түрде жасалады. Сурет - біздің мақсаттарымыз үшін оңтайландырылған деректер құрылымдарының жинағы. Ол тақырыптан (суреттің метаиндексі) және метафайлдар жинағынан тұрады. Тақырып жедел жадта тұрақты сақталады және суреттен деректерді қай жерден іздеу керектігін көрсетеді. Әрбір метафайл уақыттың жақын нүктелерінде қажет болуы мүмкін деректерді қамтиды, мысалы, бір пайдаланушыға қатысты. Сурет тақырыбын пайдаланып дерекқорды сұраған кезде, қажетті метафайл оқылады, содан кейін суретті жасағаннан кейін орын алған бинлогтағы өзгерістер ескеріледі. Бұл тәсілдің артықшылықтары туралы көбірек оқи аласыз осында.

Сонымен қатар, қатты дискідегі деректердің өзі күніне бір рет өзгереді - Мәскеуде түнде, жүктеме аз болған кезде. Осының арқасында (дискідегі құрылым күні бойы тұрақты болатынын біле отырып) біз векторларды белгіленген өлшемді массивтермен ауыстыруға мүмкіндік аламыз - және осының арқасында жадты арттыру.

Жаңа схемада хабарлама жіберу келесідей:

  1. PHP сервері хабарлама жіберу сұрауымен пайдаланушы қозғалтқышымен байланысады.
  2. пайдаланушы қозғалтқышы сұрауды қажетті сөйлесу механизмінің данасына проксимен жібереді, ол chat_local_id пайдаланушы қозғалтқышына қайтарады - осы чаттағы жаңа хабардың бірегей идентификаторы. Содан кейін chat_engine хабарды чаттағы барлық алушыларға таратады.
  3. user-engine chat-engine жүйесінен chat_local_id алады және user_local_id мәнін PHP-ге қайтарады - осы пайдаланушы үшін бірегей хабар идентификаторы. Содан кейін бұл идентификатор, мысалы, API арқылы хабарлармен жұмыс істеу үшін пайдаланылады.

ВКонтакте хабарламалар базасын нөлден қайта жазыңыз және аман болыңыз
Бірақ нақты хабарламаларды жіберуден басқа, сізге тағы бірнеше маңызды нәрселерді орындау қажет:

  • Ішкі тізімдер, мысалы, сөйлесулер тізімін ашқан кезде көретін ең соңғы хабарлар. Оқылмаған хабарламалар, тегтері бар хабарламалар («Маңызды», «Спам» және т.б.).
  • Сөйлесу жүйесінде хабарламаларды қысу
  • Пайдаланушы қозғалтқышында хабарламаларды кэштеу
  • Іздеу (барлық диалогтар арқылы және белгілі бір терезеде).
  • Нақты уақыттағы жаңарту (Longpolling).
  • Мобильді клиенттерде кэштеуді енгізу үшін тарихты сақтау.

Барлық ішкі тізімдер тез өзгеретін құрылымдар. Олармен жұмыс істеу үшін біз пайдаланамыз Сплей ағаштары. Бұл таңдау ағаштың жоғарғы жағында кейде суреттен алынған хабарлардың тұтас сегментін сақтайтындығымен түсіндіріледі - мысалы, түнгі қайта индекстеуден кейін ағаш ішкі тізімнің барлық хабарламаларын қамтитын бір жоғарғы бөліктен тұрады. Splay ағашы теңдестіру туралы ойланбастан осындай шыңның ортасына оңай кірістіруге мүмкіндік береді. Сонымен қатар, Splay қажет емес деректерді сақтамайды, бұл бізге жадты үнемдейді.

Хабарламалар ақпараттың үлкен көлемін, негізінен мәтінді қамтиды, бұл қысу мүмкіндігіне пайдалы. Тіпті бір жеке хабарламаны дәл мұрағаттан шығару маңызды. Хабарламаларды қысу үшін қолданылады Хаффман алгоритмі өзіміздің эвристикамен - мысалы, хабарларда сөздердің «сөзсіз» - бос орындармен, тыныс белгілерімен ауысатынын білеміз, сонымен қатар орыс тіліндегі таңбаларды қолданудың кейбір ерекшеліктерін есте сақтаймыз.

Чаттарға қарағанда пайдаланушылар әлдеқайда аз болғандықтан, чат жүйесінде кездейсоқ қатынау дискілерінің сұрауларын сақтау үшін біз хабарларды пайдаланушы жүйесінде кэштейміз.

Хабарламаны іздеу пайдаланушы қозғалтқышынан осы пайдаланушының чаттарын қамтитын барлық чат механизмінің даналарына диагональды сұрау ретінде жүзеге асырылады. Нәтижелер пайдаланушы қозғалтқышының өзінде біріктірілген.

Барлық егжей-тегжейлер ескерілді, қалғаны жаңа схемаға ауысу - және пайдаланушылар оны байқамай-ақ қою.

Деректерді тасымалдау

Сонымен, бізде пайдаланушының хабарламаларын сақтайтын мәтіндік қозғалтқыш және көп сөйлесу бөлмелері мен олардағы пайдаланушылар туралы деректерді сақтайтын екі кластер чат-мүшелері мен мүше-чаттары бар. Бұдан жаңа пайдаланушы қозғалтқышына және сөйлесу жүйесіне қалай өтуге болады?

Ескі схемадағы мүше чаттары негізінен оңтайландыру үшін пайдаланылды. Біз одан қажетті деректерді чат мүшелеріне жылдам жібердік, содан кейін ол көшіру процесіне қатыспады.

Чат мүшелері үшін кезек. Ол 100 дананы қамтиды, ал чат қозғалтқышында 4 мың. Деректерді тасымалдау үшін оны сәйкестендіру керек - бұл үшін чат мүшелері бірдей 4 мың көшірмеге бөлінген, содан кейін чат қозғалтқышында чат мүшелерінің бинлогын оқу іске қосылды.
ВКонтакте хабарламалар базасын нөлден қайта жазыңыз және аман болыңыз
Енді чат-қозғалтқыш чат мүшелерінің мультичат туралы біледі, бірақ екі сұхбаттасушымен диалогтар туралы әлі ештеңе білмейді. Мұндай диалогтар пайдаланушыларға сілтеме жасай отырып, мәтіндік жүйеде орналасқан. Мұнда біз деректерді «бір-біріне» алдық: әрбір чат механизмі данасы барлық мәтіндік қозғалтқыш даналарынан қажетті диалогтың бар-жоғын сұрады.

Тамаша - чат қозғалтқышы қандай көп чат бар екенін біледі және қандай диалогтар бар екенін біледі.
Әр чатта хабарлар тізімі болатындай етіп көп чаттағы хабарларды біріктіру керек. Біріншіден, чат қозғалтқышы мәтіндік жүйеден осы чаттағы барлық пайдаланушы хабарламаларын шығарады. Кейбір жағдайларда олардың саны өте көп (жүздеген миллионға дейін), бірақ өте сирек жағдайларды қоспағанда, чат толығымен жедел жадқа сәйкес келеді. Бізде реттелмеген хабарламалар бар, олардың әрқайсысы бірнеше көшірмелерде - олардың барлығы пайдаланушыларға сәйкес келетін әртүрлі мәтіндік қозғалтқыш даналарынан алынған. Мақсат - хабарларды сұрыптау және қажетсіз орын алатын көшірмелерден құтылу.

Әрбір хабардың жіберілген уақыты мен мәтіні бар уақыт белгісі бар. Біз сұрыптау үшін уақытты пайдаланамыз - біз мультичатқа қатысушылардың ең ескі хабарламаларына көрсеткіштерді орналастырамыз және уақыт белгісін ұлғайту жағына қарай жоспарланған көшірмелердің мәтінінен хэштерді салыстырамыз. Көшірмелерде хэш пен уақыт белгісі бірдей болатыны қисынды, бірақ іс жүзінде бұл әрдайым бола бермейді. Естеріңізде болса, ескі схемадағы синхрондау PHP арқылы жүзеге асырылды - сирек жағдайларда әртүрлі пайдаланушылар арасында бірдей хабарламаны жіберу уақыты әртүрлі болды. Мұндай жағдайларда біз өзімізге уақыт белгісін өңдеуге рұқсат бердік - әдетте бір секунд ішінде. Екінші мәселе - әртүрлі алушыларға арналған хабарламалардың әртүрлі реті. Мұндай жағдайларда біз әртүрлі пайдаланушылар үшін әртүрлі тапсырыс опциялары бар қосымша көшірмені жасауға рұқсат бердік.

Осыдан кейін мультичаттағы хабарламалар туралы деректер пайдаланушы қозғалтқышына жіберіледі. Мұнда импорттық хабарламалардың жағымсыз қасиеті пайда болады. Қалыпты жұмыс кезінде қозғалтқышқа келетін хабарлар user_local_id арқылы қатаң түрде өсу ретімен реттеледі. Ескі қозғалтқыштан пайдаланушы-қозғалтқышқа импортталған хабарлар бұл пайдалы сипатты жоғалтты. Сонымен қатар, тестілеудің ыңғайлылығы үшін оларға жылдам қол жеткізу, олардан бірдеңе іздеу және жаңаларын қосу мүмкіндігі болуы керек.

Импортталған хабарламаларды сақтау үшін арнайы деректер құрылымын қолданамыз.

Ол өлшем векторын көрсетеді ВКонтакте хабарламалар базасын нөлден қайта жазыңыз және аман болыңыз, барлығы қайда ВКонтакте хабарламалар базасын нөлден қайта жазыңыз және аман болыңыз - әртүрлі және кему ретімен реттелген, элементтердің ерекше тәртібімен. Әрбір сегментте индекстері бар ВКонтакте хабарламалар базасын нөлден қайта жазыңыз және аман болыңыз элементтері сұрыпталады. Мұндай құрылымдағы элементті іздеу уақытты қажет етеді ВКонтакте хабарламалар базасын нөлден қайта жазыңыз және аман болыңыз через ВКонтакте хабарламалар базасын нөлден қайта жазыңыз және аман болыңыз екілік іздеулер. Элементті қосу амортизацияланады ВКонтакте хабарламалар базасын нөлден қайта жазыңыз және аман болыңыз.

Сонымен, біз деректерді ескі қозғалтқыштардан жаңасына қалай тасымалдау керектігін түсіндік. Бірақ бұл процесс бірнеше күнді алады - және бұл күндері біздің қолданушылар бір-біріне хат жазу әдетінен бас тартуы екіталай. Осы уақыт ішінде хабарларды жоғалтпау үшін біз ескі және жаңа кластерлерді пайдаланатын жұмыс схемасына ауысамыз.

Деректер чат мүшелеріне және пайдаланушы қозғалтқышына жазылады (ескі схема бойынша қалыпты жұмыстағыдай мәтіндік қозғалтқышқа емес). пайдаланушы қозғалтқышы чат-қозғалтқышына сұрауды прокси-серверлейді - және бұл жерде мінез-құлық осы чаттың біріктірілген-біріктірілмегеніне байланысты. Егер чат әлі біріктірілмеген болса, сөйлесу механизмі хабарламаны өзіне жазбайды және оны өңдеу тек мәтіндік жүйеде жүреді. Егер чат әлдеқашан сөйлесу жүйесіне біріктірілген болса, ол chat_local_id мәнін пайдаланушы жүйесіне қайтарады және хабарды барлық алушыларға жібереді. пайдаланушы қозғалтқышы барлық деректерді мәтіндік қозғалтқышқа проксимен жібереді - егер бірдеңе орын алса, біз әрқашан ескі қозғалтқыштағы барлық ағымдағы деректерді сақтай отырып, кері қайтара аламыз. text-engine user_local_id мәнін қайтарады, ол пайдаланушы қозғалтқышы сақтайды және серверге қайтарады.
ВКонтакте хабарламалар базасын нөлден қайта жазыңыз және аман болыңыз
Нәтижесінде көшу процесі келесідей көрінеді: біз бос пайдаланушы қозғалтқышы және сөйлесу қозғалтқышы кластерлерін қосамыз. chat-engine бүкіл чат мүшелерінің бинлогын оқиды, содан кейін жоғарыда сипатталған схемаға сәйкес прокси-сервер басталады. Біз ескі деректерді тасымалдаймыз және екі синхрондалған кластерді аламыз (ескі және жаңа). Тек оқуды мәтіндік қозғалтқыштан пайдаланушы қозғалтқышына ауыстыру және проксиді өшіру ғана қалады.

нәтижелері

Жаңа тәсілдің арқасында қозғалтқыштардың барлық өнімділік көрсеткіштері жақсартылды және деректер сәйкестігіне қатысты мәселелер шешілді. Енді біз хабарламаларға жаңа мүмкіндіктерді жылдам енгізе аламыз (және мұны қазірдің өзінде жасай бастадық - біз чатқа қатысушылардың максималды санын көбейттік, қайта жіберілген хабарламаларды іздеуді жүзеге асырдық, бекітілген хабарламаларды іске қостық және бір пайдаланушыға келетін хабарлардың жалпы санына шектеуді көтердік) .

Логикадағы өзгерістер шынымен де орасан зор. Және бұл әрқашан үлкен команданың және сансыз код жолдарының ұзақ жылдар бойы дамуын білдірмейтінін атап өткім келеді. чат-қозғалтқышы және пайдаланушы-қозғалтқышы, сонымен қатар хабарды қысу үшін Хаффман, Splay ағаштары және импортталған хабарламаларға арналған құрылым сияқты барлық қосымша оқиғалармен бірге кодтың 20 мың жолынан аз. Оларды небәрі 3 ай ішінде 10 әзірлеуші ​​жазған (бірақ есте ұстаған жөн барлық үш әзірлеуші - әлем чемпиондары спорттық бағдарламалауда).

Оның үстіне, серверлер санын екі есе көбейтудің орнына, біз олардың санын екі есе қысқарттық - енді пайдаланушы қозғалтқышы мен чат қозғалтқышы 500 физикалық машинада жұмыс істейді, ал жаңа схемада жүктеме үшін үлкен орын бар. Біз жабдыққа көп ақша үнемдедік - шамамен 5 миллион доллар + операциялық шығындар жылына 750 мың доллар.

Біз ең күрделі және ауқымды мәселелердің ең жақсы шешімдерін табуға тырысамыз. Бізде олардың саны өте көп, сондықтан біз дерекқор бөлімінде дарынды әзірлеушілерді іздейміз. Егер сіз осындай есептерді шешуді жақсы көрсеңіз және білсеңіз, алгоритмдер мен деректер құрылымдарын жақсы білетін болсаңыз, сізді командаға қосылуға шақырамыз. Біздің хабарласыңыз HRмәліметтер үшін.

Бұл оқиға сіз туралы болмаса да, біз ұсыныстарды бағалайтынымызды ескеріңіз. Досыңызға айтыңыз әзірлеушілердің бос орындары, ал егер ол сынақ мерзімін сәтті аяқтаса, сіз 100 мың рубль көлемінде бонус аласыз.

Ақпарат көзі: www.habr.com

пікір қалдыру