VKontakte mesajlar bazasını sıfırdan yenidən yazın və sağ qalın

İstifadəçilərimiz yorulmadan bir-birlərinə mesaj yazır.
VKontakte mesajlar bazasını sıfırdan yenidən yazın və sağ qalın
Bu kifayət qədər çoxdur. Bütün istifadəçilərin bütün mesajlarını oxumaq üçün yola çıxsanız, bu, 150 min ildən çox vaxt aparacaq. Bir şərtlə ki, kifayət qədər qabaqcıl oxucusunuz və hər mesaja bir saniyədən çox vaxt sərf etməyin.

Belə bir həcmli məlumatla onu saxlamaq və əldə etmək üçün məntiqin optimal şəkildə qurulması çox vacibdir. Əks halda, o qədər də gözəl olmayan bir anda hər şeyin tezliklə pis gedəcəyi aydınlaşa bilər.

Bizim üçün bu an il yarım əvvəl gəldi. Buna necə gəldik və sonda nə oldu - biz sizə sıra ilə deyirik.

Ümumi məlumat

İlk tətbiqdə VKontakte mesajları PHP backend və MySQL-in kombinasiyası üzərində işləyirdi. Bu, kiçik bir tələbə veb saytı üçün tamamilə normal bir həlldir. Bununla belə, bu sayt nəzarətsiz böyüdü və özü üçün məlumat strukturlarının optimallaşdırılmasını tələb etməyə başladı.

2009-cu ilin sonunda ilk mətn mühərriki anbarı yazılmış və 2010-cu ildə mesajlar ona köçürülmüşdür.

Mətn mühərrikində mesajlar siyahılarda saxlanılırdı - bir növ "poçt qutuları". Hər bir belə siyahı bir uid tərəfindən müəyyən edilir - bütün bu mesajların sahibi olan istifadəçi. Mesaj bir sıra atributlara malikdir: həmsöhbətin identifikatoru, mətn, əlavələr və s. "Qutu" daxilində mesaj identifikatoru local_id-dir, heç vaxt dəyişmir və yeni mesajlar üçün ardıcıl təyin olunur. "Qutular" müstəqildir və mühərrik daxilində bir-biri ilə sinxronlaşdırılmır, aralarındakı əlaqə PHP səviyyəsində baş verir. Siz mətn mühərrikinin məlumat strukturuna və imkanlarına daxildən baxa bilərsiniz burada.
VKontakte mesajlar bazasını sıfırdan yenidən yazın və sağ qalın
Bu, iki istifadəçi arasında yazışmalar üçün kifayət idi. Təxmin et, sonra nə oldu?

2011-ci ilin may ayında VKontakte bir neçə iştirakçı ilə söhbətlər təqdim etdi - çoxlu söhbət. Onlarla işləmək üçün biz iki yeni klaster yaratdıq - üzv-çatlar və chat-üzvləri. Birincisi istifadəçilərin söhbətləri haqqında məlumatları, ikincisi isə söhbətlər vasitəsilə istifadəçilər haqqında məlumatları saxlayır. Siyahıların özlərinə əlavə olaraq, buraya, məsələn, dəvət edən istifadəçi və söhbətə əlavə olunduğu vaxt daxildir.

“PHP, gəlin söhbətə mesaj göndərək” istifadəçi deyir.
“Buyurun, {username}”, PHP deyir.
VKontakte mesajlar bazasını sıfırdan yenidən yazın və sağ qalın
Bu sxemin mənfi cəhətləri var. Sinxronizasiya hələ də PHP-nin məsuliyyətidir. Böyük söhbətlər və onlara eyni vaxtda mesaj göndərən istifadəçilər təhlükəli hekayədir. Mətn mühərrikinin nümunəsi uid-dən asılı olduğundan, söhbət iştirakçıları eyni mesajı müxtəlif vaxtlarda ala bilər. Tərəqqi dayansaydı, bununla yaşaya bilərdi. Amma bu baş verməyəcək.

2015-ci ilin sonunda icma mesajlarını işə saldıq, 2016-cı ilin əvvəlində isə onlar üçün API istifadəyə verdik. İcmalarda böyük chatbotların meydana çıxması ilə hətta yük paylanmasını unutmaq mümkün oldu.

Yaxşı bir bot gündə bir neçə milyon mesaj yaradır - hətta ən çox danışan istifadəçilər bununla öyünə bilməz. Bu o deməkdir ki, bu cür botların yaşadığı mətn mühərrikinin bəzi nümunələri maksimum dərəcədə əziyyət çəkməyə başladı.

2016-cı ildə mesaj mühərrikləri 100 chat-üzv və üzv-çat nümunəsi və 8000 mətn mühərrikidir. Onlar hər biri 64 GB yaddaşa malik min serverdə yerləşdirilib. İlk təcili tədbir olaraq yaddaşı daha 32 GB artırdıq. Proqnozları təxmin etdik. Kəskin dəyişikliklər olmasa, bu, təxminən bir il üçün kifayət edərdi. Ya hardware əldə etməlisiniz, ya da verilənlər bazalarını optimallaşdırmalısınız.

Arxitekturanın təbiətinə görə, yalnız avadanlığı çoxaltmaq mənasızdır. Yəni, avtomobillərin sayını ən azı iki dəfə artırmaq - açıq-aydın, bu, kifayət qədər bahalı yoldur. Biz optimallaşdıracağıq.

Yeni konsepsiya

Yeni yanaşmanın mərkəzi mahiyyəti söhbətdir. Söhbətdə onunla əlaqəli mesajların siyahısı var. İstifadəçinin söhbətlərinin siyahısı var.

Tələb olunan minimum iki yeni verilənlər bazasıdır:

  • söhbət mühərriki. Bu söhbət vektorlarının deposudur. Hər bir söhbətin ona aid mesaj vektoru var. Hər bir mesajın mətni və söhbət daxilində unikal mesaj identifikatoru var - chat_local_id.
  • istifadəçi mühərriki. Bu, istifadəçilərin vektorlarının saxlanmasıdır - istifadəçilərə bağlantılar. Hər bir istifadəçinin peer_id vektoru (həmsöhbətlər - digər istifadəçilər, multi-chat və ya icmalar) və mesajların vektoru var. Hər bir peer_id-də ona aid mesajların vektoru var. Hər bir mesajın chat_local_id və həmin istifadəçi üçün unikal mesaj ID-si var - user_local_id.

VKontakte mesajlar bazasını sıfırdan yenidən yazın və sağ qalın
Yeni klasterlər TCP-dən istifadə edərək bir-biri ilə əlaqə qurur - bu, sorğuların ardıcıllığının dəyişməməsini təmin edir. Sorğuların özləri və onlar üçün təsdiqlər sabit diskdə qeyd olunur - beləliklə, nasazlıqdan və ya mühərriki yenidən işə saldıqdan sonra istənilən vaxt növbənin vəziyyətini bərpa edə bilərik. İstifadəçi mühərriki və söhbət mühərriki hər biri 4 min parça olduğundan, klasterlər arasında sorğu növbəsi bərabər paylanacaq (lakin əslində heç biri yoxdur - və çox tez işləyir).

Verilənlər bazamızda disklə işləmək əksər hallarda ikili dəyişikliklər jurnalının (binlog), statik anlıq görüntülərin və yaddaşdakı qismən təsvirin birləşməsinə əsaslanır. Gün ərzində edilən dəyişikliklər binlog-a yazılır və cari vəziyyətin anlıq görüntüsü vaxtaşırı yaradılır. Snapshot məqsədlərimiz üçün optimallaşdırılmış məlumat strukturlarının toplusudur. O, başlıqdan (şəklin metaindeksi) və metafayllar dəstindən ibarətdir. Başlıq daimi olaraq RAM-da saxlanılır və snapshotdan məlumatların harada axtarılacağını göstərir. Hər bir metafayl zamanın yaxın nöqtələrində lazım ola biləcək məlumatları ehtiva edir, məsələn, bir istifadəçi ilə əlaqəli. Snapshot başlığından istifadə edərək verilənlər bazasını sorğuladığınız zaman tələb olunan metafayl oxunur və sonra snapshot yaradıldıqdan sonra binlogda baş verən dəyişikliklər nəzərə alınır. Bu yanaşmanın faydaları haqqında daha çox oxuya bilərsiniz burada.

Eyni zamanda, sabit diskdəki məlumatlar gündə yalnız bir dəfə dəyişir - Moskvada gecə gec saatlarda, yük minimal olduqda. Bunun sayəsində (diskdəki strukturun gün ərzində sabit olduğunu bilərək) vektorları sabit ölçülü massivlərlə əvəz edə bilərik - və bunun sayəsində yaddaş qazanırıq.

Yeni sxemdə mesaj göndərilməsi belə görünür:

  1. PHP backend mesaj göndərmək tələbi ilə istifadəçi mühərriki ilə əlaqə saxlayır.
  2. istifadəçi mühərriki sorğunu istədiyiniz chat-mühərriki nümunəsinə etibar edir, o, istifadəçi mühərrikinə chat_local_id - bu söhbət daxilində yeni mesajın unikal identifikatoruna qayıdır. Daha sonra chat_engine mesajı söhbətdəki bütün alıcılara yayımlayır.
  3. user-engine chat-engine-dən chat_local_id alır və user_local_id-i PHP-yə qaytarır - bu istifadəçi üçün unikal mesaj identifikatoru. Bu identifikator daha sonra, məsələn, API vasitəsilə mesajlarla işləmək üçün istifadə olunur.

VKontakte mesajlar bazasını sıfırdan yenidən yazın və sağ qalın
Ancaq əslində mesaj göndərməklə yanaşı, daha bir neçə vacib şeyi həyata keçirməlisiniz:

  • Alt siyahılar, məsələn, söhbət siyahısını açarkən gördüyünüz ən son mesajlardır. Oxunmamış mesajlar, etiketli mesajlar (“Vacib”, “Spam” və s.).
  • Söhbət mühərrikində mesajların sıxılması
  • İstifadəçi mühərrikində mesajların keşləşdirilməsi
  • Axtarış (bütün dialoqlar vasitəsilə və müəyyən bir dialoq daxilində).
  • Real vaxt yeniləməsi (Longpolling).
  • Mobil müştərilərdə keşləməni həyata keçirmək üçün tarixçənin saxlanması.

Bütün alt siyahılar sürətlə dəyişən strukturlardır. Onlarla işləmək üçün istifadə edirik Yay ağacları. Bu seçim onunla izah olunur ki, ağacın yuxarı hissəsində bəzən snapshotdan mesajların bütöv bir seqmentini saxlayırıq - məsələn, gecə yenidən indeksləşdirmədən sonra ağac alt siyahının bütün mesajlarını ehtiva edən bir yuxarıdan ibarətdir. Splay ağacı balanslaşdırma haqqında düşünmədən belə bir təpənin ortasına daxil etməyi asanlaşdırır. Bundan əlavə, Splay lazımsız məlumatları saxlamır, bu da yaddaşımıza qənaət edir.

Mesajlar sıxışdırmaq üçün faydalı olan çoxlu sayda məlumatı, əsasən mətni ehtiva edir. Hətta bir fərdi mesajı dəqiq şəkildə arxivdən çıxara bilməyimiz vacibdir. Mesajları sıxışdırmaq üçün istifadə olunur Huffman alqoritmi öz evristikamızla - məsələn, bilirik ki, mesajlarda sözlər "qeyri-sözlər" - boşluqlar, durğu işarələri ilə əvəzlənir və rus dili üçün simvollardan istifadənin bəzi xüsusiyyətlərini də xatırlayırıq.

Çatlardan daha az istifadəçi olduğundan, təsadüfi giriş disk sorğularını chat-mühərrikində saxlamaq üçün biz mesajları istifadəçi mühərrikində keşləyirik.

Mesaj axtarışı istifadəçi mühərrikindən bu istifadəçinin söhbətlərini ehtiva edən bütün söhbət mühərriki nümunələrinə diaqonal sorğu kimi həyata keçirilir. Nəticələr istifadəçi mühərrikinin özündə birləşdirilir.

Yaxşı, bütün detallar nəzərə alındı, yalnız yeni bir sxemə keçmək qalır - və tercihen istifadəçilər bunu fərq etmədən.

Məlumat miqrasiyası

Beləliklə, bizdə istifadəçinin mesajlarını saxlayan mətn mühərriki və çoxlu söhbət otaqları və onlarda olan istifadəçilər haqqında məlumatları saxlayan iki qrup chat-üzvləri və üzv-çatlar var. Bundan yeni istifadəçi mühərrikinə və söhbət mühərrikinə necə keçmək olar?

Köhnə sxemdəki üzv söhbətləri əsasən optimallaşdırma üçün istifadə edilmişdir. Biz ondan lazımi məlumatları tez bir zamanda chat-üzvlərinə ötürdük və o, artıq miqrasiya prosesində iştirak etmədi.

Çat üzvləri üçün növbə. Buraya 100 nümunə daxildir, chat-mühərrikində isə 4 min. Məlumatları ötürmək üçün onu uyğunlaşdırmaq lazımdır - bunun üçün chat üzvləri eyni 4 min nüsxəyə bölündü və sonra söhbət mühərrikində chat üzvlərinin binloqunun oxunması işə salındı.
VKontakte mesajlar bazasını sıfırdan yenidən yazın və sağ qalın
İndi chat-mühərriki chat üzvlərindən çoxlu söhbət haqqında bilir, lakin iki həmsöhbətlə dialoqlar haqqında hələ heç nə bilmir. Bu cür dialoqlar istifadəçilərə istinadla mətn mühərrikində yerləşir. Burada məlumatları "baş-başa" götürdük: hər bir söhbət mühərriki nümunəsi bütün mətn mühərriki nümunələrindən lazım olan dialoqun olub olmadığını soruşdu.

Əla - chat-mühərriki çoxlu söhbətlərin nə olduğunu bilir və hansı dialoqların olduğunu bilir.
Çox söhbətli söhbətlərdə mesajları birləşdirməlisiniz ki, hər bir söhbətdə mesajların siyahısını əldə edəsiniz. Birincisi, söhbət mühərriki bu söhbətdən bütün istifadəçi mesajlarını mətn mühərrikindən alır. Bəzi hallarda onların sayı kifayət qədər çoxdur (yüz milyonlara qədər), lakin çox nadir istisnalarla söhbət tamamilə RAM-a uyğun gəlir. Hər biri bir neçə nüsxədə sıralanmamış mesajlarımız var - axı, hamısı istifadəçilərə uyğun gələn müxtəlif mətn mühərriki nümunələrindən götürülüb. Məqsəd mesajları çeşidləmək və lazımsız yer tutan nüsxələrdən xilas olmaqdır.

Hər bir mesajın göndərildiyi vaxtı və mətni ehtiva edən vaxt möhürü var. Çeşidləmə üçün vaxtdan istifadə edirik - biz multichat iştirakçılarının ən köhnə mesajlarına göstəricilər yerləşdiririk və artan vaxt damğasına doğru irəliləyərək nəzərdə tutulan nüsxələrin mətnindən hashləri müqayisə edirik. Nüsxələrin eyni hash və vaxt damğasına sahib olması məntiqlidir, lakin praktikada bu həmişə belə olmur. Yadınızdadırsa, köhnə sxemdə sinxronizasiya PHP tərəfindən həyata keçirilirdi - nadir hallarda isə eyni mesajın göndərilmə vaxtı müxtəlif istifadəçilər arasında fərqlənirdi. Bu hallarda, biz özümüzə vaxt damğasını redaktə etməyə icazə verdik - adətən bir saniyə ərzində. İkinci problem müxtəlif alıcılar üçün mesajların fərqli sıralanmasıdır. Belə hallarda biz müxtəlif istifadəçilər üçün müxtəlif sifariş variantları ilə əlavə nüsxənin yaradılmasına icazə verdik.

Bundan sonra, multichatdakı mesajlar haqqında məlumat istifadəçi mühərrikinə göndərilir. Və burada idxal olunan mesajların xoşagəlməz xüsusiyyəti gəlir. Normal əməliyyatda mühərrikə gələn mesajlar ciddi şəkildə user_local_id tərəfindən artan qaydada sıralanır. Köhnə mühərrikdən istifadəçi mühərrikinə idxal edilən mesajlar bu faydalı xüsusiyyətini itirdi. Eyni zamanda, testin rahatlığı üçün onlara tez daxil olmaq, onlarda nəsə axtarmaq və yenilərini əlavə etmək lazımdır.

Biz idxal edilmiş mesajları saxlamaq üçün xüsusi məlumat strukturundan istifadə edirik.

Ölçü vektorunu təmsil edir VKontakte mesajlar bazasını sıfırdan yenidən yazın və sağ qalınhamı haradadır VKontakte mesajlar bazasını sıfırdan yenidən yazın və sağ qalın - fərqlidir və elementlərin xüsusi sırası ilə azalan ardıcıllıqla düzülür. İndekslərlə hər bir seqmentdə VKontakte mesajlar bazasını sıfırdan yenidən yazın və sağ qalın elementlər sıralanır. Belə strukturda elementin axtarışı vaxt tələb edir VKontakte mesajlar bazasını sıfırdan yenidən yazın və sağ qalın vasitəsilə VKontakte mesajlar bazasını sıfırdan yenidən yazın və sağ qalın ikili axtarışlar. Elementin əlavəsi amortizasiya olunur VKontakte mesajlar bazasını sıfırdan yenidən yazın və sağ qalın.

Beləliklə, məlumatları köhnə mühərriklərdən yenilərinə necə ötürəcəyimizi anladıq. Amma bu proses bir neçə gün çəkir - və çətin ki, bu günlər ərzində istifadəçilərimiz bir-birlərinə yazmaq vərdişindən əl çəksinlər. Bu müddət ərzində mesajları itirməmək üçün biz həm köhnə, həm də yeni klasterlərdən istifadə edən iş sxeminə keçirik.

Məlumat chat üzvlərinə və istifadəçi mühərrikinə yazılır (köhnə sxemə görə normal işdə olduğu kimi mətn mühərrikinə deyil). user-engine chat-engine sorğusunu proksiləşdirir - və burada davranış bu söhbətin artıq birləşdirilib-qovuşmamasından asılıdır. Əgər söhbət hələ birləşdirilməyibsə, söhbət mühərriki mesajı özünə yazmır və onun işlənməsi yalnız mətn mühərrikində baş verir. Əgər söhbət artıq chat mühərrikinə birləşdirilibsə, o, chat_local_id-i istifadəçi mühərrikinə qaytarır və mesajı bütün alıcılara göndərir. istifadəçi mühərriki bütün məlumatları mətn mühərrikinə proksiləşdirir - belə ki, nəsə baş verərsə, biz həmişə köhnə mühərrikdə bütün cari məlumatları saxlayaraq geri dönə bilərik. text-engine user_local_id-i qaytarır, istifadəçi mühərriki onu saxlayır və arxa tərəfə qaytarır.
VKontakte mesajlar bazasını sıfırdan yenidən yazın və sağ qalın
Nəticədə keçid prosesi belə görünür: biz boş istifadəçi-mühərriki və chat-motor klasterlərini birləşdiririk. chat-engine bütün söhbət üzvlərinin binlogunu oxuyur, sonra yuxarıda təsvir edilən sxemə uyğun olaraq proxying başlayır. Köhnə məlumatları köçürür və iki sinxronlaşdırılmış klaster alırıq (köhnə və yeni). Qalan tək şey oxunu mətn mühərrikindən istifadəçi mühərrikinə keçmək və proksiinqi söndürməkdir.

Tapıntılar

Yeni yanaşma sayəsində mühərriklərin bütün performans göstəriciləri təkmilləşdirildi və məlumatların ardıcıllığı ilə bağlı problemlər həll edildi. İndi biz mesajlarda yeni funksiyaları tez bir zamanda tətbiq edə bilərik (və artıq bunu etməyə başlamışıq - söhbət iştirakçılarının maksimum sayını artırdıq, yönləndirilmiş mesajlar üçün axtarış həyata keçirdik, sabitlənmiş mesajları işə saldıq və hər istifadəçiyə düşən mesajların ümumi sayına limiti artırdıq) .

Məntiqdəki dəyişikliklər həqiqətən çox böyükdür. Və qeyd etmək istərdim ki, bu, heç də həmişə nəhəng bir komanda və saysız-hesabsız kod sətirləri tərəfindən bütün illərin inkişafı demək deyil. chat-motoru və istifadəçi mühərriki ilə birlikdə mesajın sıxılması üçün Huffman, Splay ağacları və idxal edilmiş mesajlar üçün struktur kimi bütün əlavə hekayələr 20 min sətirdən az koddur. Və onlar cəmi 3 ay ərzində 10 tərtibatçı tərəfindən yazılmışdır (lakin nəzərə almağa dəyər ki, bütün üç inkişaf etdirici - dünya çempionları idman proqramlaşdırmasında).

Üstəlik, serverlərin sayını iki dəfə artırmaq əvəzinə, biz onların sayını yarıya qədər azaltdıq - indi istifadəçi mühərriki və söhbət mühərriki 500 fiziki maşında yaşayır, yeni sxemdə isə yükləmə üçün böyük boşluq var. Avadanlıqlara çox pul qənaət etdik - təxminən 5 milyon dollar + əməliyyat xərclərinə ildə 750 min dollar.

Biz ən mürəkkəb və genişmiqyaslı problemlər üçün ən yaxşı həll yollarını tapmağa çalışırıq. Bizdə onların çoxu var və buna görə də verilənlər bazası şöbəsində istedadlı tərtibatçılar axtarırıq. Əgər belə problemlərin həllini sevirsinizsə və bilirsinizsə, alqoritmlər və məlumat strukturları haqqında mükəmməl biliyə sahibsinizsə, sizi komandaya qoşulmağa dəvət edirik. Bizimlə əlaqə saxlayın HRətraflı məlumat üçün.

Bu hekayə sizin haqqınızda olmasa belə, tövsiyələrə dəyər verdiyimizi nəzərə alın. Dostuna danış developer vakansiyaları, və sınaq müddətini uğurla başa vurarsa, 100 min rubl bonus alacaqsınız.

Mənbə: www.habr.com

Добавить комментарий