10. MongoDb – Sharding

1. MONGODB SHARDING

Sharding büyük data setleri read de dahil olacak şekilde özellikle write performansı yükselmek ve daha kontrol edilebilir olacak şekilde yatayda ufak parçalara bölmektir. Replication konseptinde diğer makinalarda datanın full kopyası bir bütün olarak bulunmaktadır ancak datanız sürekli büyüyorsa bir noktada tek bir makinaya sığmayabilir, yeterli RAM&CPU’ya sahip olmayabilir ya da write yükünü de yaymak isteyebilirsiniz. Çoğunlukla replikasyon konsepti tek başına yeterli de olsa bu gibi durumlarda shardinge yönelmek gerekebilir. Ancak shardingin sisteme önemli bir complexity katacağı da unutulmamalıdır.

Ne Zaman Sharding Kullanmalıyız?

Teoride shardingin ne zaman kullanılacağı basittir ancak pratikte sisteminizi iyi tanımanız gerekir.

Genel olarak sharding kullanmanın iki faydası vardır.

  1. Storage distribution
  2. Load distribution

Storage Distribution

Tek bir makinaya sığmayacak datanız var ise shardinge yönelmeniz gerekir. Sistemin storage ihtiyacını anlamak zor değildir. Mongodb tüm datasını tanımlı direct pathte sıradan filelarda tutar. db.stats() ve db.collection.stats() komutları ile mevcut database’in veya collection storage usage’ının istatistiklerini görebiliriz.

Storage distribution case’ı yukarıda da bahsettiğim gibi datanızın tek bir node’a sığmama durumu ile karşılaştığınızda ve artık scale up yapamayacağınız durumda ortaya çıkabilir.

Load Distribution

Burada load dediğimiz esasen clienttan gelen requestlere karşılık CPU,RAM ya da I/O bandwidth olabilir. 

Indexlerin ve üzerinde çalışılan datasetin RAM’e fit olması çok önemlidir. Bu nokta shardınding en yaygın sebebidir. Artık data büyüdükçe çalışılan data ram’e fitlenmiyorsa sharding’e yönelmek gerekir. Tabiki serverdaki handle edilebilecek yük ve mevcut ramin ilişkisini kurmak kolay değil.

Sonuç olarak ne zaman sharding kullanılacağının kararı 

  1. Network usage 
  2. Disk usage
  3. CPU usage
  4. En önemlisi çalışılan setin oranı veya aktif olarak kullanılan datanın ram’e oranının düzenli gözlemlenmesi ile verilebilecek bir karardır.

Sharding yapabilmek için bir kaç component beraber çalışmalı. Bu componentlerin birlikte çalışması durumundaki sisteme de sharded cluster denir. Mongodb’de shardingin nasıl çalıştığını anlayabilmek için sharded clusterdaki her componentin yaptığı işi anlamak gerekir.

Shardlar : Datayı tutan uygulamalardır. Sharded clusterda sadece mongos routerları ya da system adminleri direkt olarak shardlara bağlanabilir. 

Mongos Router : Sharding yapıda querynin doğru shardlara route olmasından sorumlu bir componente ihtiyaç vardır. Bu işi mongos router yapar. Cluster meta datasını cacheler ve bu dataları doğru shard veya shardlara yönlenmek için kullanır. Yani mongos clustera single point of contact noktası sağlar. Mongos processleri lightweight ve nonpersistentdır. Bu sebeple genelde application makinasına da deploy edilebilirler. Yani application mongosa local olarak bağlanır. Mongos da individual shardlara connectionları yönetir.

Config Serverları : Cluster hakkındaki meta dataları persist eder. Bunlara hangi shardın datanın hangi subsetine sahip olacağı da dahildir. Mongos processleri non persistent olduğu için metadataların bir yerde tutulması gerekir. Bu da config serverların işidir. Metadatalar, global cluster configlerini, her databasein location configurasyonlarını, kollektion ve shardların içerisinde bulunan data rangeinin bilgileri gibi bilgileri tutar.

Mongos’da her process start edildiğinde mongos config serverdaki metadatayı kendi tarafına kopyalar. Bu datalar olmadan clusteri bir bütün olarak ele almak mümkün değildir. Resimde görüldüğü üzere 3 adet config server vardır ancak replica set olarak deploy edilmezler. Çünkü async replicationdan daha güçlü bir şeye ihtiyaç vardır. 2PC kullanılır. Bu da config serverlar arasındaki consistencyi garanti eder. Production deploymentinda 3 server kullanmak ve aynı makinalarda deploy etmek gereklidir.

2. MONGODB DATA ORGANIZASYONU

  1. Document : MongoDB’de verinin en küçük birimi olan document, sistemdeki tek bir nesneyi temsil eder ve daha fazla bölünemez. Bu kavramı ilişkisel bir veritabanındaki bir row’a benzetebiliriz. Bir belge ve içindeki tüm alanlar, tek bir atomik birim olarak kabul edilir.
  1. Chunk : Chunk, bir alandaki değerlere göre gruplandırılmış document bir grubudur. Bu kavram sadece sharded  yapılandırmalarda bulunur. Chunk, shard anahtarı olarak bilinen bir alan veya alan kümesine göre belgelerin mantıksal bir gruplamasıdır. 
  1. Collection : Bir veritabanı içindeki documentların adlandırılmış bir gruplamasıdır. MongoDB, bir veritabanını uygulama için mantıklı gruplamalara ayırmak için koleksiyon kavramını sunar. Bu sadece belgelerin adlandırılmış bir gruplamasıdır ve uygulama tarafından herhangi bir sorgu çalıştırmak için açıkça belirtilmelidir. 
  2.  Database : Sistemdeki document gruplamalarını içerir. Bu, sistemdeki en üst düzeyde adlandırılmış bir gruplamadır. Bir veritabanı, belgelerin gruplamalarını içerdiği için, belgelerle ilgili herhangi bir işlemi gerçekleştirmek için bir koleksiyon da belirtilmelidir. 
3. SHARDED CLUSTERLARDA QUERY ve INDEX

Uygulama açısından sharded bir clusterla sharded olmayan bir mongodb clusterına query atmak arasında bir fark yoktur. Olay, arka kısımda dönmektedir.

Query Routing

Diyelim ki sharded clusterda bir query atıyorsunuz. Mongos’un doğru ve tam result sunabilmek için kaç shard ile iletişim kurması gerekir? Bu aslında query selectorün içerisinde shard keyin olup olmamasıyla ilgili bir konudur. 

Config serverlar shard keyleri shardlara mapleyen bir yapı kurarlar. Bunlar da aslında chunklardır. Eğer query shard key’i içerirse mongos meta datalara danışıp hangi shard querynin result setini içeriyor belirleyebilir. Buna Targeted Query denir. Ancak, shard key querynin bir parçası değilse, query planner query sonucunu tamamlayabilmek için tüm shardları ziyaret etmelidir. Buna da Global ya da Scatter/Gather Query denir.

Yukarıdaki diagram iki shardlı bir cluster, iki mongos router ve iki uygulama serverindan olusur. Bu cluster için için shard key {username : 1, _id:1} olsun. 

Sol tarafta usernamei içeren targeted bir query atılıyor. Bu durumda mongos correct shard’a yönelebilmek için username value’sunu kullanabilir.Sağ tarafta ise shard key içermeyen global ya da scatter/gather query atılıyor. Bu durumda mongos router queryi tüm shardlara yaymak zorunda.

Arada ciddi bir performans farkı vardır. Eğer tüm queryleriniz global ise bunun anlamı clusterdaki tüm shardlar querye cevap vermelidir. Ancak targeting sharded clusterin performansını etkileyen tek konu değildir ayrıca unsharded clusterda konuşulan tüm performans konular sharded cluster için de geçerlidir

Sharded Clusterda Index

Unsharded clusterda olduğu gibi sharded clusterda da indexing oldukça önemli bir konudur.

  • Her shard kendi indexini maintain eder. Sharded clusterda bir index tanımladığınızda, her shard kendi içerisinde bulunan parça için indexini yaratır.
  • Sharded collectionsdaki shardlar aynı indexlere sahip olmalıdır.
  • Sharded collections, uniqe indexe yalnızca _id alanında ve shard key’de izin verir. Benzersiz indexler başka yerlerde yasaktır çünkü bunların uygulanması, mongo db’deki parçalamanın temel tasarımına aykırı olan parçalar arası iletişimi gerektirir.

Explain() metodu bu noktada queryleri optimize etmek için çok uygundur ve querynin targeted olup olmadığını ya da index kullanıp kullanmadığını gösterir.

SHARDED CLUSTERLARDA AGGREGATION

 Ayrıca aggregation sorgular da shardingten yararlanabilir. Aggregation sorgular ve analizleri single queryden daha komplikedir. Aggregation sorgusunun kaç sharda dokunması gerektiği sorgunun kendisine ve datalara bağlıdır. Eğer tüm dataları sayan bir queryniz varsa mongosun tüm shardlara dokunması gerekir. Ancak daha küçük bir data sayımı yapıyorsanız daha az sharda dokunabilir.

4. IDEAL SHARD KEY SECIMI

Yanlış bir shard key seçimi uygulamanın shardingten alabileceği bir çok faydayı yok edebilir ve shard keyler immutable dir.

Hotspot : Yanlış shard key seçimi tüm write ve readlerin tek bir sharddaki tek bir chunka gitmesine sebep olacaktır. Bu da çoğu shard boşta kalırken tek bir shardın aşırı yüklenmesine sebep olacaktır. Bu olaya hotspot ya da celebrity problem denir.

Unsplittable Chunks : Çok büyük datayı kapsayan shard keyler çok fazla dökümanın aynı shard key altında toplanmasına sebep olabilir. Bu da dokümanların farklı chunklara bölünmesini engeller ki bu da mongodbnin datayı dengeli dağıtmasını  limitler.

Poor Targeting : Writelar clusterda düzgün dağılsa da eğer ki shard key querylerle ilişkiye sahip değilse yani shard keyi içermeyen bir query atıyorsanız düşük query performansı azalır. (Targeting , global query).

Imbalanced Writes (hotspot)

Shard key tanımlarken ilk akla gelen mongodbnin kendi id’si _id’yi seçmek olabilir. Her dokümanda var ve unique. Querylerde de kullanılabilir. Ancak bu seçimde bir problem var. _id’nin değeri sürekli artan bir değer. Bunun anlamı her döküman bir önceki dökümana göre daha büyük bir shard key’e sahip olur. Peki bu neden problem teşkil eder? Eğer ki sistem tamamen dengelenmişse her döküman aynı chunka gider.

1000 adet döküman insert ettiğimizi düşünelim ve 1001 1002 1003 ü insert etmek üzereyiz. Mongodb 1000’eden fazlasını görmediği için 1000’den max’e keya split etmez. Bunun anlamı 3 döküman ve diğer tüm dökümanlar aynı chunkta yer alır.

Bu örneği bir spreed sheet uygulaması üzerinden düşünmeye devam edelim. Yaratılan her yeni speadsheet aynı sharda yazılacaktır. 100 shardınız olsa dahi,tüm writelar tek bir sharda gidecek diğerleri de idle kalacak. Bu da shardingin en önemli özelliği olan otomatik insert load distribution’ı geçersiz kılar. Bunun dışında _id shard key olarak kullanılmak istenirse, _id’yi kendi yarattığınız değer ile ezebilirsiniz ya da _id üzerinde kendi hashed shard key’inizi yapabilirisiniz.

Unsplittable Chunks

Peki username gibi bir alanı shard key olarak seçersek ne gibi bir durumla karşılaşırız. Genel olarak username sorgularımızda kullanabileceğimiz bir alandır ve görece olarak dengelenmiş insert sağlar çünkü user insert dağılımı göreceği olarak eşittir. Ancak burada chunkların sınırsızca büyümesi gibi bir sıkıntı doğar. Örneğin, Veli ismindeki bir kullanıcı 10 gblık bir spreadsheet datası yüklediğini düşünelim. Bu 64 mblik max boyuttan da fazladır. Normalde, chunk çok fazla büyüdüğünde mongodb chunkları daha küçük parkalara böler ve cluster bazında dengeleme yapar. Ancak bu durumda chunkı split edecek yer yoktur çünkü tek bir single shard key içerir. Bu da problemlere yol açar ve cluster imbalanced olur.

Şu ana kadar shard key seçimiyle ilgili 3 noktaya değindik. Biri readlerin nasıl targeted olacağı üzerineydi. İkincisi writeların nasıl doğru bi şekilde distribute olacağı diğer ise mongodbnin collectiondaki chunkları nasıl efektif bir şekilde ayrıştırıp migrate edeceği üzerineydi. Bu 3ünü iyi bir şekilde sağlayan bir shard keyi nasıl seçeriz? En makul yaklaşım compound shard key kullanmak olacaktır. Hem username hem de _id’yi shard key olarak kullanan bir sistem düşünün. İyi bir targeted’a sahiptir çünkü username çoğunlukla sorgularınızda yer alacaktır. İyi bir write balance’ına sahiptir çünkü username alfabeyi baz aldığı için eşit dağılıma sahiptir ve son olarak da chunkları dağıtmak için yeterli derecede fina-grainedtir çünkü unique olan _id değerini içerir.