13. Redis Scaling

Scaling Redis

Scaling tek bir makinenin gücünün sınırlarını gördüğümüzde veri ve performans ihtiyacımız artmaya devam ediyorsak karşımıza çıkan en net çözümdür. Scaling out yani yatayda ölçekleme, processleri birden fazla makinaya dağıtıp süreçleri paralelde işletmek üzerine kuruludur. Dolayısıyla bu da dağıtık bir sistem inşa etmek etmek demektir. Rediste scalingi read ve write kapasitesini artırmak için ayrı çözümler olarak kulanabiliriz. Read kapasitesini artırabilmek için read-only slave serverlar ekleyerek yani replicasyon özelliğinden faydalanarak, Write kapasitesinin sınırlarını artırmak için de sharding yönetimi kullanabiliriz.

1.Redis Read Scale Etmek

1.a) Redis Replikasyon

Sistemlerimiz çok fazla yük altında çalışmasıyla replikasyon özelliği hayatımıza girdi. Replikasyon dediğimiz yöntem aslında ana master serverimizde tuttuğumuz verimizin diğer serverlarda kopyasının tutulmasıdır. Redis de ölçeklenmeye yardımcı olmak açısından replikasyonu desteklemektedir. Redis her ne kadar çok hızlı olsa da zaman zaman darboğaza düştüğü durumlar olabilir ve bu gibi durumlarda da replikasyon özelliği yardımımıza koşabilir.

1.b) Master-Slave Replikasyonu

Redis master-Slave replikasyonunu destekler. Yani bir master serveriniz ana redis serverınızdır ve diğer slaveler bu mastera subscribe olup masterda veri güncellendikçe kendilerini update eder ve orjinal veriye sahip olur.

Slave serverlar iki ana amaç için kullanılabilir. İlki redis master serverınızın yavaş çalıştığı durumlarda yükü bölmek ve sistemi hızlandırmak. Redis master, serverınızda çok fazla operasyon olduğunda read operasyonlarını slavelere dağıtıp yükü hafifleterek serverı hızlandırabilirsiniz. Tabi slavelere güncel veriyi dağıtacak olan node master node olduğundan writelar yine master node üzerinden ilerlemeye devam eder.

İkinci bir kullanım amacı ise handling failure. Bunu ayrı bir başlıkta ele alacak olsam da kısaca bahsetmek gerekirse eğer master nodeunuz herhangi bir sebepten çökerse,  slave nodedaki veriyi kullanarak yeni bir master node’a taşıyabilir ya da direkt slave node’u master olarak kullanabilirsiniz.

Bazen bir mastera çok sayıda slave bağlandığında da performans kayıpları olabilir. Aslında redis serverlarının temelde master veya slave olması arasında fark yok. Dolayısıyla bu durumda biz master node’a slaveleri bağlarken slavelere de diğer slaveleri bağlayabiliriz. Yani master’ın slaveyi, slave’in de başka bir slave’i olabilir. Bu durumda bütün veriyi slavelere master üzerinden değil, bir kısmına master üzerinden diğer kısmına diğer slaveler üzerinden dağıtabiliriz. Ayrıca redis append-only özelliği ile replikasyonu bir arada kullanırsak veri kaybını sıfıra indirgeyebiliriz.

1.c) Redis Read Scale

Redis serverlarımızı scale etmeden önce, performans attırmak için gözden geçirebileceğimiz tüm seçenekleri gözden geçirmemiz eğer ki elimizdeki seçenekler tükendiyse sclale etme sürecine girmemiz yerinde olacaktır.

  • Eğerki küçük yapılar kullanıyorsak, ziplist sizeımızın çok büyük olmadığından emin olmamız gerekir. (Ziplist ile Memory Optimizasyonu)
  • Kullandığıımız veri yapılarının performans etkilerini gözden geçirmemiz gerekir.
  • Eğer ki redise cachelemek için büyük objeler gönderiyorsak bu objeleri compress etmek ve netword bandwidth’ini read ve writelar için azalmayı düşünmeliyiz.
  • Redis pipelining ve connection poolingi özelliklerini de kullanmayı unutmamalıyız.

Eğer performansa dair herşeyi yaptığımızı düşünüyorsak geriye bir tek scale etmek kalıyor  . Read performansını arttırmanın en temel ve basit yolu redise read-only slave serverlar eklemektir. Bu read-only slave serverlar master servera bağlı olup masterın replicalarını alıp real -time a yakın bir sürede güncel olurlar. Yani siz master server’a bir data yazarsınız ve master da bunu slaveleriyle paylaşır. Burada önemli nokta master çökerse ne olacağıdır.

Bir slave mastera bağlandığında masterın snapshotu yani bağlandığı anda masterdaki verinin kopyası alınır ve slave’e gönderilir. Eğer birden fazla slave mastera bu sırada bağlanırsa tüm slavere ayrı snapshot çekilmesi yerine hepsine aynı snapshot gider ve bu performans açısından oldukça fayda sağlar. Ancak çok fazla slave mastera bağlandığı takdirde, master slave arasında trafik artışı ve performans problemi çıkabilir. Bunu azaltmanın yolu da master’a slave bağlarken slave’e de başka slaveler bağlamaktır. Yani master-slave-slave şekilde bir ağaç yapısı kurmaktır.

Redisin replicasyon ve failover durumları için kullanılabilecek için yeni bir toolu olan redis sentinel de göz önünde bulundurulabilir. Aslında Redis sentinel bir redis server modudur ve bu nodeda redis normal bir redis server olarak hareket etmez. Bunun yerine master ve slavelerin davranışını ve statusunu takip eden bir yapı olarak hareket eder. Masterin fail olması durumunda redis sentinel var olan slaveler arasından bir master seçebiliri. Bu slave master seçildikten sonra sentinel tüm diğer slaveleri yeni master üzerinden bağlayacaktır.

2.Write ve Memory Kapasitesini Scale Emtek

Replication yöneti ile redisin read’ini ölçekleyebileceğimizden bahsettik. Write’ların ise hala master node üzerinden yürümeye devam edeceğinden  de bahsettik. Peki write yükü artığında ve write’ı güncellememiz gerektiğinde ne yapmalıyız? Burada artık sharding devreye girer. Normal şartlarda shardingle memory kapasitemizi artırmayı düşünürüz ancak bu yöntem eğer ki makinemizin gücü son noktaya ulaştuysa write performansımızı artırmak için, geçerli bir yöntemdir. Yani redis üzerinde tek makina sınırlarına ulaştıysa artık bu memoryi ve dolayısıyla write’ı scale edebilmek için sharding metodunu kullanabiliriz.

Redis’de veri parçalama (partitioning), tüm verileri birden çok Redis örneğine bölme tekniğidir, böylece her örnek yalnızca anahtarların bir alt kümesini içerecektir

Eğer ki memory’i optimize etmek ve performansı maximize etmek için her şeyi denediysek ve elimizdeki makinenin sınırlarını gördüysek, artık verimizi farklı instancelara sharding etmenin vakti gelmiş demektir.

2.a) Ne Zaman Sharding Kullanmalıyız?

Çok büyük dataları yönetiyorsak ve tek bir makinenin memorysiyle yetinmek yerine çok fazla bilgisayarın gücüne ihtiyaç duyuyorsak, hesaplama gücünü birden çok CPU, birden çok bilgisayar arasında ölçeklendirip ve ağ bant genişliğini kullanmak istiyorsak shardinge başvurabiliriz. Tabi verileri yatayda parçalara ayırmak yani sharding yapmak ve bunları ayrı makinalara koymak yani dağıtık bir sistem oluşturmak kulağa kazandıran bir yöntem gibi gelse de bunun bazı trade-offları var ve sistemleriniz dağıtık hale geldiğinde sorunlarınız da dağıtık hale geliyor. Rediste shardinge başvurmadan önce shardingin getireceği bazı sıkıntılardan bahsedebiliriz.

  •  Sharding sonucu farkı instancelarda bulunan multiple keylerle operasyon yapamazsınız.
  • Farklı redis instancelarında bulunan keyler ile transaction işlemi yapamazsınız.
  • Key bazlı partitioning yapamazsınız. Mesela çok büyük bir list veya sorted list objesini farklı instance’a koymak gibi.
  • Backup ve persistence yönetimi eskiye göre çok daha komplex olur. Çünkü çok daha fazla RDB ve AO fileları ile uğraşmanız gerekir  backup involves aggregation (merging) of the RDB files from many instances.
  • Runtimeda clusterınıza instance eklemek veya silmek data misbalancingleri oluşturur ki bunun çözümü de preshardingtir.
2.b) Sharding (Partitioining) Schemes

Rediste elimizdeki veri yapısına göre sharding yapmak için bazı patternler mevcut. Bunların birkaçından bahsedebiliriz.

– Range Partitioning

Aslında temel mantık basit nesnelerinizin belli rangelerini farklı redis instancelarınıza maplersiniz. Örneğin, bazı kullanıcı verilerini tuttuğumuzu ve her kullanıcının kendi unique identifier’a (ID) sahip olduğunu varsayalım. Range partitioningte, Id’si 0 ile 10000 arasında olan user bilgileri Instance 1’de , kimliği 10001 ila 20000 arasında olan kullanıcılarını 2. Redis Intancesinde duracak şekilde tanımlayabiliriz. Bu range partitioningtir. Avantajları olduğu gibi dezavantajları da vardır. 

– Hash Partitioning

Bu partitioning yöntemi hash fonksiyonu içeren tüm keyler için yapılabilir. Bu hash fonkisonu bir key name’i bir sayıya mapler.

Hash_funct adında böyle bir fonksiyonumuz olduğunu varsayalım,  şema şu şekilde çalışır.

  1. Bir key ismi al ve onu hash_func ile bir sayıya maple.
  2. Sonuç sayıyı da bir redis instancesine maple bunu da mod operasyonunu gibi bir  operasyon kullanarak yap.

Bunun dışında Hash partitioningin advanced versiyonu olan Consistent Hashing adında da bir yöntem bulunmaktadır.

2.c) Presharding Yöntemi

Rediste sharding yapısını kullanmaya başladığınızda runtimeda instance eklemek ve silmek oldukça zordur. Ancak bunun üstesinden gelmek için kullanabileceğiniz teknikler de var. Bunlardan biri preshardingtir.

Preshardingin mantığı oldukça basittir. Bir makinada başlangıçta birden fazla redis instanceı oluşturursunuz ki bu 32 ya da 64e kadar çıkabilir çünkü redis oldukça lightweight bir üründür. Bu sayede data storageın büyümesi gerektiğinde ve redisin bunu handle etmesi gerektiğinde redis instancelarını bir makineden başka makineye taşımak mümkündür. Eğer ki bir adet redis serverınız varsa ve bir adet daha eklemeniz gerekiyorsa redis instancelarınızın yarısını diğer servera taşırsınız. Bu işlemi her redis instanceı bir servera karşılık gelene kadar yapabilirsiniz.