Veri Deposu — Redis
Redis, REmote Dİctionary Server kelimelerinin baş harflerinden oluşan, açık kaynak(open-source) olarak geliştirilen, verileri bellekte tutan ve ayrıca <key, value>prensibine de sahip NoSQL veri tabanıdır. Rakiplerine göre en büyük avantajlarından biri String, List, HashSet, SortedSet gibi veri tiplerine sahip olmasıdır. Verileri bellekte tutup, sıklıkla istenilen verileri de aşağı yukarı milisaniye altında performanslı okuma/yazma işlemine tabii tutmaktadır. Rediste her ikisi de ayrı uygulamalar olmak üzere bir sunucu tarafı bir de client tarafı vardır. Client üzerinden redise istekler atılır, veriler sunucuda tutulur.
Ben veriye bakarım çok sık erişiliyor mu diye, bir de güncellenme süresine bakarım az mı diye..
Caching(Önbelleğe alma) Nedir? Çeşitleri Nelerdir?
Genel anlamda hem verinin kaydedilmesi hem de ihtiyaç halinde okunmasına caching işlemi diyoruz. Caching ile response sürelerimizi azaltabilir, daha iyi bir kullanıcı deneyimi sunup, gereksiz maliyeti azaltma konusunda faydalanabiliriz. 😇
InMemory Cache (Private Cache)
Uygulamayı barındıran web sunucusunun ram (bellek-memory) inde verinin tutulmasıdır. Bellekten veri okumak ile sabit diskten veri okumak arasında ciddi bir hız farkı ⏳ vardır. Cachelemede de ilgili verilere hızlı erişebilmek için genellikle belleğe kayıt edilir ve kaydedilen bu verilerin boyut sınırı sunucunun bellek miktarına bağlıdır.
Bir örnek vermem gerekirse, veri tabanından 100 satırlık araç listesini bir kullanıcı isteğine istinaden gidip aldınız. Veriyi belleğe kaydedip sonra da kullanıcıya görüntülediniz. Başka bir kullanıcı tekrar aynı kriterlerde ki (cache key den bahsediyorum.) 100 aracı listelemek istediğinde veri tabanına bağlantı (connection) aç-kapa gibi işlemlerle ilgilenmeyeceğimizden yani direkt olarak bellek üzerinden veriye erişeceğimizden efsane bir performans farkı yaratacağız.
Her güzelin haliyle bir kusuru var. Şimdi soldaki görselde tamamen birbirinden bağımsız, uygulamadan A ve B olmak üzere 2 tane farklı sunucularda çalışan instance (örnek) alınmış olarak düşünelim.
Tabii ki bunun önünde bir load balancer (yük dengeleyici) ⚖️ konumlandırılıyor ve sunucunun uygunluğuna göre istekleri yönlendirdiğini varsayalım.
Kötü Senaryo — Veri Tutarsızlığı
Şimdi saat sabah 06:00'da istek geldiğini varsayalım ve Load balancer gelen isteği yukarıdaki sunucuya yönlendirdi. Gelen istek sonucunda cache’de isteğe ait veri olmadığını belirledim, veri tabanından veriyi alıp önce cacheledim sonra da kullanıcıya response döndüm. Buraya kadar her şey okey.. Sonra saat 07:00 de bir istek daha geldi ve load balancer trafikten dolayı alttaki sunucuya yönlendirdi. (Bu arada gelen isteğin kriterlerinin aynı olduğunu varsayıyorum). Bu uygulamada baktı ki cache’de veri yok, gitti veri tabanından çekip, cacheledi ve kullanıcıya response döndü. Eee ben ne anladım şimdi bu işten? Yukarıdaki sunucuda saat 06:00 da ki cache, alttaki sunucuda ise 07:00 de ki cache var. Sonuç, üstteki sunucuya giden kullanıcı farklı veri, alttaki sunucuya giden kullanıcı ise farklı veri ile karşılaşacak.
Şimdi buradaki kritik sorun uygulamamdan birden fazla instance’ı farklı sunucularda ayağa kaldırdığım zaman başlıyor. Sorunu kısmi olarak çözmek için sticky sessiondan faydalanabiliriz. Sticky Session ile load balancer a sana istek geldiğinde bu hangi kullanıcıdan geliyorsa bunu bil ve isteği hangi sunucuya yönlendirdiysen o kullanıcıdan gelen istekleri daha önce yönlendirmiş olduğun sunucuya yönlendir diyebiliriz. Bu kesin çözüm değil, kısmi çözüm sağlar. Yatırım tavsiyesi değildir.
Nasıl kullanırım?
Startup.cs tarafında services.AddMemoryCache() eklemesini yapıyoruz. Daha sonra arzu ettiğin yerde IMemoryCache interface’ini kullanarak aşağıdaki metotlara erişip verileri cacheleyebilirsin.
Get(): Veri okumak için kullanılır.
Set(): Veri kaydetmek için kullanılır.
TryGetValue(): Bellekte verilen key değerine ait veri var mı? yok mu? Geriye boolean değer ve out keywordü ile veriyi döner.
GetOrCreate(): Bellekte verilen key değerine ait veri varsa döner, yoksa oluşturup sonra döner.
Remove(): Bellekteki herhangi bir key’i silmek için kullanılır.
Cache Priority: Cacheleme sırasında belirlediğimiz önem 🚨 derecesidir. Belleğimiz dolduğu zaman cache’leme yapmak istersek, bellekteki key’lerden hangisi silinecek? CacheItemPriority Enum ı 4 adet değere sahiptir. Low, Normal, High, NeverRemove. Eğer priority değerini NeverRemove seçerseniz, bellek kapasitesi dolduğunda yeni veri kaydederken exception alırsınız.
RegisterPostEvictionCallback: Bellek dolduğunda priority değerlerine göre bellekten veriler siliniyor. Bellekten bir veri silindiği zaman hangi sebepten silindiğini tespit edebileceğimiz metottur.
Distributed Cache (Shared Cache)
Verinin, uygulamayı barındıran sunucudan bağımsız bir cache servisinde tutulması işlemidir. Veri uygulamadan tamamen ayrı bir serviste tutulacağı için herhangi bir t (zaman) anında verinin farklı bir versiyonunu elde etmez, yani veri tutarlılığını sağlarsınız.
InMemoryCache’de veri tutulduğunda sunucu yeniden başlatılırsa haliyle cache’deki veriler silinir, fakat Distributed cache servisini kullanırsak cache lediğimiz veriler ayrı bir serviste tutulacağından dolayı veri kaybı yaşamayacağız.
On-Demand ve PrePopulation Caching Nedir?
On-Demand: Talep edildiği anda cachelenme olayıdır.
PrePopulation: Uygulamayı ayağa kaldırmakla beraber daha talep gelmeden belirlediğimiz verilerin cachelenmesi işlemidir.
Absolute Time ve Sliding Time Nedir?
Verileri cachelerken bellekte ne kadar süre kalacağına dair bir ömür biçebiliyoruz.
Absolute Time: Cache’e verilen ömrün ta kendisidir. Örneği 5 dakika verirseniz, 5 dakika sonunda erişmeye çalıştığınızda toz bulutuyla karşılaşırsınız.
Sliding Time: Bir cache’in bellekte ne kadar in aktif kalacağıyla ilgilidir. Örneğin 3 dakika belirlersek ve bu 3 dakika ⏱ içerisinde cachedeki veriye erişirsek, cache’in ömrünü 3 dakika daha arttırmış oluyoruz. Kısaca vermiş olduğumuz zaman dilimi içerisinde cachelenmiş veriye erişilirse bu cache’in ömrünü belirlediğimiz süre zarfınca arttırmış oluyoruz.
Sliding Time’i tek başına kullanmak oldukça tehlikelidir. Sliding time belirlerken aynı zamanda absolute time da belirleyerek sliding time da ki bayat veri tehlikesini atlatabiliriz. Örneğin sliding time=5 dk, absolute time=10dk belirlediniz. Bu sırada bir istek geldiğinde verinin ömrü 5 dk daha uzar fakat tekrar istek geldiğinde 5 dakika daha artmaz. Yani absolute time kadar cache in ömrünü uzatabilirim.
AspNetCore İle Redis Nasıl Kullanılır? (2 Farklı Yol)
Yol 1 — IDistributedCache
En hızlı konfigürasyonu sağlayıp redise üzerinde işlem yapmamızın alternatif yollarından biridir. Eğer sadece get ve set yapacaksanız yani redis tarafındaki veri tipleriyle çalışmayacaksanız IDistributedCache işimizi görecektir.
Kullanım için nuget paket yöneticisi ile Microsoft.Extensions.Caching.StackExchangeRedis paketini kurmanız gerekmektedir. InMemoryCache de olduğu gibi startup.cs dosyasında AddStackExchangeRedisCache servisini eklemeyi unutmuyoruz.
IDistributedCache’i herhangi bir controller’ın kurucusunda DI geçerek get ve set metotlarıyla birlikte çok hızlı bir şekilde verileri redisdeki belleğe cacheleyebiliriz. Cachleme yaparken herhangi bir süre belirtmezseniz ram dolana kadar bu veri durur sonra ram dolduğu zaman redis en az kullanılan veriden başlayarak belleği boşaltmaya başlar.
Yol 2 — StackExchange.Redis API
Redis üzerindeki tüm veri tipleriyle çalışmak istiyorsanız yani redis-cli aracılığıyla mevcut olan tüm komutları kullanmak istiyorsanız nuget üzerinden StackExchange.Redis paketini kurmalısınız. Bu paket yardımıyla redis server ile haberleşiyor olacağız.
IDistributedCache sadece get ve set metotlarıyla çalışmamıza izin veriyordu fakat redis-cli tarafında görmüş olduğunuz kodların çoğunu redis api ile beraber birer metot olarak kullanabiliriz.
Redis Nasıl Kurulur?
Redisin Linux tarafında resmî bir sürümü var fakat Windows işletim sistemi için resmî bir sürümü bulunmamaktadır. Windows için bağımsız geliştiriciler, o koca yürekli insanlar tarafından geliştirilmiş olan versiyonları var, bunları kullanabiliriz. Tabii ki bunlar redisin güncel versiyonlarıyla birebir ilerlemiyor.
- Redis: https://redis.io/
- Chocolatey: https://chocolatey.org/
- Chocolatey Kurulum Dokümanı: https://docs.chocolatey.org/en-us/choco/setup
- Desktop Manager: https://rdm.dev/
1- Chocolatey (Windows Paket Yöneticisi)
Windows Powershell’i yönetici olarak çalıştırıyoruz.
Bu kurulum sırasında ExecutionPolicy hatası almanız olasıdır. Kısa bir araştırma ile hatanın üstesinden gelebilirsiniz.
Kurulum sonrasında varsayılan olarak 6379 portundan redis-server ayağa kalkacaktır.
Redis cli tarafında ise ping komutuna karşılık pong cevabını alıyorsanız, ustam sen bu işi yapıyorsun tam gaz devam! Olmuş yani :)
2- Github / Microsoft Archive
Microsoft Archive adresine gidiyoruz, latets releases a tıklayarak indiriyoruz. Önce indirilen dosyada içindeki redis-server.exe yi sonra da redis-cli.exe yi çalışıyoruz. redis-cli üzerinden PING yazıp PONG cevabını alıyorsanız ustam sen bu işi yapıyorsun tam gaz devam! Olmuş yani :)
Redis Veri Tipleri
Redis String
Redis tarafında stringleri binary safe olduğundan dolayı value tarafında istediğimiz her şeyi kaydedebiliriz yani value string olmak zorunda değildir. Bir image’da pdf’de kaydedebiliriz. Veri atama işlemini SET okuma işlemini GET komutuyla yapıyoruz.
Cache olan verinin bir kısmını okuma, substring mantığı:
- GETRANGE name 0 2 ( GETRANGE {name} {0} {2} )
- Değeri 1 arttırma: INCR price
- Değeri x kadar arttırma: INCRBY price x
- Değeri 1 azaltma: DECR price
- Değeri x kadar azaltma: DECRY price x
- Var olan verilerinize ek yapma: APPEND name cihat
Redist List - LinkedList(c#)
Liste şeklinde veriyi tutabiliyoruz. Güzel yanı baştan ekle, sondan ekle ya da baştan sil, sondan sil gibi özelliklerimiz bulunmaktadır. Araya bir data ekleyemiyoruz ya başına ekliyoruz ya da sonuna ekliyoruz.
- Listeye soldan veri ekleme: LPUSH vehicles golf
- Listeye sağdan veri ekleme: RPUSH vehicles polo
- Listenin tamamını getirme: LRANGE vehicles 0 -1 ( LRANGE vehicles{startIndex} {endIndex} )
- Dizinin solundan sil: LPOP vehicles
- Dizin sağından sil: RPOP vehicles
- Dizinin belli bir index’deki elemanı getirme: LINDEX vehicles 1 ( LINDEX vehicles {indexNumber } )
Redis Set
List’den 2 farklı özelliği vardır. Dizin içerisinde tutacağımız veriler benzersiz olmalıdır yani aynı veriden 2 tane bulunamaz. List’lerde olan başa sona ekleme özelliği Set’de yok. Sette eklenen veri random olarak dizi içerisinde herhangi bir yere eklenir ve set’ler içerisinde de verilerimiz binary olarak kaydedileceğinden ötürü bir image, pdf gibi dosya türlerini kaydedebiliriz.
Veri ekleme: SADD brands seat ( SADD {key} {value} )
Listedeki tüm elemanları getirmek: SMEMBERS brands ( SMEMBERS {key} )
Listeden öğe silme: SREM brands skoda( SREM {key} {value} )
Redis Sorted Set
Sıralama üzerine bir veri tipidir. İstediğimiz yere istediğimiz veriyi ekleyebiliriz. Değerlerimiz benzersizdir yani aynı sırada iki tane kaydedemeyiz.
- Veri ekleme: ZADD colors 1 red ( ZADD {key} {queue:sıra} {value} )
- Verileri Getir: ZRANGE colors 0 -1
- Verileri scorelarıyla(sırasıyla) Getir: ZRANGE colors 0 -1 WITHSCORES
- Veri Silme: ZREM colors red ( ZREM {key} {value} )
Redis Hash
Verileri key ve value olarak tutabileceğimiz, c# tarafındaki dictionary’ye benzeyen bir veri tipidir.
- Veri Ekleme: HMSET vehicles golf 450000 ( HMSET {name} {key} {value} ) -> Dictionary<key,value>(golf, 450000)
- Veri Okuma: HGET vehicles golf ( HGET {name} {key} ) -> return value;
- Veri Silme: HDEL vehicles golf ( HDEL {name} {key} )
- Verileri Listeleme: HGETALL vehicles
Redis Desktop Manager
Belleğe kaydetmiş olduğumuz verileri görsel bir ara yüz üzerinden görüntülememizi sağlayan basit bir programdır. Ücretlidir ama 15 günlük de deneme süresi vardır.
Arayüz üzerinden görüntülediğimizde varsayılan olarak 16 adet veri tabanı vardır şeklinde düşünülebiliriz. 16 adet gelmesinin amacı ki bunu arttırabiliriz de, verileri gruplayıp derli toplu halde, kategorize olarak tutabilmemizdir. Örneğin db1(0) sadece servis katmanı verilerini, db2(0) sadece kullanıcı bilgileri tutacağım veya db(3) uygulamamla ilgili cache verilerini tutacağım şeklinde bir bölümlendirme yapabiliriz.
TTL: -1 verinin süresinin belirtilmediğini gösterir. Cachelenen veriye bir süre belirlemiş olsaydınız bu kısımdaki değişiklikleri izliyor olabilecektiniz.
Redis-cli normal şartlarda türkçe karakterleri kabul etmemektedir. Bunun önüne geçebilmek için redis cli’i ayağa kaldırırken “redis-cli --raw” komutunu kullanmalıyız.
Sektörde Neler Oluyor? 🏬
Rakam olarak güncel olmayabilir fakat stackshare verilerine göre 5451 şirket Redis kullanmakta ve bize tanıdık gelecek bazı isimler;
Twitter, GitHub, Slack, Shopify, Pinterest, Snapchat, Instagram, StackOverflow, Flickr
Au revoir 😎