VKontakte mesaj veritabanını sıfırdan yeniden yazın ve hayatta kalın

Kullanıcılarımız yorulmadan birbirlerine mesaj yazıyorlar.
VKontakte mesaj veritabanını sıfırdan yeniden yazın ve hayatta kalın
Bu oldukça fazla. Tüm kullanıcıların tüm mesajlarını okumak için yola çıksanız bu 150 bin yıldan fazla zaman alır. Oldukça ileri düzey bir okuyucu olmanız ve her mesaja bir saniyeden fazla zaman harcamamanız şartıyla.

Bu kadar büyük miktarda veri varken, bu veriyi saklama ve ona erişme mantığının en iyi şekilde oluşturulması kritik önem taşıyor. Aksi takdirde, pek de harika olmayan bir anda, her şeyin yakında ters gideceği açıkça ortaya çıkabilir.

Bizim için bu an bir buçuk yıl önce geldi. Buna nasıl geldik ve sonunda ne oldu - size sırayla anlatıyoruz.

Geçmiş

İlk uygulamada VKontakte mesajları PHP arka uç ve MySQL kombinasyonu üzerinde çalışıyordu. Bu, küçük bir öğrenci web sitesi için tamamen normal bir çözümdür. Ancak bu site kontrolsüz bir şekilde büyüdü ve kendisi için veri yapılarının optimizasyonunu talep etmeye başladı.

2009 yılı sonunda ilk metin motoru deposu yazıldı ve 2010 yılında mesajlar buraya aktarıldı.

Metin motorunda mesajlar bir tür "posta kutusu" olan listelerde saklanıyordu. Bu tür listelerin her biri, tüm bu mesajların sahibi olan kullanıcı kimliği tarafından belirlenir. Bir mesajın bir dizi özelliği vardır: muhatap tanımlayıcı, metin, ekler vb. “Box” içindeki mesaj tanımlayıcısı local_id'dir, hiçbir zaman değişmez ve yeni mesajlara sırayla atanır. “Kutular” bağımsızdır ve motor içinde birbirleriyle senkronize değildir; aralarındaki iletişim PHP düzeyinde gerçekleşir. Metin motorunun veri yapısına ve yeteneklerine içeriden bakabilirsiniz. burada.
VKontakte mesaj veritabanını sıfırdan yeniden yazın ve hayatta kalın
Bu, iki kullanıcı arasındaki yazışmalar için oldukça yeterliydi. Bilin bakalım sonra ne oldu?

Mayıs 2011'de VKontakte, birçok katılımcıyla çoklu sohbet sohbetlerini başlattı. Onlarla çalışmak için iki yeni küme oluşturduk: üye sohbetleri ve sohbet üyeleri. Birincisi kullanıcıların sohbetlerine ilişkin verileri saklar, ikincisi ise sohbetlere göre kullanıcılar hakkındaki verileri saklar. Bu, listelerin yanı sıra, örneğin davet eden kullanıcıyı ve sohbete eklendiği zamanı da içerir.

Kullanıcı “PHP, hadi sohbete mesaj gönderelim” diyor.
PHP “Haydi {username}” diyor.
VKontakte mesaj veritabanını sıfırdan yeniden yazın ve hayatta kalın
Bu planın dezavantajları vardır. Senkronizasyon hala PHP'nin sorumluluğundadır. Büyük sohbetler ve onlara aynı anda mesaj gönderen kullanıcılar tehlikeli bir hikaye. Metin motoru örneği kullanıcı kimliğine bağlı olduğundan, sohbet katılımcıları aynı mesajı farklı zamanlarda alabilir. Eğer ilerleme dursaydı, bununla yaşayabilirdik. Ama bu olmayacak.

2015'in sonunda topluluk mesajlarını, 2016'nın başında da onlara yönelik bir API'yi kullanıma sunduk. Topluluklarda büyük sohbet robotlarının ortaya çıkmasıyla birlikte yük dağıtımını bile unutmak mümkün oldu.

İyi bir bot günde birkaç milyon mesaj üretir; en konuşkan kullanıcılar bile bununla övünemez. Bu, bu tür botların üzerinde yaşadığı bazı metin motoru örneklerinin sonuna kadar zarar görmeye başladığı anlamına geliyor.

2016'daki mesaj motorları, 100 sohbet üyesi ve üye sohbeti örneği ve 8000 metin motorudur. Her biri 64 GB belleğe sahip bin sunucuda barındırılıyordu. İlk acil önlem olarak hafızayı 32 GB daha artırdık. Tahminleri tahmin ettik. Köklü değişiklikler olmasaydı, bu yaklaşık bir yıl daha yeterli olurdu. Ya donanıma sahip olmanız ya da veritabanlarını kendiniz optimize etmeniz gerekir.

Mimarinin doğası gereği donanımı kat kat artırmak mantıklıdır. Yani, en azından araba sayısını ikiye katlamak - açıkçası bu oldukça pahalı bir yol. Optimize edeceğiz.

Yeni konsept

Yeni yaklaşımın temel özü sohbettir. Bir sohbetin kendisiyle ilgili mesajların bir listesi vardır. Kullanıcının bir sohbet listesi vardır.

Gerekli minimum iki yeni veritabanıdır:

  • sohbet motoru. Burası sohbet vektörlerinin deposudur. Her sohbetin kendisiyle ilgili bir mesaj vektörü vardır. Her mesajın sohbet içinde bir metni ve benzersiz bir mesaj tanımlayıcısı vardır - chat_local_id.
  • kullanıcı motoru. Bu, kullanıcı vektörlerinin (kullanıcılara bağlantıların) depolandığı yerdir. Her kullanıcının birpeer_id vektörü (muhataplar - diğer kullanıcılar, çoklu sohbet veya topluluklar) ve bir mesaj vektörü vardır. Her birpeer_id'nin kendisiyle ilgili bir mesaj vektörü vardır. Her mesajın bir chat_local_id'si ve o kullanıcı için benzersiz bir mesaj kimliği vardır - user_local_id.

VKontakte mesaj veritabanını sıfırdan yeniden yazın ve hayatta kalın
Yeni kümeler birbirleriyle TCP kullanarak iletişim kurar; bu, isteklerin sırasının değişmemesini sağlar. İsteklerin kendisi ve bunlara ilişkin onaylar sabit sürücüye kaydedilir; böylece, motorun bir arızası veya yeniden başlatılmasından sonra herhangi bir zamanda kuyruğun durumunu geri yükleyebiliriz. Kullanıcı motoru ve sohbet motorunun her biri 4 bin parça olduğundan, kümeler arasındaki istek sırası eşit olarak dağıtılacaktır (ancak gerçekte hiç yoktur ve çok hızlı çalışır).

Veritabanlarımızda diskle çalışmak çoğu durumda ikili değişiklik günlüğü (binlog), statik anlık görüntüler ve bellekteki kısmi görüntünün birleşimine dayanır. Gün içindeki değişiklikler bir binlog'a yazılır ve mevcut durumun anlık görüntüsü periyodik olarak oluşturulur. Anlık görüntü, amaçlarımız için optimize edilmiş veri yapılarının bir koleksiyonudur. Bir başlıktan (görüntünün metaindex'i) ve bir dizi meta dosyadan oluşur. Başlık kalıcı olarak RAM'de saklanır ve anlık görüntüdeki verilerin nerede aranacağını gösterir. Her meta dosyası, yakın zamanda ihtiyaç duyulabilecek verileri (örneğin, tek bir kullanıcıyla ilgili) içerir. Anlık görüntü başlığını kullanarak veritabanını sorguladığınızda, gerekli meta dosya okunur ve ardından anlık görüntü oluşturulduktan sonra binlogda meydana gelen değişiklikler dikkate alınır. Bu yaklaşımın faydaları hakkında daha fazla bilgi edinebilirsiniz burada.

Aynı zamanda, sabit sürücüdeki veriler günde yalnızca bir kez değişir - yükün minimum olduğu Moskova'da gece geç saatlerde. Bu sayede (disk üzerindeki yapının gün boyunca sabit olduğunu bilerek), vektörleri sabit boyuttaki dizilerle değiştirmeyi göze alabiliyoruz ve bu sayede bellek kazancı elde edebiliyoruz.

Yeni şemada mesaj göndermek şuna benzer:

  1. PHP arka ucu, bir mesaj gönderme isteği ile kullanıcı motoruyla iletişim kurar.
  2. kullanıcı motoru, isteği istenen sohbet motoru örneğine proxy olarak gönderir ve bu, bu sohbet içindeki yeni bir mesajın benzersiz tanımlayıcısı olan kullanıcı motoru chat_local_id'ye geri döner. Chat_engine daha sonra mesajı sohbetteki tüm alıcılara yayınlar.
  3. user-engine, chat-engine'den chat_local_id'yi alır ve user_local_id'yi bu kullanıcı için benzersiz bir mesaj tanımlayıcısı olan PHP'ye döndürür. Bu tanımlayıcı daha sonra örneğin API aracılığıyla mesajlarla çalışmak için kullanılır.

VKontakte mesaj veritabanını sıfırdan yeniden yazın ve hayatta kalın
Ancak gerçekten mesaj göndermenin yanı sıra birkaç önemli şeyi daha uygulamanız gerekir:

  • Alt listeler, örneğin, görüşme listesini açarken gördüğünüz en son mesajlardır. Okunmamış mesajlar, etiketli mesajlar (“Önemli”, “Spam” vb.).
  • Sohbet motorunda mesajları sıkıştırma
  • Kullanıcı motorunda iletilerin önbelleğe alınması
  • Arama (tüm diyaloglar aracılığıyla ve belirli bir diyalog içinde).
  • Gerçek zamanlı güncelleme (Uzun oylama).
  • Mobil istemcilerde önbelleğe alma uygulamak için geçmişi kaydetme.

Tüm alt listeler hızla değişen yapılardır. Onlarla çalışmak için kullanıyoruz Ağaçları yaymak. Bu seçim, ağacın tepesinde bazen bir anlık görüntüden gelen mesajların tüm bir bölümünü sakladığımız gerçeğiyle açıklanmaktadır - örneğin, her gece yeniden indekslemeden sonra ağaç, alt listedeki tüm mesajları içeren bir üst kısımdan oluşur. Splay ağacı, dengelemeyi düşünmenize gerek kalmadan böyle bir tepe noktasının ortasına yerleştirmeyi kolaylaştırır. Ayrıca Splay gereksiz verileri saklamaz, bu da bize hafıza tasarrufu sağlar.

Mesajlar, çoğunlukla metin olmak üzere, sıkıştırılabilmesi yararlı olan büyük miktarda bilgi içerir. Tek bir mesajı bile doğru bir şekilde arşivden çıkarabilmemiz önemlidir. Mesajları sıkıştırmak için kullanılır Huffman algoritması kendi buluşsal yöntemlerimizle - örneğin, mesajlarda kelimelerin "kelime olmayan" (boşluklar, noktalama işaretleri) ile değiştiğini biliyoruz ve ayrıca Rus dili için sembol kullanmanın bazı özelliklerini de hatırlıyoruz.

Kullanıcı sayısı sohbetlere göre çok daha az olduğundan, rastgele erişimli disk isteklerini sohbet motorunda kaydetmek için mesajları kullanıcı motorunda önbelleğe alıyoruz.

Mesaj arama, kullanıcı motorundan bu kullanıcının sohbetlerini içeren tüm sohbet motoru örneklerine çapraz bir sorgu olarak uygulanır. Sonuçlar kullanıcı motorunun kendisinde birleştirilir.

Tüm ayrıntılar dikkate alındı, geriye kalan tek şey yeni bir şemaya geçmek - ve tercihen kullanıcılar bunu fark etmeden.

Veri göçü

Yani, mesajları kullanıcıya göre saklayan bir metin motorumuz ve çoklu sohbet odaları ve bu odalardaki kullanıcılar hakkındaki verileri saklayan iki grup sohbet üyesi ve üye sohbetimiz var. Bundan yeni kullanıcı motoruna ve sohbet motoruna nasıl geçilir?

Eski şemadaki üye sohbetleri öncelikli olarak optimizasyon için kullanılıyordu. Gerekli verileri hızlı bir şekilde sohbet üyelerine aktardık ve ardından artık geçiş sürecine katılmadı.

Sohbet üyeleri için kuyruk. 100 örnek içerirken, sohbet motorunda 4 bin örnek var. Verileri aktarmak için uyumlu hale getirmeniz gerekir - bunun için sohbet üyeleri aynı 4 bin kopyaya bölündü ve ardından sohbet motorunda sohbet üyelerinin binlogunun okunması etkinleştirildi.
VKontakte mesaj veritabanını sıfırdan yeniden yazın ve hayatta kalın
Artık sohbet motoru sohbet üyelerinden çoklu sohbeti biliyor ancak iki muhatapla yapılan diyaloglar hakkında henüz hiçbir şey bilmiyor. Bu tür diyaloglar metin motorunda kullanıcılara referansla yerleştirilmiştir. Burada verileri "doğrudan" ele aldık: her sohbet motoru örneği, tüm metin motoru örneklerine ihtiyaç duydukları diyaloğun olup olmadığını sordu.

Harika - sohbet motoru hangi çoklu sohbet sohbetlerinin olduğunu biliyor ve hangi diyalogların olduğunu biliyor.
Her sohbette bir mesaj listesi elde etmek için çoklu sohbet sohbetlerindeki mesajları birleştirmeniz gerekir. İlk olarak, sohbet motoru bu sohbetteki tüm kullanıcı mesajlarını metin motorundan alır. Bazı durumlarda oldukça fazla sayıda bulunur (yüz milyonlarcaya kadar), ancak çok nadir istisnalar dışında sohbet tamamen RAM'e sığar. Her biri birkaç kopya halinde olan sırasız mesajlarımız var - sonuçta hepsi kullanıcılara karşılık gelen farklı metin motoru örneklerinden alınıyor. Amaç, mesajları sıralamak ve gereksiz yer kaplayan kopyalardan kurtulmaktır.

Her mesajın gönderildiği zamanı ve metni içeren bir zaman damgası vardır. Sıralama için zamanı kullanırız - çoklu sohbet katılımcılarının en eski mesajlarına işaretçiler yerleştiririz ve amaçlanan kopyaların metnindeki karmaları karşılaştırarak zaman damgasını artırmaya doğru ilerleriz. Kopyaların aynı karma ve zaman damgasına sahip olması mantıklıdır ancak pratikte durum her zaman böyle değildir. Hatırlayacağınız gibi, eski şemadaki senkronizasyon PHP tarafından gerçekleştiriliyordu ve nadir durumlarda, aynı mesajın gönderilme zamanı farklı kullanıcılar arasında farklılık gösteriyordu. Bu durumlarda, zaman damgasını genellikle bir saniye içinde düzenlememize izin verdik. İkinci sorun, farklı alıcılar için mesajların farklı sırasıdır. Bu gibi durumlarda, farklı kullanıcılar için farklı sipariş seçenekleriyle fazladan bir kopyanın oluşturulmasına izin verdik.

Bundan sonra çoklu sohbetteki mesajlarla ilgili veriler kullanıcı motoruna gönderilir. Ve burada içe aktarılan mesajların hoş olmayan bir özelliği ortaya çıkıyor. Normal çalışmada, motora gelen mesajlar user_local_id'ye göre kesinlikle artan sırada sıralanır. Eski motordan kullanıcı motoruna aktarılan mesajlar bu yararlı özelliği kaybetmiştir. Aynı zamanda testin kolaylığı için bunlara hızlı bir şekilde erişebilmeniz, içlerinde bir şeyler arayabilmeniz ve yenilerini ekleyebilmeniz gerekir.

İçe aktarılan mesajları saklamak için özel bir veri yapısı kullanıyoruz.

boyutunda bir vektörü temsil eder VKontakte mesaj veritabanını sıfırdan yeniden yazın ve hayatta kalınherkes nerede VKontakte mesaj veritabanını sıfırdan yeniden yazın ve hayatta kalın - farklıdır ve özel bir öğe sırası ile azalan sırada sıralanmıştır. Endeksli her segmentte VKontakte mesaj veritabanını sıfırdan yeniden yazın ve hayatta kalın elemanlar sıralanır. Böyle bir yapıda eleman aramak zaman alır VKontakte mesaj veritabanını sıfırdan yeniden yazın ve hayatta kalın sayesinde VKontakte mesaj veritabanını sıfırdan yeniden yazın ve hayatta kalın ikili aramalar Bir unsurun eklenmesi, üzerinden itfa edilir VKontakte mesaj veritabanını sıfırdan yeniden yazın ve hayatta kalın.

Böylece verileri eski motorlardan yenilerine nasıl aktaracağımızı bulduk. Ancak bu süreç birkaç gün sürüyor ve bu günlerde kullanıcılarımızın birbirlerine yazma alışkanlığından vazgeçmeleri pek mümkün değil. Bu süre zarfında mesajların kaybolmaması için hem eski hem de yeni kümelerin kullanıldığı bir çalışma düzenine geçiyoruz.

Veriler sohbet üyelerine ve kullanıcı motoruna yazılır (eski şemaya göre normal operasyonda olduğu gibi metin motoruna değil). user-engine, isteği chat-engine'e proxy olarak gönderir - ve burada davranış, bu sohbetin zaten birleştirilmiş olup olmadığına bağlıdır. Sohbet henüz birleştirilmemişse, sohbet motoru mesajı kendisine yazmaz ve işlenmesi yalnızca metin motorunda gerçekleşir. Sohbet zaten sohbet motoruyla birleştirilmişse, kullanıcı motoruna chat_local_id değerini döndürür ve mesajı tüm alıcılara gönderir. user-engine tüm verileri text-engine'e proxy olarak gönderir; böylece bir şey olursa, her zaman geri dönebiliriz ve mevcut tüm verileri eski motorda saklayabiliriz. text-engine, user-engine'ın depoladığı ve arka uca geri döndürdüğü user_local_id değerini döndürür.
VKontakte mesaj veritabanını sıfırdan yeniden yazın ve hayatta kalın
Sonuç olarak geçiş süreci şuna benziyor: boş kullanıcı motoru ve sohbet motoru kümelerini birbirine bağlıyoruz. sohbet motoru, sohbet üyelerinin binlog'unun tamamını okur, ardından yukarıda açıklanan şemaya göre proxy oluşturma başlar. Eski verileri aktarıyoruz ve iki senkronize küme (eski ve yeni) alıyoruz. Geriye kalan tek şey okumayı metin motorundan kullanıcı motoruna geçirmek ve proxy'yi devre dışı bırakmak.

Bulgular

Yeni yaklaşım sayesinde motorların tüm performans metrikleri iyileştirildi ve veri tutarlılığıyla ilgili sorunlar çözüldü. Artık mesajlara hızlı bir şekilde yeni özellikler uygulayabiliyoruz (ve bunu zaten yapmaya başladık - maksimum sohbet katılımcısı sayısını artırdık, iletilen mesajlar için bir arama uyguladık, sabitlenmiş mesajları başlattık ve kullanıcı başına toplam mesaj sayısı sınırını yükselttik) .

Mantıktaki değişiklikler gerçekten muazzamdır. Ve bunun her zaman büyük bir ekip ve sayısız kod satırı tarafından yıllarca süren geliştirme anlamına gelmediğini belirtmek isterim. sohbet motoru ve kullanıcı motoru ile birlikte mesaj sıkıştırma için Huffman, Splay ağaçları ve içe aktarılan mesajlar için yapı gibi tüm ek hikayeler 20 bin satırdan az koddur. Ve bunlar sadece 3 ayda 10 geliştirici tarafından yazıldı (ancak şunu akılda tutmakta fayda var: tüm üç geliştirici - Dünya şampiyonları spor programcılığında).

Üstelik, sunucu sayısını iki katına çıkarmak yerine yarı yarıya azalttık; artık kullanıcı motoru ve sohbet motoru 500 fiziksel makinede çalışıyor ve yeni planın yük için geniş bir payı var. Ekipmanlardan çok fazla tasarruf ettik; işletme giderlerinden yılda yaklaşık 5 milyon $ + 750 bin $.

En karmaşık ve büyük ölçekli sorunlara en iyi çözümleri bulmaya çalışıyoruz. Bunlardan çok sayıda var ve bu nedenle veritabanı departmanında yetenekli geliştiriciler arıyoruz. Bu tür problemleri nasıl çözeceğinizi seviyor ve biliyorsanız, algoritmalar ve veri yapıları konusunda mükemmel bilginiz varsa, sizi ekibe katılmaya davet ediyoruz. Bizimle iletişime geçin HRdetaylar için.

Bu hikaye sizinle ilgili olmasa bile tavsiyelere değer verdiğimizi lütfen unutmayın. Bir arkadaşına bundan bahset geliştirici açık pozisyonlarıve deneme süresini başarıyla tamamlarsa 100 bin ruble bonus alacaksınız.

Kaynak: habr.com

Yorum ekle