
Artem Denisov ( , )
Badoo dünyanın en büyük arkadaşlık sitesidir. Şu anda dünya çapında yaklaşık 330 milyon kayıtlı kullanıcımız var. Ancak bugünkü sohbetimiz bağlamında çok daha önemli olan şey, yaklaşık 3 petabayt kullanıcı fotoğrafını saklamamızdır. Kullanıcılarımız her gün yaklaşık 3,5 milyon yeni fotoğraf yüklüyor ve okuma yükü yaklaşık Saniyede 80 bin istek. Bu bizim arka uçumuz için oldukça fazla ve bazen bununla ilgili zorluklar yaşanıyor.

Fotoğrafları saklayan ve gönderen bu sistemin tasarımından genel olarak bahsedeceğim ve bir geliştirici gözüyle bakacağım. Nasıl geliştiğine dair kısa bir retrospektif olacak, burada ana kilometre taşlarını özetleyeceğim, ancak yalnızca şu anda kullanmakta olduğumuz çözümler hakkında daha ayrıntılı olarak konuşacağım.
Şimdi başlayalım.

Dediğim gibi bu retrospektif olacak ve bir yerden başlamak için en yaygın örneği ele alalım.

Ortak bir görevimiz var, kullanıcı fotoğraflarını kabul etmemiz, saklamamız ve göndermemiz gerekiyor. Bu formda görev geneldir, her şeyi kullanabiliriz:
- modern bulut depolama,
- artık çok sayıda bulunan kutulu bir çözüm;
- Veri merkezimizde birkaç makine kurup üzerlerine büyük hard diskler yerleştirip fotoğrafları orada saklayabiliriz.
Badoo tarihsel olarak - hem şimdi hem de o zamanlar (henüz başlangıç aşamasındayken) - kendi DC'lerimizin içinde kendi sunucularında yaşıyor. Bu nedenle bu seçenek bizim için en uygunuydu.

Birkaç makine aldık, onlara "fotoğraf" adını verdik ve fotoğrafları saklayan bir kümeye sahip olduk. Ama sanki bir şeyler eksikmiş gibi görünüyor. Tüm bunların işe yaraması için bir şekilde hangi fotoğrafları hangi makinede saklayacağımızı belirlememiz gerekiyor. Burada da Amerika'yı açmaya gerek yok.

Depolama alanımıza kullanıcılar hakkında bilgiler içeren bir alan ekliyoruz. Bu, parçalama anahtarı olacaktır. Bizim durumumuzda buna place_id adını verdik ve bu yer kimliği, kullanıcı fotoğraflarının saklandığı yeri işaret ediyor. Haritalar yapıyoruz.
İlk aşamada bu manuel olarak bile yapılabilir - bu kullanıcının böyle bir yere sahip bir fotoğrafının böyle bir sunucuya düşeceğini söylüyoruz. Bu harita sayesinde bir kullanıcının ne zaman fotoğraf yüklediğini, nereye kaydedeceğini ve nereden vereceğini her zaman biliyoruz.
Bu kesinlikle önemsiz bir plan, ancak oldukça önemli avantajları var. Birincisi dediğim gibi basit olması, ikincisi ise bu yaklaşımla basitçe yeni arabalar teslim edip haritaya ekleyerek yatay olarak kolayca ölçeklenebiliyoruz. Başka bir şey yapmanıza gerek yok.
Bir süredir bizde de durum böyleydi.

Bu 2009 civarındaydı. Arabaları teslim ettiler, teslim ettiler...
Ve bir noktada bu planın bazı dezavantajları olduğunu fark etmeye başladık. Dezavantajları nelerdir?
Öncelikle kapasitemiz kısıtlı. Tek bir fiziksel sunucuya istediğimiz kadar sabit disk sığdıramayız. Bu da zamanla ve veri setinin büyümesiyle birlikte belli bir sorun haline geldi.
Ve ikinci. Bu, makinelerin alışılmadık bir konfigürasyonudur, çünkü bu tür makinelerin diğer bazı kümelerde yeniden kullanılması zordur; performans açısından zayıf olmalılar, ancak aynı zamanda büyük bir sabit diske sahip olmalılar.
Bunların hepsi 2009 yılı içindi, ancak prensip olarak bu gereksinimler bugün hala geçerlidir. Bir retrospektifimiz var, yani 2009'da bununla ilgili her şey tamamen kötüydü.
Ve son nokta fiyattır.

O zamanlar fiyat çok yüksekti ve bazı alternatifler aramamız gerekiyordu. Onlar. hem veri merkezlerindeki alanı hem de tüm bunların bulunduğu fiziksel sunucuları bir şekilde daha iyi kullanmamız gerekiyordu. Sistem mühendislerimiz de birçok farklı seçeneği gözden geçirdikleri geniş bir çalışmaya başladı. Ayrıca PolyCeph ve Lustre gibi kümelenmiş dosya sistemlerine de baktılar. Performans sorunları ve oldukça zor operasyonlar vardı. Reddettiler. Bir şekilde ölçeklendirmek için tüm veri setini NFS aracılığıyla her araca monte etmeye çalıştık. Okuma da kötü gitti, farklı satıcıların farklı çözümlerini denedik.
Ve sonunda Depolama Alanı Ağını kullanmaya karar verdik.

Bunlar, büyük miktarlarda veri depolamak için özel olarak tasarlanmış büyük SHD'lerdir. Son optik çıkış makinelerine monte edilen diskli raflardır. O. oldukça küçük bir tür makine havuzumuz var ve gönderme mantığımıza şeffaf olan bu SHD'ler, yani. nginx'imizin veya başka birinin bu fotoğraflara yönelik istekleri yerine getirmesi için.
Bu kararın bariz avantajları vardı. Bu SHD'dir. Fotoğrafların saklanması amaçlanıyor. Bu, makineleri sabit disklerle donatmaktan daha ucuza gelir.
İkinci artı.

Bu, kapasitenin çok daha büyük hale gelmesidir, yani. çok daha küçük bir hacimde çok daha fazla depolamayı barındırabiliyoruz.
Ancak oldukça hızlı bir şekilde ortaya çıkan dezavantajlar da vardı. Bu sistemdeki kullanıcı sayısı ve yük arttıkça performans sorunları ortaya çıkmaya başladı. Ve buradaki sorun oldukça açık - çok sayıda fotoğrafı küçük bir hacimde depolamak için tasarlanan herhangi bir SHD, kural olarak yoğun okumadan muzdariptir. Bu aslında herhangi bir bulut depolama alanı veya başka herhangi bir şey için geçerlidir. Artık sonsuz ölçeklenebilecek ideal bir depolamaya sahip değiliz, içine her şeyi doldurabilirsiniz ve okumaları çok iyi tolere eder. Özellikle sıradan okumalar.

Fotoğraflarımızda olduğu gibi, çünkü fotoğraflar tutarsız bir şekilde isteniyor ve bu da performanslarını büyük ölçüde etkileyecek.
Hatta bugünkü rakamlara göre depolamanın bağlı olduğu bir makinede fotoğraflar için 500 RPS'nin üzerinde bir yere ulaşırsak sorunlar çoktan başlıyor. Bu bizim için yeterince kötüydü çünkü kullanıcı sayısı artıyor ve işler daha da kötüleşecek. Bunun bir şekilde optimize edilmesi gerekiyor.
Optimize etmek için, o zaman açıkçası yük profiline bakmaya karar verdik - genel olarak ne oluyor, nelerin optimize edilmesi gerekiyor.

Ve burada her şey bizim elimizde.
İlk slaytta zaten söylemiştim: Günde sadece 80 milyon yükleme ve saniyede 3,5 bin okuma isteğimiz var. Yani bu üç büyüklükte bir farktır. Okumanın optimize edilmesi gerektiği açıktır ve bunun nasıl yapılacağı da pratik olarak açıktır.
Küçük bir nokta daha var. Hizmetin özellikleri, bir kişinin kaydolması, bir fotoğraf yüklemesi, ardından kendisi gibi diğer insanlara aktif olarak bakmaya başlaması ve aktif olarak diğer insanlara gösterilmesi şeklindedir. Sonra bir eş bulur ya da bulamaz, nasıl sonuçlanacağına bağlıdır ve bir süreliğine hizmeti kullanmayı bırakır. Şu anda, onu kullandığında fotoğrafları çok sıcak; talep görüyor, pek çok insan onları görüntülüyor. Bunu yapmayı bırakır bırakmaz, daha önce olduğu gibi diğer insanlarla etkileşimden çok hızlı bir şekilde vazgeçiyor ve fotoğrafları neredeyse hiç istenmiyor.

Onlar. Çok küçük bir sıcak veri setimiz var. Ancak aynı zamanda ona yönelik birçok talep var. Ve burada tamamen açık bir çözüm, bir önbellek eklemektir.
LRU'lu bir önbellek tüm sorunlarımızı çözecektir. Biz ne yapıyoruz?

Photocache adı verilen depolama alanı olan büyük kümemizin önüne nispeten küçük bir tane daha ekliyoruz. Bu aslında sadece bir önbellek proxy'sidir.
İçeriden nasıl çalışıyor? İşte kullanıcımız, işte depolama alanı. Her şey eskisi gibi. Araya ne ekleyeceğiz?

Bu yalnızca fiziksel yerel diski olan ve hızlı olan bir makinedir. Bu, örneğin bir SSD ile olur. Ve bu diskte bir tür yerel önbellek depolanıyor.
Nasıl görünüyor? Kullanıcı bir fotoğraf için istek gönderir. NGINX bunu ilk olarak yerel önbellekte arar. Değilse, depolama alanımıza proxy_pass gönderin, fotoğrafı oradan indirin ve kullanıcıya verin.
Ama bu çok sıradan ve içeride ne olduğu belli değil. Bunun gibi bir şey çalışıyor.

Önbellek mantıksal olarak üç katmana bölünmüştür. “Üç katman” dediğimde bu, karmaşık bir sistemin olduğu anlamına gelmiyor. Hayır, bunlar şartlı olarak dosya sistemindeki yalnızca üç dizindir:
- Bu, bir proxy'den yeni indirilen fotoğrafların gittiği bir arabellektir.
- Bu, şu anda aktif olarak istenen fotoğrafları saklayan etkin bir önbellektir.
- Ve fotoğrafların kendilerine daha az istek geldiğinde yavaş yavaş sıcak önbellekten dışarı itildiği soğuk bir önbellek.
Bunun işe yaraması için bir şekilde bu önbelleği yönetmemiz, içindeki fotoğrafları yeniden düzenlememiz vb. gerekiyor. Bu aynı zamanda çok ilkel bir süreçtir.

Nginx, her istek için RAMDisk erişim.log dosyasına yazar; burada o anda sunulan fotoğrafın yolunu (tabii ki göreceli yol) ve hangi bölüme sunulduğunu belirtir. Onlar. "fotoğraf 1" diyebilir ve ardından arabellek, sıcak önbellek, soğuk önbellek veya proxy yazabilir.
Buna bağlı olarak fotoğrafla ne yapacağımıza bir şekilde karar vermemiz gerekiyor.
Her makinede çalışan, bu günlüğü sürekli okuyan ve belirli fotoğrafların kullanımına ilişkin istatistikleri hafızasında saklayan küçük bir arka plan programımız var.

Sadece orada topluyor, sayaçları tutuyor ve periyodik olarak aşağıdakileri yapıyor. Aktif olarak talep edilen ve çok sayıda talep bulunan fotoğrafları, nerede olurlarsa olsunlar sıcak önbelleğe taşıyor.

Nadiren talep edilen ve daha az talep edilen fotoğraflar, yavaş yavaş sıcak önbellekten soğuk önbelleğe aktarılır.

Önbellekte yer kalmadığında, soğuk önbellekteki her şeyi ayrım gözetmeksizin silmeye başlarız. Ve bu arada, bu iyi çalışıyor.
Fotoğrafın ara belleğe proxy olarak aktarılırken hemen kaydedilmesi için proxy_store direktifini kullanıyoruz ve arabellek de bir RAMDisk'tir, yani. kullanıcı için çok hızlı çalışır. Bu, önbellekleme sunucusunun dahili bileşenleriyle ilgilidir.
Geriye kalan soru, isteklerin bu sunucular arasında nasıl dağıtılacağıdır.
Diyelim ki yirmi depolama makinesi ve üç önbellekleme sunucusundan oluşan bir küme var (bu şekilde oldu).

Hangi isteklerin hangi fotoğraflar için olduğunu ve bunları nereye ulaştıracağımızı bir şekilde belirlememiz gerekiyor.
En yaygın seçenek Round Robin'dir. Yoksa tesadüfen mi yaptın?
Bunun elbette bir takım dezavantajları var çünkü böyle bir durumda önbelleği çok verimsiz kullanıyor olacağız. İstekler bazı rastgele makinelere düşecektir: burada önbelleğe alınmıştır, ancak bir sonrakinde artık orada değildir. Ve eğer tüm bunlar işe yararsa, çok kötü olacak. Kümede az sayıda makine olsa bile.
Hangi sunucuya hangi isteği göndereceğimizi bir şekilde net bir şekilde belirlememiz gerekiyor.
Banal bir yol var. URL'den hash'i veya URL'deki parçalama anahtarımızdan hash'i alıp sunucu sayısına bölüyoruz. Çalışacak? İrade.

Onlar. %2 isteğimiz var, örneğin bazı "example_url" için her zaman "XNUMX" indeksiyle sunucuya gidecek ve önbellek sürekli olarak mümkün olan en iyi şekilde imha edilecek.
Ancak böyle bir şemada yeniden parçalamada bir sorun var. Yeniden Paylaşma - Sunucu sayısını değiştirmeyi kastediyorum.
Önbellekleme kümemizin artık başa çıkamayacağını ve başka bir makine eklemeye karar verdiğimizi varsayalım.
Ekleyelim.

Artık her şey üçe değil dörde bölünebiliyor. Böylece eskiden sahip olduğumuz anahtarların neredeyse tamamı, URL'lerin neredeyse tamamı artık başka sunucularda yaşıyor. Önbelleğin tamamı bir an için geçersiz kılındı. Tüm talepler depolama kümemize düştü, kötüleşti, hizmet hatası oluştu ve kullanıcılar memnun kalmadı. Bunu yapmak istemiyorum.
Bu seçenek de bize uymuyor.
O. ne yapmalıyız? Bir şekilde önbelleği verimli bir şekilde kullanmalı, aynı isteği aynı sunucuya tekrar tekrar göndermeli, ancak yeniden parçalamaya karşı dirençli olmalıyız. Ve öyle bir çözüm var ki, o kadar da karmaşık değil. Buna tutarlı karma denir.

Neye benziyor?

Parçalama anahtarından bir miktar fonksiyon alıp tüm değerlerini çembere yayıyoruz. Onlar. 0 noktasında minimum ve maksimum değerleri birleşir. Daha sonra tüm sunucularımızı yaklaşık olarak şu şekilde aynı daireye yerleştiriyoruz:

Her sunucu bir nokta ile tanımlanır ve buna göre saat yönünde giden sektöre bu ana bilgisayar hizmet eder. İstekler bize geldiğinde, örneğin A isteğinin - orada bir karma değeri var - ve sunucu 2 tarafından sunulduğunu hemen görüyoruz. B isteği - sunucu 3 tarafından vb.

Yeniden parçalama sırasında bu durumda ne olur?

Daha önce olduğu gibi tüm önbelleği geçersiz kılmıyoruz ve tüm anahtarları kaydırmıyoruz, ancak her sektörü kısa bir mesafeye kaydırıyoruz, böylece göreceli olarak eklemek istediğimiz altıncı sunucumuz boş alana sığacak ve oraya ekliyoruz.

Tabii böyle bir durumda anahtarlar da dışarı çıkıyor. Ancak eskisinden çok daha zayıf hareket ediyorlar. Ve ilk iki anahtarımızın sunucularında kaldığını ve önbellek sunucusunun sadece son anahtar için değiştiğini görüyoruz. Bu oldukça verimli çalışıyor ve eğer yeni ana bilgisayarları aşamalı olarak eklerseniz, burada büyük bir sorun olmaz. Her seferinde biraz ekleyip eklersiniz, önbellek tekrar dolana kadar beklersiniz ve her şey yolunda gider.
Geriye tek soru retlerle ilgili kalıyor. Bir tür arabanın arızalı olduğunu varsayalım.

Ve örneğin makine yeniden başlatıldıysa ve bir şekilde hizmet isteklerine ihtiyacımız varsa, şu anda bu haritayı gerçekten yeniden oluşturmak, önbelleğin bir kısmını geçersiz kılmak vb. istemeyiz. Her tesiste, şu anda kapalı olan herhangi bir makinenin yerine geçecek bir yedek fotoğraf önbelleği tutuyoruz. Ve aniden sunucularımızdan biri kullanılamaz hale gelirse trafik oraya gider. Doğal olarak orada herhangi bir önbelleğimiz yok, yani. hava soğuk ama en azından kullanıcı istekleri işleniyor. Eğer bu kısa bir aralıksa, o zaman bunu tamamen sakin bir şekilde yaşarız. Depolamada daha fazla yük var. Bu aralık uzunsa, o zaman zaten bir karar verebiliriz - bu sunucuyu haritadan kaldırıp kaldırmamak veya belki onu bir başkasıyla değiştirmek.
Bu önbellekleme sistemiyle ilgili. Sonuçlara bakalım.
Görünüşe göre burada karmaşık bir şey yok. Ancak önbelleği yönetmeye yönelik bu yöntem bize yaklaşık %98'lik bir hile oranı verdi. Onlar. Saniyede bu 80 bin istekten sadece 1600'ü depolamaya ulaşıyor ve bu tamamen normal bir yük, sakince dayanıyorlar, her zaman bir rezervimiz var.
Bu sunucuları üç DC'mize yerleştirdik ve üç varlık noktası aldık: Prag, Miami ve Hong Kong.

O. hedef pazarlarımızın her birinde aşağı yukarı yerel olarak konumlanmış durumdalar.
Ve güzel bir bonus olarak, içerik sunmak için pek gerekli olmadığından CPU'nun aslında boşta olduğu bu önbellek proxy'sine sahibiz. Ve orada NGINX+ Lua'yı kullanarak pek çok faydacı mantık uyguladık.

Örneğin, webp veya progresif jpeg (bunlar etkili modern formatlardır) ile deneyler yapabilir, trafiği nasıl etkilediğini görebilir, bazı kararlar alabilir, belirli ülkeler için etkinleştirebilir vb.; anında dinamik yeniden boyutlandırma yapın veya fotoğrafları kırpın.
Bu, örneğin fotoğrafları görüntüleyen bir mobil uygulamanız varsa ve mobil uygulama, büyük bir fotoğraf isteyip ardından onu belirli bir boyuta yeniden boyutlandırarak istemcinin CPU'sunu harcamak istemediğinde, bu iyi bir kullanım durumudur. görünüm. UPort koşullu URL'sinde bazı parametreleri dinamik olarak belirleyebiliriz ve fotoğraf önbelleği, fotoğrafın kendisini yeniden boyutlandıracaktır. Kural olarak, diskte fiziksel olarak sahip olduğumuz boyutu, istenen boyuta mümkün olduğunca yakın olarak seçecek ve onu belirli koordinatlarda aşağı satacaktır.
Bu arada, yüksek yüklü sistem geliştiricileri konferansının son beş yılına ait video kayıtlarını kamuya açık hale getirdik. . İzleyin, öğrenin, paylaşın ve abone olun .
Oraya bir çok ürün mantığını da ekleyebiliriz. Örneğin URL parametrelerini kullanarak farklı filigranlar ekleyebilir, fotoğrafları bulanıklaştırabilir, bulanıklaştırabilir veya piksellendirebiliriz. Bu, bir kişinin fotoğrafını göstermek istediğimiz ama yüzünü göstermek istemediğimiz zamandır, bu iyi çalışıyor, hepsi burada uygulandı.
Ne elde ettik? Üç varlık noktamız var, iyi bir kandırma oranımız var ve aynı zamanda bu makinelerde boşta kalan CPU'muz da yok. Artık elbette eskisinden daha önemli hale geldi. Kendimize daha güçlü arabalar vermeliyiz ama buna değer.
Bu, fotoğrafların iadesiyle ilgilidir. Burada her şey oldukça açık ve nettir. Sanırım Amerika'yı ben keşfetmedim, neredeyse tüm CDN'ler bu şekilde çalışıyor.
Ve büyük olasılıkla, bilgili bir dinleyicinin bir sorusu olabilir: Neden her şeyi CDN olarak değiştirmiyorsunuz? Hemen hemen aynı olacaktır; tüm modern CDN'ler bunu yapabilir. Ve bunun birçok nedeni var.
Bunlardan ilki fotoğraflardır.

Bu, altyapımızın kilit noktalarından biridir ve üzerinde mümkün olduğunca fazla kontrole ihtiyacımız var. Bu, üçüncü taraf bir satıcının sunduğu bir tür çözümse ve bunun üzerinde herhangi bir gücünüz yoksa, büyük bir veri kümeniz olduğunda ve çok büyük bir akışınız olduğunda bununla yaşamak sizin için oldukça zor olacaktır. kullanıcı istekleri.
Sana bir örnek vereyim. Artık altyapımızla, mesela bir sorun olduğunda ya da yeraltında bir çökme olduğunda, göreceli olarak makinenin yanına gidip orada oyalanabiliyoruz. Yalnızca ihtiyacımız olan bazı metriklerin koleksiyonunu ekleyebiliriz, bir şekilde deney yapabiliriz, bunun grafikleri nasıl etkilediğini görebiliriz vb. Artık bu önbelleğe alma kümesinde çok sayıda istatistik toplanıyor. Ve periyodik olarak ona bakıyoruz ve bazı anormallikleri keşfetmek için uzun zaman harcıyoruz. CDN tarafında olsaydı kontrol edilmesi çok daha zor olurdu. Veya örneğin bir kaza olursa ne olduğunu biliyoruz, bununla nasıl yaşayacağımızı, bunun üstesinden nasıl geleceğimizi biliyoruz. Bu ilk sonuçtur.
İkinci sonuç da oldukça tarihsel çünkü sistem uzun süredir gelişiyor ve farklı aşamalarda birçok farklı iş gereksinimi vardı ve bunlar her zaman CDN konseptine uymuyor.
Ve bir öncekinden çıkan nokta şu:

Bunun nedeni, fotoğraf önbelleklerinde her zaman istek üzerine eklenemeyen çok sayıda özel mantığa sahip olmamızdır. Herhangi bir CDN'nin isteğiniz üzerine size bazı özel şeyler eklemesi pek olası değildir. Örneğin, istemcinin bir şeyi değiştirmesini istemiyorsanız URL'leri şifrelemek. Sunucudaki URL'yi değiştirip şifrelemek ve ardından bazı dinamik parametreleri buraya göndermek mi istiyorsunuz?
Bu nasıl bir sonuca işaret ediyor? Bizim durumumuzda CDN pek iyi bir alternatif değil.

Ve sizin durumunuzda, herhangi bir özel iş gereksiniminiz varsa, o zaman size gösterdiğim şeyi kendiniz kolayca uygulayabilirsiniz. Ve bu, benzer bir yük profiliyle mükemmel şekilde çalışacaktır.
Ancak bir tür genel çözümünüz varsa ve görev çok spesifik değilse, kesinlikle güvenli bir şekilde CDN alabilirsiniz. Veya zaman ve kaynaklar sizin için kontrolden daha önemliyse.

Ve modern CDN'ler size şimdi anlattığım hemen hemen her şeye sahiptir. Artı eksi bazı özellikler hariç.
Bu fotoğrafların dağıtılmasıyla ilgili.
Şimdi retrospektifimizde biraz ilerleyelim ve depolamadan bahsedelim.
2013 geçiyordu.

Önbellekleme sunucuları eklendi, performans sorunları ortadan kalktı. Herşey yolunda. Veri kümesi büyüyor. 2013 yılı itibariyle, depolamaya bağlı yaklaşık 80 sunucumuz ve her DC'de yaklaşık 40 önbelleğe alma sunucumuz vardı. Bu, her DC'de 560 terabaytlık veridir; toplamda yaklaşık bir petabayt.

Veri kümesinin büyümesiyle birlikte işletme maliyetleri de önemli ölçüde artmaya başladı. Bu ne anlama geliyordu?

Bir SAN ile, ona bağlı makineler ve önbelleklerle çizilen bu şemada birçok arıza noktası var. Sunucuları önbelleğe alma sorununu daha önce çözmüş olsaydık, her şey az çok öngörülebilir ve anlaşılırdı, ancak depolama tarafında her şey çok daha kötüydü.
Birincisi, başarısız olabilecek Depolama Alanı Ağının (SAN) kendisidir.
İkinci olarak, optik aracılığıyla son makinelere bağlanır. Optik kartlarda ve bujilerde sorun olabilir.

Elbette SAN'ın kendisinde olduğu kadar çok sayıda yok ama yine de bunlar aynı zamanda başarısızlık noktalarıdır.
Sırada depoya bağlı olan makinenin kendisi var. Ayrıca başarısız olabilir.

Toplamda üç başarısızlık noktamız var.
Ayrıca, arıza noktalarına ek olarak, depolamanın kendisi de yoğun bakım gerektirir.
Bu karmaşık, çok bileşenli bir sistemdir ve sistem mühendisleri bununla çalışırken zor anlar yaşayabilir.
Ve son, en önemli nokta. Bu üç noktadan herhangi biri başarısız olursa, dosya sistemi çökebileceği için kullanıcı verilerini kaybetme şansımız sıfırdır.

Diyelim ki dosya sistemimiz bozuldu. İlk olarak, kurtarılması uzun zaman alır; büyük miktarda veri nedeniyle bir hafta sürebilir. İkincisi, sonunda büyük olasılıkla, bir şekilde kullanıcı fotoğraflarıyla birleştirilmesi gereken bir sürü anlaşılmaz dosyayla karşılaşacağız. Ve veri kaybetme riskiyle karşı karşıyayız. Risk oldukça yüksektir. Ve bu tür durumlar ne kadar sık meydana gelirse ve tüm zincirde ne kadar çok sorun ortaya çıkarsa, bu risk de o kadar yüksek olur.
Bu konuda bir şeyler yapılması gerekiyordu. Ve sadece verileri yedeklememiz gerektiğine karar verdik. Bu aslında bariz bir çözüm ve iyi bir çözüm. Ne yaptık?

Sunucumuz daha önce depolamaya bağlandığında böyle görünüyordu. Bu bir ana bölümdür, aslında optik aracılığıyla uzaktan depolama için bir montajı temsil eden bir blok cihazdır.
Hemen ikinci bir bölüm ekledik.

Yanına ikinci bir depolama alanı yerleştirdik (neyse ki para açısından o kadar da pahalı değil) ve buna yedekleme bölümü adını verdik. Ayrıca optik aracılığıyla bağlanır ve aynı makinede bulunur. Ancak bir şekilde aralarındaki verileri senkronize etmemiz gerekiyor.
Burada yakınlarda eşzamansız bir kuyruk oluşturuyoruz.

Pek meşgul değil. Yeterli kayıtlarımızın olmadığını biliyoruz. Sıra, MySQL'de "bu fotoğrafı yedeklemeniz gerekiyor" gibi satırların yazıldığı bir tablodur. Herhangi bir değişiklik veya yüklemede, asenkron veya bir tür arka plan çalışanını kullanarak ana bölümden yedeklemeye kopyalarız.
Ve böylece her zaman iki tutarlı bölümümüz olur. Bu sistemin bir parçası arızalansa bile her zaman bir yedek ile ana bölümü değiştirebiliriz ve her şey çalışmaya devam eder.
Ancak bundan dolayı okuma yükü büyük ölçüde artıyor çünkü... ana bölümden okuyan müşterilere ek olarak, çünkü önce oradaki fotoğrafa bakarlar (orada daha yenidir) ve daha sonra bulamadılarsa yedekte ararlar (ancak NGINX bunu yapar), sistemimizde ayrıca bir artı yedek artık ana bölümden okunuyor. Bu bir darboğaz değildi ama aslında yükü bu şekilde artırmak istemedim.
Ve küçük bir SSD olan üçüncü bir disk ekledik ve ona arabellek adını verdik.

Şimdi nasıl çalışıyor?
Kullanıcı ara belleğe bir fotoğraf yükler, ardından kuyruğa iki bölüme kopyalanması gerektiğini belirten bir olay atılır. Kopyalanır ve fotoğraf bir süre (örneğin bir gün) arabellekte kalır ve ancak o zaman oradan temizlenir. Bu, kullanıcı deneyimini büyük ölçüde artırır, çünkü kullanıcı bir fotoğraf yükler, kural olarak istekler hemen takip etmeye başlar veya kendisi sayfayı güncelleyip yeniler. Ancak bunların hepsi yüklemeyi yapan uygulamaya bağlıdır.
Veya örneğin kendini göstermeye başladığı diğer kişiler bu fotoğrafın ardından hemen istek gönderiyorlar. Henüz önbellekte değil; ilk istek çok hızlı bir şekilde gerçekleşiyor. Esasen fotoğraf önbelleğindekiyle aynı. Yavaş depolamanın bu konuyla hiçbir ilgisi yok. Ve bir gün sonra temizlendiğinde, ya zaten önbellekleme katmanımızda önbelleğe alınmıştır ya da büyük olasılıkla artık kimsenin ona ihtiyacı yoktur. Onlar. Buradaki kullanıcı deneyimi, bu kadar basit manipülasyonlar sayesinde çok iyi bir şekilde arttı.
Ve en önemlisi: veri kaybını durdurduk.

Diyelim ki durduk potansiyel Verileri kaybederiz, çünkü aslında onu kaybetmedik. Ama tehlike vardı. Bu çözümün elbette iyi olduğunu görüyoruz ama sorunu tamamen çözmek yerine, biraz belirtilerini hafifletmeye benziyor. Ve burada bazı sorunlar devam ediyor.
Birincisi, bu, tüm bu mekanizmanın üzerinde çalıştığı fiziksel ana bilgisayarın formundaki bir arıza noktasıdır;

İkincisi, SAN'larda hala sorunlar var, ağır bakımları vb. devam ediyor. Kritik bir faktör değildi ama bir şekilde onsuz yaşamaya çalışmak istedim.
Ve üçüncü versiyonu (aslında ikinci versiyonu) - rezervasyon versiyonunu yaptık. Ne benziyordu?
İşte bu...

Temel sorunlarımız bunun fiziksel bir konak olması gerçeğidir.
Öncelikle SAN'ları kaldırıyoruz çünkü deneme yapmak istiyoruz, sadece yerel sabit diskleri denemek istiyoruz.

Bu zaten 2014-2015 ve o zamanlar disklerin durumu ve bir ana bilgisayardaki kapasiteleri çok daha iyi hale geldi. Neden denememeye karar verdik.
Daha sonra yedekleme bölümümüzü alıp fiziksel olarak ayrı bir makineye aktarıyoruz.

Böylece bu diyagramı elde ederiz. Aynı veri setlerini saklayan iki arabamız var. Birbirlerini tamamen yedeklerler ve verileri aynı MySQL'deki eşzamansız bir kuyruk aracılığıyla ağ üzerinden senkronize ederler.

Bunun iyi çalışmasının nedeni elimizde çok az kayıt bulunmasıdır. Onlar. Eğer yazmak, okumakla karşılaştırılabilir olsaydı, belki de bir tür ağ yükü ve sorunlarımız olurdu. Çok az yazı, çok fazla okuma var; bu yöntem işe yarıyor, yani. Bu iki sunucu arasında nadiren fotoğraf kopyalıyoruz.
Biraz daha detaylı bakarsanız bu nasıl çalışıyor?

Yüklemek. Dengeleyici basitçe bir çift ile rastgele ana bilgisayarları seçer ve ona yükler. Aynı zamanda doğal olarak sağlık kontrollerini de yapıyor ve arabanın düşmediğinden emin oluyor. Onlar. fotoğrafları yalnızca canlı bir sunucuya yüklüyor ve ardından eşzamansız bir kuyruk aracılığıyla bunların tamamı komşusuna kopyalanıyor. Yükleme ile her şey son derece basittir.
Görev biraz daha zor.

Lua bize burada yardımcı oldu çünkü vanilya NGINX üzerinde böyle bir mantık oluşturmak zor olabilir. İlk önce ilk sunucuya bir istekte bulunuyoruz, fotoğrafın orada olup olmadığına bakıyoruz, çünkü potansiyel olarak örneğin bir komşuya yüklenmiş olabilir, ancak henüz buraya ulaşmamıştır. Fotoğraf oradaysa, bu iyi. Bunu hemen müşteriye veriyoruz ve muhtemelen önbelleğe alıyoruz.

Eğer orada değilse, komşumuza bir talepte bulunuruz ve oradan almamız garanti edilir.

O. yine şunu söyleyebiliriz: performansta sorunlar olabilir, çünkü sürekli gidiş-dönüşler oluyor - fotoğraf yüklendi, burada değil, bir yerine iki istek yapıyoruz, bu yavaş çalışmalı.
Bizim durumumuzda bu yavaş yavaş işe yaramıyor.

Bu sistemde bir dizi ölçüm topluyoruz ve böyle bir mekanizmanın koşullu akıllı oranı yaklaşık %95'tir. Onlar. Bu yedeklemenin gecikmesi küçüktür ve bu nedenle fotoğraf yüklendikten sonra onu ilk kez alacağımız ve iki kez hiçbir yere gitmemize gerek kalmayacağı neredeyse garantidir.
Peki elimizde gerçekten harika olan başka ne var?
Daha önce ana yedekleme bölümümüz vardı ve bunları sırayla okuyorduk. Onlar. Her zaman önce ana olanı, sonra da yedeği aradık. Tek bir hareketti.
Artık aynı anda iki makineden okumayı kullanıyoruz. Talepleri Round Robin kullanarak dağıtıyoruz. Vakaların küçük bir yüzdesinde iki talepte bulunuruz. Ancak genel olarak, artık daha önce sahip olduğumuzdan iki kat daha fazla okuma stoğumuz var. Ve hem gönderme makinelerindeki hem de o zamanlar sahip olduğumuz doğrudan depolama makinelerindeki yük büyük ölçüde azaldı.
Hata toleransına gelince. Aslında esas olarak bunun için savaştık. Hata toleransı ile burada her şey harika çıktı.

Bir araba bozuluyor.

Sorun değil! Sistem mühendisi gece uyanmayabilir, sabaha kadar bekler, kötü bir şey olmaz.
Bu makine arızalansa bile, sıra bozuksa, herhangi bir sorun da yoksa, günlük önce canlı makinede toplanacak, sonra kuyruğa ve sonra da arabaya eklenecektir. bir süre sonra faaliyete geçecektir.

Bakım konusunda da aynı şey geçerli. Makinelerden birini kapatıyoruz, tüm havuzlardan manuel olarak çekiyoruz, trafik almayı bırakıyor, bir tür bakım yapıyoruz, bir şeyi düzenliyoruz, sonra tekrar hizmete veriyoruz ve bu yedekleme oldukça hızlı bir şekilde yetişiyor. Onlar. Günde bir arabanın aksama süresi birkaç dakika içinde tamamlanıyor. Bu gerçekten çok az. Hata toleransı ile tekrar söylüyorum, burada her şey yolunda.
Bu işten çıkarma planından ne gibi sonuçlar çıkarılabilir?
Hata toleransımız var.
Kullanımı kolay. Makinelerde yerel sabit diskler bulunduğundan, onunla çalışan mühendisler için operasyonel açıdan bu çok daha uygundur.
Çift okuma ödeneği aldık.
Bu, hata toleransının yanı sıra çok iyi bir bonus.
Ancak sorunlar da var. Şimdi bununla ilgili bazı özelliklerin çok daha karmaşık bir geliştirmesine sahibiz çünkü sistem sonunda %100 tutarlı hale geldi.

Diyelim ki, arka plandaki bir işte sürekli şunu düşünmeliyiz: "Şu anda hangi sunucuda çalışıyoruz?", "Burada gerçekten güncel bir fotoğraf var mı?" vesaire. Elbette bunların hepsi özetlenmiş ve iş mantığını yazan programcı için şeffaftır. Ancak yine de bu büyük karmaşık katman ortaya çıktı. Ama bundan aldığımız güzellikler karşılığında buna katlanmaya hazırız.
Ve burada yine bazı çatışmalar ortaya çıkıyor.
Başlangıçta her şeyi yerel sabit disklerde saklamanın kötü olduğunu söylemiştim. Şimdi de beğendiğimizi söylüyorum.
Evet, aslında zamanla durum çok değişti ve artık bu yaklaşımın birçok avantajı var. Öncelikle çok daha basit bir operasyon elde ediyoruz.
İkincisi, daha üretken çünkü bu otomatik denetleyicilere veya disk raflarına bağlantılara sahip değiliz.
Orada çok büyük miktarda makine var ve bunlar burada, makineye bir baskın için monte edilen birkaç diskten ibaret.
Ama dezavantajları da var.

Bu, günümüz fiyatlarıyla bile SAN kullanmaktan yaklaşık 1,5 kat daha pahalıdır. Bu nedenle, büyük kümemizin tamamını cesurca yerel sabit disklere sahip arabalara dönüştürmemeye ve hibrit bir çözüm bırakmaya karar verdik.
Makinelerimizin yarısı sabit disklerle çalışıyor (yani yarısı değil, muhtemelen yüzde 30'u). Geri kalanı ise ilk rezervasyon planına sahip olan eski arabalardır. Yeni verilere veya başka bir şeye ihtiyacımız olmadığından, bağlantıları bir fiziksel ana bilgisayardan iki ana bilgisayara taşıdık.
Artık geniş bir okuma stoğumuz var ve onu genişlettik. Daha önce bir makineye bir depolama monte ettiysek, şimdi örneğin bir çifte dört depolama monte ediyoruz. Ve gayet iyi çalışıyor.
Neleri başardık, ne için mücadele ettik, başarılı olup olmadığımızı kısaca özetleyelim.
sonuçlar
33 milyon kadar kullanıcımız var.
Üç noktada varlığımız var: Prag, Miami, Hong Kong.
Tüm bunları işleyen ve önbelleği yöneten, NGINX'in basit makinelerinin, erişim.log'unun ve Python arka plan programlarının çalıştığı hızlı yerel disklere (SSD'ler) sahip arabalardan oluşan bir önbellekleme katmanı içerirler.
Arzu ederseniz, projenizdeyseniz, fotoğraflar bizim için olduğu kadar sizin için de kritik değilse veya geliştirme hızı ve kaynak maliyetleri karşısında denge kontrolü sizin için ters yöndeyse, o zaman güvenle değiştirebilirsiniz. Bir CDN ile modern CDN'ler iyi iş çıkarıyorlar.
Daha sonra, birbirini yedekleyen makine çiftlerinden oluşan kümelerimizin bulunduğu depolama katmanı gelir; dosyalar değiştikçe, dosyalar birinden diğerine eşzamansız olarak kopyalanır.
Üstelik bu makinelerin bir kısmı yerel sabit disklerle çalışıyor.
Bu makinelerin bazıları SAN'lara bağlıdır.

Ve bir yandan kullanımı daha uygun ve biraz daha verimli, diğer yandan yerleştirme yoğunluğu ve gigabayt başına fiyat açısından uygun.
Bu, sahip olduğumuz mimariye ve bunların nasıl geliştiğine dair kısa bir genel bakış.
Kaptandan birkaç ipucu daha, çok basit.
Birincisi, aniden fotoğraf altyapınızdaki her şeyi acilen iyileştirmeniz gerektiğine karar verirseniz, önce ölçüm yapın, çünkü belki de hiçbir şeyin iyileştirilmesine gerek yoktur.

Sana bir örnek vereyim. Sohbetlerdeki eklerden fotoğraf gönderen bir grup makinemiz var ve program 2009'dan beri orada çalışıyor ve kimse bundan muzdarip değil. Herkes iyidir, herkes her şeyi sever.
Ölçmek için önce bir dizi metrik asın, onlara bakın ve ardından neyden memnun olmadığınıza ve nelerin iyileştirilmesi gerektiğine karar verin. Bunu ölçmek için Pinba adında harika bir aracımız var.
Her istek ve yanıt kodu için NGINX'ten çok ayrıntılı istatistikler ve zamanların dağılımı - ne isterseniz toplamanıza olanak tanır. Her türden farklı analiz sistemine bağlantıları vardır ve o zaman hepsine güzel bir şekilde bakabilirsiniz.
Önce ölçtük, sonra geliştirdik.
Daha öte. Okumayı önbellekle, yazmayı ise parçalamayla optimize ediyoruz ancak bu açık bir nokta.

Daha öte. Sisteminizi şimdi oluşturmaya yeni başlıyorsanız, fotoğrafları değişmez dosyalar olarak oluşturmak çok daha iyidir. Çünkü önbellek geçersiz kılma, mantığın fotoğrafın doğru sürümünü nasıl bulması gerektiği vb. ile ilgili bir dizi sorunu anında kaybedersiniz.

Diyelim ki yüz tane yüklediniz, sonra döndürdünüz ve fiziksel olarak farklı bir dosya haline getirdiniz. Onlar. Düşünmeye gerek yok: şimdi biraz yer kazanacağım, onu aynı dosyaya yazacağım, sürümü değiştireceğim. Bu her zaman işe yaramaz ve daha sonra çok fazla baş ağrısına neden olur.
Sonraki nokta. Anında yeniden boyutlandırma hakkında.
Daha önce, kullanıcılar bir fotoğraf yüklediğinde, tüm durumlar için, farklı müşteriler için hemen bir sürü boyutu kesiyorduk ve hepsi diskteydi. Artık bundan vazgeçtik.
Yalnızca üç ana boyut bıraktık: küçük, orta ve büyük. Uport'ta bize istenen boyutun arkasındaki boyuttan geri kalan her şeyin ölçeğini küçültüyoruz, sadece küçültmeyi yapıp kullanıcıya veriyoruz.
Buradaki önbellek katmanının CPU'su, bu boyutları her depolamada sürekli olarak yeniden oluşturduğumuz durumdan çok daha ucuz çıkıyor. Diyelim ki yeni bir tane eklemek istiyoruz, bu bir ay sürecek - tüm bunları kümeyi bozmadan düzgün bir şekilde yapacak bir komut dosyasını her yerde çalıştırın. Onlar. Şimdi seçme fırsatınız varsa, mümkün olduğunca az fiziksel boyut yapmak daha iyidir, ancak en azından dağıtımın bir kısmı, diyelim ki üç olsun. Ve diğer her şey, hazır modüller kullanılarak anında yeniden boyutlandırılabilir. Artık her şey çok kolay ve erişilebilir.
Artımlı eşzamansız yedekleme de iyidir.
Uygulamamızın gösterdiği gibi, bu şema, değiştirilen dosyaların gecikmeli kopyalanmasıyla harika çalışıyor.

Son nokta da açıktır. Eğer altyapınızda şu an bu tür sorunlar yoksa ama bozulabilecek bir şey varsa, biraz daha olunca mutlaka kırılacaktır. Bu nedenle bunu önceden düşünmek ve sorun yaşamamak daha iyidir. Söylemek istediğim tek şey buydu.
Kişiler
»
»
Bu rapor, yüksek yüklü sistem geliştiricileri konferansındaki en iyi konuşmalardan birinin transkriptidir . HighLoad++ 2017 konferansına bir aydan az süre kaldı.
Onu zaten hazırladık , program şu anda aktif olarak oluşturuluyor.
Bu yıl mimariler ve ölçeklendirme konusunu keşfetmeye devam ediyoruz:
- / İgor Vasilyev
- / Dmitri Egorov
- / Anatoly Plaskovski
- / Roman Shekhovtsov, Alexey Gromatchikov
- / Philip Delgado
Bu materyallerden bazılarını, yüksek yüklü sistemlerin geliştirilmesine ilişkin çevrimiçi eğitim kursumuzda da kullanıyoruz. özel olarak seçilmiş mektuplar, makaleler, materyaller, videolardan oluşan bir zincirdir. Ders kitabımız halihazırda 30'dan fazla benzersiz materyal içermektedir. Bağlamak!
Kaynak: habr.com
