NewSQL = NoSQL+ACID

NewSQL = NoSQL+ACID
Yakın zamana kadar Odnoklassniki, SQL Server'da gerçek zamanlı olarak işlenen yaklaşık 50 TB veriyi depoluyordu. Böyle bir hacim için, SQL DBMS kullanarak hızlı, güvenilir ve hatta veri merkezine hata toleranslı erişim sağlamak neredeyse imkansızdır. Genellikle bu gibi durumlarda NoSQL depolarından biri kullanılır, ancak her şey NoSQL'e aktarılamaz: bazı varlıklar ACID işlem garantileri gerektirir.

Bu bizi NewSQL depolamayı, yani NoSQL sistemlerinin hata toleransını, ölçeklenebilirliğini ve performansını sağlayan, ancak aynı zamanda klasik sistemlere aşina olan ACID garantilerini koruyan bir DBMS'yi kullanmaya yöneltti. Bu yeni sınıfta çalışan endüstriyel sistem sayısı çok az olduğundan böyle bir sistemi kendimiz hayata geçirdik ve ticari işletmeye aldık.

Nasıl çalışıyor ve ne oldu - kesimin altını okuyun.

Bugün Odnoklassniki'nin aylık izleyici kitlesi 70 milyondan fazla tekil ziyaretçidir. Biz İlk beşteyiz Dünyanın en büyük sosyal ağları arasında yer alan ve kullanıcıların en çok vakit geçirdiği yirmi site arasında yer alıyor. OK altyapısı çok yüksek yükleri yönetir: cephe başına bir milyondan fazla HTTP isteği/sn. 8000'den fazla parçadan oluşan bir sunucu filosunun parçaları, dört Moskova veri merkezinde birbirine yakın konumda bulunuyor ve bu da aralarında 1 ms'den daha az bir ağ gecikmesine izin veriyor.

Cassandra'yı 2010 yılından beri 0.6 sürümünden itibaren kullanıyoruz. Bugün faaliyette olan birkaç düzine küme var. En hızlı küme saniyede 4 milyondan fazla işlemi gerçekleştirir ve en büyük depolar 260 TB'dir.

Ancak bunların hepsi depolama için kullanılan sıradan NoSQL kümeleridir. zayıf koordine veri. Odnoklassniki'nin kuruluşundan bu yana kullanılan ana tutarlı depolama alanı olan Microsoft SQL Server'ı değiştirmek istedik. Depolama, 300 TB'lık veri (iş varlıkları) içeren 50'den fazla SQL Server Standard Edition makinesinden oluşuyordu. Bu veriler ACID işlemlerinin bir parçası olarak değiştirilir ve yüksek tutarlılık.

Verileri SQL Server düğümleri arasında dağıtmak için hem dikey hem de yatay kullandık bölümleme (parçalama). Geçmişte basit bir veri parçalama şeması kullanıyorduk: her varlık, varlık kimliğinin bir işlevi olan bir belirteçle ilişkilendiriliyordu. Aynı jetona sahip varlıklar aynı SQL sunucusuna yerleştirildi. Ana ve alt kayıtların belirteçlerinin her zaman eşleşecek ve aynı sunucuda yer alacak şekilde ana-detay ilişkisi uygulandı. Bir sosyal ağda neredeyse tüm kayıtlar kullanıcı adına oluşturulur; bu, bir işlevsel alt sistemdeki tüm kullanıcı verilerinin tek bir sunucuda depolandığı anlamına gelir. Yani, bir ticari işlem neredeyse her zaman tek bir SQL sunucusundaki tabloları içeriyordu; bu da, yerel ACID işlemlerini kullanarak veri tutarlılığının sağlanmasını mümkün kılıyordu. yavaş ve güvenilmez dağıtılmış ACID işlemleri.

Parçalama ve SQL'i hızlandırma sayesinde:

  • Varlık kimliği parçalanırken başka bir sunucuda bulunabileceğinden Yabancı anahtar kısıtlamalarını kullanmıyoruz.
  • DBMS CPU'sunun ek yükü nedeniyle saklı yordamları ve tetikleyicileri kullanmıyoruz.
  • Yukarıdakilerin tümü ve diskten çok sayıda rastgele okuma yapılması nedeniyle JOIN'leri kullanmıyoruz.
  • Bir işlemin dışında, kilitlenmeleri azaltmak için Taahhüt Edilmemiş Okuma izolasyon düzeyini kullanırız.
  • Yalnızca kısa işlemler gerçekleştiriyoruz (ortalama 100 ms'den kısa).
  • Çok sayıda kilitlenme nedeniyle çok satırlı UPDATE ve DELETE kullanmıyoruz; bir seferde yalnızca bir kaydı güncelliyoruz.
  • Sorguları her zaman yalnızca dizinler üzerinde gerçekleştiririz; bizim için tam tablo tarama planına sahip bir sorgu, veritabanının aşırı yüklenmesi ve başarısız olmasına neden olmak anlamına gelir.

Bu adımlar SQL sunucularından neredeyse maksimum performansı elde etmemizi sağladı. Ancak sorunlar giderek çoğaldı. Şimdi onlara bakalım.

SQL'le ilgili sorunlar

  • Kendi kendine yazılan parçalamayı kullandığımızdan, yeni parçaların eklenmesi yöneticiler tarafından manuel olarak yapılıyordu. Bunca zaman boyunca ölçeklenebilir veri kopyaları isteklere hizmet vermiyordu.
  • Tablodaki kayıt sayısı arttıkça ekleme ve değiştirme hızı azalır; mevcut bir tabloya dizinler eklenirken hız bir kat düşer; dizinlerin oluşturulması ve yeniden oluşturulması, kesinti süresiyle birlikte gerçekleşir.
  • Üretimde SQL Server için az miktarda Windows'un bulunması altyapı yönetimini zorlaştırıyor

Ama asıl sorun şu ki

hata toleransı

Klasik SQL sunucusunun hata toleransı zayıftır. Diyelim ki tek bir veritabanı sunucunuz var ve her üç yılda bir arızalanıyor. Bu süre zarfında site 20 dakika kapalı kalacaktır, bu kabul edilebilir bir durumdur. 64 sunucunuz varsa site her üç haftada bir kapanır. Ve 200 sunucunuz varsa site her hafta çalışmaz. Bu bir sorundur.

Bir SQL sunucusunun hata toleransını geliştirmek için ne yapılabilir? Vikipedi bizi inşa etmeye davet ediyor yüksek oranda kullanılabilir küme: Bileşenlerden herhangi birinin arızalanması durumunda yedek bir bileşenin bulunduğu yer.

Bu, pahalı ekipmanlardan oluşan bir filo gerektirir: çok sayıda çoğaltma, optik fiber, paylaşılan depolama ve bir rezervin dahil edilmesi güvenilir bir şekilde çalışmaz: anahtarlamaların yaklaşık %10'u, ana düğümün arkasındaki bir tren gibi yedek düğümün arızalanmasıyla sona erer.

Ancak bu kadar yüksek kullanılabilirliğe sahip bir kümenin ana dezavantajı, bulunduğu veri merkezinin arızalanması durumunda kullanılabilirliğin sıfır olmasıdır. Odnoklassniki'de dört veri merkezi var ve bunlardan birinde tam bir arıza olması durumunda çalışmayı sağlamamız gerekiyor.

Bunun için kullanabiliriz Çoklu Master SQL Server'da yerleşik çoğaltma. Bu çözüm, yazılımın maliyeti nedeniyle çok daha pahalıdır ve eş zamanlı çoğaltmada öngörülemeyen işlem gecikmeleri ve eşzamansız çoğaltmada çoğaltmaların uygulanmasındaki gecikmeler (ve sonuç olarak değişikliklerin kaybolması) gibi çoğaltmayla ilgili iyi bilinen sorunlarla karşı karşıyadır. ima edilen manuel çakışma çözümü bu seçeneği bizim için tamamen uygulanamaz hale getiriyor.

Bütün bu sorunlara radikal bir çözüm gerekiyordu ve biz de bunları detaylı bir şekilde analiz etmeye başladık. Burada SQL Server'ın esas olarak ne yaptığını - işlemler - tanımamız gerekiyor.

Basit işlem

Uygulamalı bir SQL programcısının bakış açısından en basit işlemi ele alalım: bir albüme fotoğraf eklemek. Albümler ve fotoğraflar farklı plakalarda saklanır. Albümün halka açık bir fotoğraf sayacı var. Daha sonra böyle bir işlem aşağıdaki adımlara ayrılır:

  1. Albümü anahtarla kilitliyoruz.
  2. Fotoğraf tablosunda bir giriş oluşturun.
  3. Fotoğrafın genel durumu varsa albüme genel bir fotoğraf sayacı ekleyin, kaydı güncelleyin ve işlemi gerçekleştirin.

Veya sözde kodda:

TX.start("Albums", id);
Album album = albums.lock(id);
Photo photo = photos.create(…);

if (photo.status == PUBLIC ) {
    album.incPublicPhotosCount();
}
album.update();

TX.commit();

En yaygın ticari işlem senaryosunun veri tabanından uygulama sunucusunun hafızasına veri okumak, bir şeyleri değiştirmek ve yeni değerleri tekrar veri tabanına kaydetmek olduğunu görüyoruz. Genellikle böyle bir işlemde birkaç varlığı, birkaç tabloyu güncelleriz.

Bir işlemi yürütürken, aynı verilerde başka bir sistemden eş zamanlı değişiklik meydana gelebilir. Örneğin Antispam, kullanıcının bir şekilde şüpheli olduğuna ve dolayısıyla kullanıcının tüm fotoğraflarının artık herkese açık olmaması gerektiğine, bunların denetlenmeye gönderilmesi gerektiğine karar verebilir; bu, photo.status'un başka bir değerle değiştirilmesi ve ilgili sayaçların kapatılması anlamına gelir. Açıkçası, bu işlem, uygulamanın atomikliği ve rakip değişikliklerin izolasyonu garantisi olmadan gerçekleşirse, aşağıdaki gibi: ASİT, o zaman sonuç ihtiyaç duyulan şey olmayacaktır - ya fotoğraf sayacı yanlış değeri gösterecektir ya da tüm fotoğraflar denetime gönderilmeyecektir.

Odnoklassniki'nin tüm varlığı boyunca, çeşitli ticari varlıkları tek bir işlemde manipüle eden pek çok benzer kod yazılmıştır. NoSQL'e geçiş deneyimine dayanmaktadır. Nihai Tutarlılık En büyük zorluğun (ve zaman yatırımının) veri tutarlılığını korumak için kod geliştirmekten kaynaklandığını biliyoruz. Bu nedenle, yeni depolamanın temel gereksiniminin, uygulama mantığı için gerçek ACID işlemlerinin sağlanması olduğunu düşündük.

Daha az önemli olmayan diğer gereksinimler şunlardı:

  • Veri merkezi arızalanırsa yeni depolama birimine hem okuma hem de yazma özelliğinin mevcut olması gerekir.
  • Mevcut geliştirme hızının sürdürülmesi. Yani, yeni bir depoyla çalışırken kod miktarı yaklaşık olarak aynı olmalıdır; depoya herhangi bir şey eklemeye, çatışmaları çözmek için algoritmalar geliştirmeye, ikincil dizinleri korumaya vb. gerek olmamalıdır.
  • Yeni depolamanın hızının hem verileri okurken hem de işlemleri işlerken oldukça yüksek olması gerekiyordu; bu da akademik açıdan titiz, evrensel ancak yavaş çözümlerin geçerli olmadığı anlamına geliyordu. iki aşamalı taahhütler.
  • Otomatik anında ölçeklendirme.
  • Egzotik donanım satın almaya gerek kalmadan sıradan ucuz sunucuları kullanma.
  • Şirket geliştiricileri tarafından depolama geliştirme imkanı. Başka bir deyişle, tercihen Java'daki özel veya açık kaynaklı çözümlere öncelik verildi.

Kararlar kararlar

Olası çözümleri analiz ederek iki olası mimari seçeneğine ulaştık:

Birincisi, herhangi bir SQL sunucusunu alıp gerekli hata toleransını, ölçeklendirme mekanizmasını, yük devretme kümesini, çakışma çözümünü ve dağıtılmış, güvenilir ve hızlı ACID işlemlerini uygulamaktır. Bu seçeneği son derece önemsiz ve emek yoğun olarak değerlendirdik.

İkinci seçenek, uygulanan ölçeklendirme, yük devretme kümesi, çakışma çözümü ile hazır bir NoSQL depolama alanı almak ve işlemleri ve SQL'i kendiniz uygulamaktır. ACID işlemlerinin yanı sıra SQL'in hayata geçirilmesi işi bile ilk bakışta yıllar sürecek bir iş gibi görünüyor. Ancak daha sonra pratikte kullandığımız SQL özellik setinin ANSI SQL'den bu kadar uzak olduğunu fark ettik. Cassandra CQL ANSI SQL'den uzak. CQL'e daha yakından baktığımızda ihtiyacımız olana oldukça yakın olduğunu fark ettik.

Cassandra ve CQL

Peki Cassandra'yı ilginç kılan ne, hangi yeteneklere sahip?

Öncelikle burada çeşitli veri türlerini destekleyen tablolar oluşturabilir; birincil anahtar üzerinde SELECT veya UPDATE yapabilirsiniz.

CREATE TABLE photos (id bigint KEY, owner bigint,…);
SELECT * FROM photos WHERE id=?;
UPDATE photos SET … WHERE id=?;

Replika verilerinin tutarlılığını sağlamak için Cassandra şunları kullanır: çoğunluk yaklaşımı. En basit durumda bu, aynı satırın üç kopyası kümenin farklı düğümlerine yerleştirildiğinde, düğümlerin çoğunluğunun (yani üçte ikisinin) bu yazma işleminin başarısını onaylaması durumunda yazmanın başarılı olduğu kabul edilir. . Satır verileri, okuma sırasında düğümlerin çoğunluğunun yoklanması ve onaylanması durumunda tutarlı kabul edilir. Böylece üç kopyayla, bir düğümün arızalanması durumunda eksiksiz ve anında veri tutarlılığı garanti edilir. Bu yaklaşım, daha da güvenilir bir plan uygulamamıza olanak sağladı: istekleri her zaman üç kopyanın tümüne gönderin ve en hızlı iki kopyadan yanıt bekleyin. Bu durumda üçüncü kopyanın geç yanıtı atılır. Yanıt vermede geç kalan bir düğüm ciddi sorunlara sahip olabilir - frenler, JVM'de çöp toplama, Linux çekirdeğinde doğrudan bellek geri kazanımı, donanım arızası, ağ bağlantısının kesilmesi. Ancak bu durum müşterinin işlemlerini veya verilerini hiçbir şekilde etkilemez.

Üç düğümle iletişim kurduğumuz ve iki düğümden yanıt aldığımız yaklaşıma denir. spekülasyon: Ekstra kopyalar için bir istek, "düşmeden" önce bile gönderilir.

Cassandra'nın bir diğer faydası da, yaptığınız bir dizi değişikliğin ya tamamen uygulanmasını ya da hiç uygulanmamasını sağlayan bir mekanizma olan Batchlog'dur. Bu, A'yı ASİT - atomiklik açısından kutudan çıkarmamızı sağlar.

Cassandra'daki işlemlere en yakın şey sözde "hafif işlemler". Ancak bunlar "gerçek" ASİT işlemlerinden çok uzaktır: aslında bu, bunu yapmak için bir fırsattır. CAS Ağır Paxos protokolünü kullanan fikir birliğini kullanarak yalnızca bir kayıttan elde edilen veriler üzerinde. Dolayısıyla bu tür işlemlerin hızı düşüktür.

Cassandra'da neyi kaçırdık

Bu yüzden Cassandra'da gerçek ASİT işlemlerini uygulamak zorunda kaldık. Bunu kullanarak klasik DBMS'nin diğer iki kullanışlı özelliğini kolayca uygulayabildik: yalnızca birincil anahtarla veri seçimleri yapmamıza izin vermeyen tutarlı hızlı dizinler ve düzenli monoton otomatik artan kimlik oluşturucu.

C*Bir

Böylece yeni bir DBMS doğdu C*Bir, üç tür sunucu düğümünden oluşur:

  • Depolama – (neredeyse) standart Cassandra sunucuları, verileri yerel disklerde depolamaktan sorumludur. Verilerin yükü ve hacmi arttıkça, bunların miktarı kolaylıkla onlarca ve yüzlere ölçeklendirilebilir.
  • İşlem koordinatörleri - işlemlerin yürütülmesini sağlar.
  • İstemciler, iş operasyonlarını uygulayan ve işlemleri başlatan uygulama sunucularıdır. Böyle binlerce müşteri olabilir.

NewSQL = NoSQL+ACID

Her türden sunucu ortak bir kümenin parçasıdır; birbirleriyle iletişim kurmak için dahili Cassandra mesaj protokolünü kullanır ve dedikodu küme bilgilerinin alışverişi için. Heartbeat ile sunucular karşılıklı arızaları öğrenir, tek bir veri şemasını (tabloları, bunların yapısını ve çoğaltılmasını) korur; bölümleme şeması, küme topolojisi vb.

istemciler

NewSQL = NoSQL+ACID

Standart sürücüler yerine Fat Client modu kullanılıyor. Böyle bir düğüm veri depolamaz ancak isteğin yürütülmesi için bir koordinatör görevi görebilir; yani Müşterinin kendisi, isteklerin koordinatörü olarak hareket eder: depolama kopyalarını sorgular ve çakışmaları çözer. Bu, uzak bir koordinatörle iletişim gerektiren standart sürücüden daha güvenilir ve daha hızlı olmakla kalmaz, aynı zamanda isteklerin iletimini kontrol etmenize de olanak tanır. İstemcide açık olan bir işlemin dışında istekler depolara gönderilir. Müşteri bir işlem açmışsa, işlem içindeki tüm istekler işlem koordinatörüne gönderilir.
NewSQL = NoSQL+ACID

C*Bir İşlem Koordinatörü

Koordinatör, C*One için sıfırdan uyguladığımız bir şeydir. İşlemleri, kilitleri ve işlemlerin uygulanma sırasını yönetmekten sorumludur.

Hizmet verilen her işlem için koordinatör bir zaman damgası oluşturur: sonraki her işlem, önceki işlemden daha büyüktür. Cassandra'nın çakışma çözümleme sistemi zaman damgalarına dayandığından (çakışan iki kayıttan en son zaman damgasına sahip olan güncel kabul edilir), çakışma her zaman sonraki işlem lehine çözülecektir. Böylece uyguladık Lamport izle - dağıtılmış bir sistemdeki çatışmaları çözmenin ucuz bir yolu.

kilitler

Yalıtımı sağlamak için en basit yöntemi kullanmaya karar verdik - kaydın birincil anahtarına dayalı kötümser kilitler. Başka bir deyişle, bir işlemde bir kaydın önce kilitlenmesi, ardından okunması, değiştirilmesi ve kaydedilmesi gerekir. Yalnızca başarılı bir taahhütten sonra, rakip işlemlerin kullanabilmesi için bir kaydın kilidi açılabilir.

Dağıtılmamış bir ortamda bu tür bir kilitlemenin uygulanması basittir. Dağıtılmış bir sistemde iki ana seçenek vardır: ya küme üzerinde dağıtılmış kilitleme uygulayın ya da aynı kaydı içeren işlemlere her zaman aynı koordinatör tarafından hizmet verilecek şekilde işlemleri dağıtın.

Bizim durumumuzda veriler zaten SQL'deki yerel işlem grupları arasında dağıtıldığından, yerel işlem gruplarının koordinatörlere atanmasına karar verildi: bir koordinatör tüm işlemleri 0'dan 9'a kadar jetonlarla, ikincisi - 10'dan 19'a kadar jetonlarla gerçekleştirir, ve benzeri. Sonuç olarak, koordinatör örneklerinin her biri işlem grubunun yöneticisi haline gelir.

Daha sonra koordinatörün hafızasına sıradan bir HashMap şeklinde kilitler uygulanabilir.

Koordinatör hataları

Bir koordinatör yalnızca bir grup işleme hizmet ettiğinden, işlemi gerçekleştirmeye yönelik ikinci girişimin zaman aşımına uğraması için, onun başarısız olduğu gerçeğinin hızlı bir şekilde belirlenmesi çok önemlidir. Bunu hızlı ve güvenilir kılmak için tam bağlantılı bir çekirdek kalp atışı protokolü kullandık:

Her veri merkezi en az iki koordinatör düğümüne ev sahipliği yapar. Periyodik olarak her koordinatör diğer koordinatörlere kalp atışı mesajı göndererek işleyişini ve en son kümedeki hangi koordinatörden hangi kalp atışı mesajını aldığını onlara bildirir.

NewSQL = NoSQL+ACID

Kalp atışı mesajlarının bir parçası olarak diğerlerinden benzer bilgiler alan her koordinatör, çekirdek ilkesine göre hangi küme düğümlerinin çalışıp hangilerinin çalışmadığına kendisi karar verir: X düğümü, kümedeki düğümlerin çoğunluğundan normal durum hakkında bilgi aldıysa Y düğümünden mesajların alınması, ardından Y çalışır. Ve tam tersi, çoğunluk Y düğümünden gelen mesajların eksik olduğunu bildirir bildirmez Y reddetmiştir. Eğer çekirdek X düğümüne artık kendisinden mesaj almadığını bildirirse, X düğümünün kendisinin başarısız olduğunu düşünmesi ilginçtir.

Kalp atışı mesajları yüksek frekansta, saniyede yaklaşık 20 kez, 50 ms'lik bir süre ile gönderilir. Java'da, çöp toplayıcının neden olduğu benzer duraklama uzunlukları nedeniyle uygulamanın 50 ms içinde yanıt vermesini garanti etmek zordur. Bu yanıt süresini, GC duraklamalarının süresi için bir hedef belirlememize olanak tanıyan G1 çöp toplayıcıyı kullanarak elde edebildik. Ancak bazen, çok nadiren, kolektör duraklamaları 50 ms'yi aşar ve bu da hatalı arıza tespitine yol açabilir. Bunun olmasını önlemek için, koordinatör, uzak düğümdeki bir arızayı, kendisinden gelen ilk kalp atışı mesajı kaybolduğunda, yalnızca arka arkaya birkaç tanesinin kaybolması durumunda bildirmez. 200 yılında koordinatör düğümündeki bir arızayı bu şekilde tespit etmeyi başardık. Hanım.

Ancak hangi düğümün çalışmayı durdurduğunu hızlı bir şekilde anlamak yeterli değildir. Bu konuda bir şeyler yapmamız gerekiyor.

Rezervasyon

Klasik plan, bir yöneticinin başarısız olması durumunda, aşağıdaki yöntemlerden birini kullanarak yeni bir seçim başlatmayı içerir: moda evrensel algoritmalar. Ancak bu tür algoritmaların zaman yakınsaması ve seçim sürecinin uzunluğu ile ilgili iyi bilinen sorunları vardır. Tamamen bağlantılı bir ağda koordinatör değiştirme planı kullanarak bu tür ek gecikmeleri önlemeyi başardık:

NewSQL = NoSQL+ACID

Diyelim ki 50. grupta bir işlem yapmak istiyoruz. Değiştirme şemasını yani ana koordinatörün arızalanması durumunda 50. grupta hangi düğümlerin işlemleri gerçekleştireceğini önceden belirleyelim. Amacımız, veri merkezi arızası durumunda sistem işlevselliğini sürdürmektir. İlk rezervin başka bir veri merkezinden bir düğüm, ikinci rezervin ise üçüncü bir veri merkezinden bir düğüm olacağını belirleyelim. Bu şema bir kez seçilir ve kümenin topolojisi değişene kadar, yani yeni düğümler girene kadar değişmez (bu çok nadiren olur). Eskisinin arızalanması durumunda yeni bir aktif yönetici seçme prosedürü her zaman şu şekilde olacaktır: ilk yedek, aktif yönetici haline gelecektir ve eğer çalışması durmuşsa, ikinci yedek, aktif yönetici olacaktır.

Bu şema evrensel algoritmadan daha güvenilirdir, çünkü yeni bir master'ı etkinleştirmek için eskisinin başarısızlığını belirlemek yeterlidir.

Peki müşteriler şu anda hangi ustanın çalıştığını nasıl anlayacak? Binlerce müşteriye 50 ms'de bilgi göndermek imkansızdır. Bir müşterinin henüz bu ana bilgisayarın artık çalışmadığını bilmeden bir işlemi açmak için istek göndermesi ve isteğin zaman aşımına uğraması gibi bir durum mümkündür. Bunun olmasını önlemek için, müşteriler spekülatif olarak grup yöneticisine ve onun her iki rezervine aynı anda işlem açma talebinde bulunur, ancak yalnızca şu anda aktif yönetici olan kişi bu talebe yanıt verecektir. Müşteri, işlem içindeki sonraki tüm iletişimi yalnızca etkin yönetici ile yapacaktır.

Yedekleme yöneticileri, kendilerine ait olmayan işlemlere ilişkin alınan istekleri, bir süre saklanacakları doğmamış işlemler kuyruğuna yerleştirir. Aktif yönetici ölürse, yeni yönetici kuyruğundan işlem açma isteklerini işler ve istemciye yanıt verir. Müşteri eski ana yöneticiyle zaten bir işlem açtıysa, ikinci yanıt göz ardı edilir (ve tabii ki böyle bir işlem tamamlanmayacak ve müşteri tarafından tekrarlanacaktır).

İşlem nasıl çalışır?

Diyelim ki bir müşteri, koordinatöre şu veya bu birincil anahtarla şu veya bu kuruluş için bir işlem açılması yönünde bir talep gönderdi. Koordinatör bu varlığı kilitler ve bellekteki kilit tablosuna yerleştirir. Gerekirse koordinatör bu varlığı depodan okur ve elde edilen verileri koordinatörün belleğinde bir işlem durumunda saklar.

NewSQL = NoSQL+ACID

İstemci bir işlemdeki verileri değiştirmek istediğinde, koordinatöre varlığı değiştirmesi için bir istek gönderir ve koordinatör yeni verileri bellekteki işlem durumu tablosuna yerleştirir. Bu, kaydı tamamlar; depolama alanına kayıt yapılmaz.

NewSQL = NoSQL+ACID

Bir müşteri aktif bir işlemin parçası olarak kendi değiştirilmiş verilerini talep ettiğinde koordinatör aşağıdaki gibi hareket eder:

  • kimlik zaten işlemdeyse, veriler bellekten alınır;
  • Bellekte kimlik yoksa, eksik veriler depolama düğümlerinden okunur, halihazırda bellekte bulunanlarla birleştirilir ve sonuç istemciye verilir.

Böylece, istemci kendi değişikliklerini okuyabilir, ancak diğer istemciler bu değişiklikleri görmez çünkü bunlar yalnızca koordinatörün belleğinde depolanır; bunlar henüz Cassandra düğümlerinde değildir.

NewSQL = NoSQL+ACID

İstemci taahhüt gönderdiğinde, hizmetin belleğindeki durum koordinatör tarafından günlüğe kaydedilen bir toplu iş halinde kaydedilir ve günlük bir toplu iş olarak Cassandra depolama alanına gönderilir. Mağazalar, bu paketin atomik (tamamen) uygulanmasını sağlamak için gereken her şeyi yapar ve kilitleri serbest bırakan ve müşteriye işlemin başarısını onaylayan koordinatöre bir yanıt gönderir.

NewSQL = NoSQL+ACID

Geri almak için koordinatörün yalnızca işlem durumunun kapladığı belleği boşaltması gerekir.

Yukarıdaki iyileştirmelerin bir sonucu olarak ACID ilkelerini uyguladık:

  • Atomiklik. Bu, hiçbir işlemin sisteme kısmen kaydedilmeyeceğinin, alt işlemlerinin tamamının tamamlanacağının veya hiçbirinin tamamlanmayacağının garantisidir. Cassandra'da kayıtlı toplu iş aracılığıyla bu prensibe bağlı kalıyoruz.
  • Tutarlılık. Her başarılı işlem, tanımı gereği yalnızca geçerli sonuçları kaydeder. Bir işlem açıldıktan ve işlemlerin bir kısmı gerçekleştirildikten sonra sonucun geçersiz olduğu anlaşılırsa geri alma işlemi gerçekleştirilir.
  • İzolasyon. Bir işlem yürütüldüğünde eş zamanlı işlemler, işlemin sonucunu etkilememelidir. Rakip işlemler koordinatördeki kötümser kilitler kullanılarak izole edilir. Bir işlemin dışındaki okumalar için, Okuma Taahhüt Edilen düzeyinde izolasyon ilkesi gözetilir.
  • Kararlılık. Daha düşük seviyelerdeki sorunlara (sistem kesintisi, donanım arızası) bakılmaksızın, başarıyla tamamlanan bir işlemde yapılan değişiklikler, işlemler yeniden başlatıldığında korunmalı.

İndekslere göre okuma

Basit bir tablo ele alalım:

CREATE TABLE photos (
id bigint primary key,
owner bigint,
modified timestamp,
…)

Bir kimliği (birincil anahtar), sahibi ve değiştirilme tarihi vardır. Çok basit bir talepte bulunmanız gerekiyor - "son gün için" değişiklik tarihi olan sahibin verilerini seçin.

SELECT *
WHERE owner=?
AND modified>?

Böyle bir sorgunun hızlı bir şekilde işlenebilmesi için, klasik bir SQL DBMS'de sütunlara göre (sahip, değiştirilmiş) bir dizin oluşturmanız gerekir. Artık ASİT garantimiz olduğundan bunu oldukça kolay yapabiliriz!

C*One'daki dizinler

Kayıt kimliğinin birincil anahtar olduğu fotoğrafların bulunduğu bir kaynak tablosu vardır.

NewSQL = NoSQL+ACID

Bir dizin için C*One, orijinalin kopyası olan yeni bir tablo oluşturur. Anahtar, dizin ifadesiyle aynıdır ve aynı zamanda kaynak tablodaki kaydın birincil anahtarını da içerir:

NewSQL = NoSQL+ACID

Artık "son günün sahibi" sorgusu başka bir tablodan seçim olarak yeniden yazılabilir:

SELECT * FROM i1_test
WHERE owner=?
AND modified>?

Kaynak tablo fotoğrafları ve indeks tablosu i1'deki verilerin tutarlılığı koordinatör tarafından otomatik olarak korunur. Yalnızca veri şemasına dayanarak, bir değişiklik alındığında koordinatör, yalnızca ana tabloda değil aynı zamanda kopyalarda da bir değişiklik oluşturur ve saklar. Dizin tablosu üzerinde hiçbir ek işlem yapılmaz, günlükler okunmaz ve hiçbir kilit kullanılmaz. Yani, dizin eklemek neredeyse hiç kaynak tüketmez ve değişikliklerin uygulanma hızı üzerinde neredeyse hiçbir etkisi yoktur.

ACID'yi kullanarak SQL benzeri indeksler uygulayabildik. Tutarlı, ölçeklenebilir, hızlı, birleştirilebilirler ve CQL sorgu dilinde yerleşiktirler. Dizinleri desteklemek için uygulama kodunda herhangi bir değişiklik yapılması gerekmez. Her şey SQL'deki kadar basit. Ve en önemlisi, indeksler orijinal işlem tablosunda yapılan değişikliklerin yürütme hızını etkilemez.

Ne oldu

C*One'ı üç yıl önce geliştirdik ve ticari kullanıma sunduk.

Sonunda ne elde ettik? Bunu bir sosyal ağdaki en önemli veri türlerinden biri olan fotoğraf işleme ve depolama alt sistemi örneğini kullanarak değerlendirelim. Fotoğrafların gövdelerinden değil, her türlü meta bilgiden bahsediyoruz. Artık Odnoklassniki'de bu tür yaklaşık 20 milyar kayıt var, sistem saniyede 80 bin okuma isteğini, veri değişikliğiyle ilgili saniyede 8 bine kadar ACID işlemini işliyor.

Çoğaltma faktörü = 1 olan SQL kullandığımızda (ancak RAID 10'da), fotoğraf meta bilgileri Microsoft SQL Server çalıştıran 32 makineden oluşan yüksek düzeyde kullanılabilir bir kümede (artı 11 yedek) depolandı. Yedeklemelerin depolanması için de 10 sunucu tahsis edildi. Toplam 50 pahalı araba. Aynı zamanda sistem, yedeksiz olarak nominal yükte çalışıyordu.

Yeni sisteme geçtikten sonra çoğaltma faktörü = 3 - her veri merkezinde bir kopya aldık. Sistem, toplam 63 sunucu olmak üzere 6 Cassandra depolama düğümü ve 69 koordinatör makineden oluşur. Ancak bu makineler çok daha ucuzdur, toplam maliyetleri bir SQL sisteminin maliyetinin yaklaşık% 30'u kadardır. Aynı zamanda yük %30'da tutulur.

C*One'ın piyasaya sürülmesiyle gecikme de azaldı: SQL'de yazma işlemi yaklaşık 4,5 ms sürdü. C*One'da - yaklaşık 1,6 ms. İşlem süresi ortalama 40 ms'nin altında, commit işlemi 2 ms'de tamamlanıyor, okuma ve yazma süresi ortalama 2 ms'dir. 99. yüzdelik dilim - yalnızca 3-3,1 ms, mola sayısı 100 kat azaldı - bunların hepsi spekülasyonun yaygın kullanımı nedeniyle.

Şu ana kadar SQL Server düğümlerinin çoğu hizmet dışı bırakıldı; yeni ürünler yalnızca C*One kullanılarak geliştiriliyor. C*One'ı bulutumuzda çalışacak şekilde uyarladık tek bulutBu, yeni kümelerin dağıtımını hızlandırmayı, yapılandırmayı basitleştirmeyi ve işlemi otomatikleştirmeyi mümkün kıldı. Kaynak kodu olmadan bunu yapmak çok daha zor ve zahmetli olacaktır.

Şimdi diğer depolama tesislerimizi buluta aktarmaya çalışıyoruz ancak bu tamamen farklı bir hikaye.

Kaynak: habr.com

Yorum ekle