sql >> Databáze >  >> NoSQL >> MongoDB

Jak programově předem rozdělit Shard Key založený na GUID pomocí MongoDB

Známe počáteční velikost dat (120 GB) a víme, že výchozí maximální velikost bloku v MongoDB je 64 MB. Pokud rozdělíme 64 MB na 120 GB, dostaneme 1920 – takže to je minimální počet kusů, na které bychom se měli pro začátek dívat. Jak se stává, 2048 je náhodou mocnina 16 děleno 2, a vzhledem k tomu, že GUID (náš shard klíč) je založen na hexadecimálním čísle, je mnohem jednodušší se s ním vypořádat než s 1920 (viz níže).

POZNÁMKA: Toto předběžné rozdělení je nutné provést před do sbírky jsou přidána jakákoli data. Pokud použijete příkaz enableSharding() na kolekci, která obsahuje data, MongoDB rozdělí data sama a vy to pak budete spouštět, dokud již bloky existují – to může vést k poměrně zvláštní distribuci bloků, takže pozor.

Pro účely této odpovědi předpokládejme, že databáze se bude jmenovat users a kolekce se nazývá userInfo . Předpokládejme také, že GUID bude zapsáno do _id pole. S těmito parametry bychom se připojili k mongos a spusťte následující příkazy:

// nejprve přepněte na uživatele DBuse users;// nyní povolte sharding pro uživatele DBsh.enableSharding("users"); // povolte sharding na příslušné kolekcish.shardCollection("users.userInfo", {"_id" :1});// nakonec vypněte balancer (viz níže možnosti pro jednotlivé kolekce)// to zabrání migraci před zahájením a zasahováním do rozdělení soutěží o meta data lockssh.stopBalancer();  

Nyní, podle výše uvedeného výpočtu, musíme rozdělit rozsah GUID na 2048 bloků. K tomu potřebujeme alespoň 3 hexadecimální číslice (16 ^ 3 =4096) a vložíme je do nejvýznamnějších číslic (tj. 3 nejvíce vlevo) pro rozsahy. Opět by to mělo být spuštěno z mongos shell

// Jednoduše použijte cyklus for pro každou číslicifor ( var x=0; x <16; x++ ){ for( var y=0; y<16; y++ ) { // pro nejvnitřnější smyčku we zvýší se o 2, aby získal 2048 celkových iterací // udělejte z ++ 4096 - to by dalo ~30 MB bloků na základě původních čísel pro ( var z=0; z<16; z+=2 ) { // nyní vytvořte GUID s nulami pro výplň - metoda toString pohodlně používá argument pro určení základní předpony var ="" + x.toString(16) + y.toString(16) + z.toString(16) + "00000000000000000000000000000"; // nakonec pomocí příkazu split vytvořte příslušný blok db.adminCommand( { split :"users.userInfo" , middle :{ _id :prefix } } ); } }} 

Až to bude hotové, zkontrolujme stav přehrávání pomocí sh.status() pomocník:

mongos> sh.status()--- Stav sdílení --- verze shardování:{ "_id" :1, "version" :3, "minCompatibleVersion" :3, "currentVersion" :4, " clusterId" :ObjectId("527056b8f6985e1bcce4c4cb")} úlomky:{ "_id" :"shard0000", "host" :"localhost:30000" } { "_id" :"shard0001", "host":30" :"001} { "_id" :"shard0002", "host" :"localhost:30002" } { "_id" :"shard0003", "host" :"localhost:30003" } databáze:{ "_id" :"admin", " partitioned" :false, "primary" :"config" } { "_id" :"users", "partitioned" :true, "primary" :"shard0001" } users.userInfo shard key:{ "_id" :1 } chunks :shard0001 2049 příliš mnoho bloků pro tisk, použijte podrobné, pokud chcete vynutit tisk 

Máme svých 2048 kousků (plus jeden navíc díky min/max kouskům), ale všechny jsou stále na původním střepu, protože balancér je vypnutý. Pojďme tedy znovu povolit balancer:

sh.startBalancer(); 

To se okamžitě začne vyrovnávat a bude to relativně rychlé, protože všechny části jsou prázdné, ale i tak to bude chvíli trvat (mnohem pomaleji, pokud to konkuruje migracím z jiných kolekcí). Po uplynutí určité doby spusťte sh.status() znovu a tady to (měli byste) mít - 2048 kousků, všechny pěkně rozdělené do 4 úlomků a připravené na počáteční načtení dat:

mongos> sh.status()--- Stav sdílení --- verze shardování:{ "_id" :1, "version" :3, "minCompatibleVersion" :3, "currentVersion" :4, " clusterId" :ObjectId("527056b8f6985e1bcce4c4cb")} úlomky:{ "_id" :"shard0000", "host" :"localhost:30000" } { "_id" :"shard0001", "host":30" :"001} { "_id" :"shard0002", "host" :"localhost:30002" } { "_id" :"shard0003", "host" :"localhost:30003" } databáze:{ "_id" :"admin", " partitioned" :false, "primary" :"config" } { "_id" :"users", "partitioned" :true, "primary" :"shard0001" } users.userInfo shard key:{ "_id" :1 } chunks :shard0000 512 shard0002 512 shard0003 512 shard0001 513 příliš mnoho bloky k tisku, použijte podrobné, pokud chcete vynutit tisk { "_id" :"test", "partitioned" :false, "primary" :"shard0002" } 

Nyní jste připraveni začít načítat data, ale chcete-li absolutně zaručit, že nedojde k žádnému rozdělení nebo migraci, dokud nebude načítání dat dokončeno, musíte udělat ještě jednu věc – vypnout balancer a autosplitting po dobu importu:

  • Chcete-li zakázat veškeré vyvažování, spusťte tento příkaz z mongos:sh.stopBalancer()
  • Chcete-li ponechat spuštěné další operace vyvažování, můžete je u konkrétní kolekce zakázat. Použití jmenného prostoru výše jako příklad:sh.disableBalancing("users.userInfo")
  • Chcete-li vypnout automatické rozdělování během načítání, budete muset restartovat každý mongos budete používat k načítání dat pomocí --noAutoSplit možnost.

Po dokončení importu vraťte kroky podle potřeby (sh.startBalancer() , sh.enableBalancing("users.userInfo") a restartujte mongos bez --noAutoSplit ), aby se vše vrátilo do výchozího nastavení.

**

Aktualizace:Optimalizace pro rychlost

**

Výše uvedený přístup je v pořádku, pokud nespěcháte. Jak věci stojí, a jak zjistíte, pokud to otestujete, vyvažovačka není příliš rychlá - dokonce ani s prázdnými kusy. Jak tedy zvyšujete počet vytvořených kusů, tím déle bude trvat, než se vyrovná. Viděl jsem, že dokončení vyvážení 2048 bloků trvá déle než 30 minut, i když se to bude lišit v závislosti na nasazení.

To může být v pořádku pro testování nebo pro relativně tichý cluster, ale mít balancer vypnutý a nevyžadující žádné další aktualizace rušit bude mnohem těžší zajistit na zaneprázdněném clusteru. Jak tedy věci urychlíme?

Odpovědí je udělat několik ručních pohybů brzy, a pak rozdělit kousky, jakmile budou na příslušných úlomcích. Všimněte si, že to je žádoucí pouze s určitými fragmenty klíčů (jako je náhodně distribuovaný UUID) nebo určitými vzory přístupu k datům, takže buďte opatrní, abyste v důsledku toho neskončili se špatnou distribucí dat.

Pomocí výše uvedeného příkladu máme 4 úlomky, takže místo toho, abychom provedli všechna rozdělení a poté vyrovnali, rozdělíme se na 4. Ručním pohybem jsme pak na každý úlomek položili jeden kus a nakonec tyto kusy rozdělili na požadovaný počet.

Rozsahy ve výše uvedeném příkladu by vypadaly takto:

$ min -> "40000000000000000000000000000000" "4000000000000000000000000000" -> "800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.” ”” ”” ”” COCE

Jsou to jen 4 příkazy k jejich vytvoření, ale když už to máme, proč znovu nepoužít smyčku výše ve zjednodušené/upravené podobě:

for ( var x=4; x <16; x+=4){ var prefix ="" + x.toString(16) + "00000000000000000000000000000"; db.adminCommand( { split :"users.userInfo" , middle :{ _id :prefix } } ); }  

Takto vypadají myšlenky nyní – máme naše 4 kusy, všechny na shard0001:

mongos> sh.status()--- Stav sdílení --- verze shardování:{ "_id" :1, "version" :4, "minCompatibleVersion" :4, "currentVersion" :5, " clusterId" :ObjectId("53467e59aea36af7b82a75c1")} úlomky:{ "_id" :"shard0000", "host" :"localhost:30000" } { "_id" :"shard0001", "host" :"001}" { "_id" :"shard0002", "host" :"localhost:30002" } { "_id" :"shard0003", "host" :"localhost:30003" } databáze:{ "_id" :"admin", " partitioned" :false, "primary" :"config" } { "_id" :"test", "partitioned" :false, "primary" :"shard0001" } { "_id" :"users", "partitioned" :true , "primary" :"shard0001" } users.userInfo shard key:{ "_id" :1 } chunks:shard0001 4 { "_id" :{ "$minKey" :1 } } -->> { "_id" :" 0 0 0000000000000000000000 "} ON:Shard0001 TimeStamp (1, 3) {" _id ":" 80000000000000000000 "} ->> {" _id ":" C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.:"C0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000} na Shard0001 (1, 5) {" {"{" {"{" {"{" {_id ". " } -->> { "_id" :{ "$maxKey" :1 } } on :shard0001 Timestamp(1, 6)  

Ponecháme $min kus tam, kde je, a přesuňte další tři. Můžete to udělat programově, ale záleží na tom, kde se kousky původně nacházejí, jak jste pojmenovali své úlomky atd., takže tento návod zatím ponechám, není příliš náročný - stačí 3 moveChunk příkazy:

mongos> sh.moveChunk("users.userInfo", {"_id" :"4000000000000000000000000000000"}, "shard0000"){ "millis" :1091, "ok" shmongo :1 ("users.userInfo", {"_id" :"8000000000000000000000000000000"}, "shard0002"){ "millis" :1078, "ok" :1 }mongos> sh.moveChunder""_focus" :"c000000000000000000000000000000000"}, "shard0003"){ "millis" :1083, "ok" :1 }  

Znovu to zkontrolujte a ujistěte se, že jsou kusy tam, kde je očekáváme:

mongos> sh.status()--- Stav sdílení --- verze shardování:{ "_id" :1, "version" :4, "minCompatibleVersion" :4, "currentVersion" :5, " clusterId" :ObjectId("53467e59aea36af7b82a75c1")} úlomky:{ "_id" :"shard0000", "host" :"localhost:30000" } { "_id" :"shard0001", "host" :"001}" { "_id" :"shard0002", "host" :"localhost:30002" } { "_id" :"shard0003", "host" :"localhost:30003" } databáze:{ "_id" :"admin", " partitioned" :false, "primary" :"config" } { "_id" :"test", "partitioned" :false, "primary" :"shard0001" } { "_id" :"users", "partitioned" :true , "primary" :"shard0001" } users.userInfo shard key:{ "_id" :1 } chunks:shard0001 1 shard0000 1 shard0002 1 shard0003 1 { "_id" :{ "$minKey" :--> 1 } { "_id" :"4000000000000000000000000000000" } on :shard0001 Timestamp(4, 1 ) { "_id" :"40000000000000000000000000000000" } -->> { "_id" :"80000000000000000000000000000000" } on :shard0000 Timestamp(2, 0) { "_id" :"80000000000000000000000000000000" } -->> { "_id" :"c000000000000000000000000000000000000000" } dne :shard0002 Časové razítko(3, 0) { "_id" :"c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" 0:00 /code> 

To odpovídá našim navrhovaným rozsahům výše, takže vše vypadá dobře. Nyní spusťte původní smyčku výše, abyste je rozdělili "na místě" na každém úlomku a měli bychom mít vyváženou distribuci, jakmile smyčka skončí. Ještě jeden sh.status() by měl potvrdit věci:

mongos> for ( var x=0; x <16; x++ ){... for( var y=0; y<16; y++ ) {... // pro nejvnitřnější smyčku budeme zvýšením o 2 získáte 2048 celkových iterací... // udělejte toto z++ za 4096 - to by dalo ~30 MB bloků na základě původních čísel... pro ( var z=0; z<16; z+=2 ) {. .. // nyní zkonstruujte GUID s nulami pro výplň - metoda toString snadno používá argument pro specifikaci základny... var prefix ="" + x.toString(16) + y.toString(16) + z.toString (16) + "0000000000000000000000000000";... // nakonec pomocí příkazu split vytvořte příslušný chunk... db.adminCommand( { split :"users.userInfo" , middle :{ _id :prefix } } ); ... }... }... } { "ok" :1 }mongos> sh.status()--- Stav sdílení --- verze shardování:{ "_id" :1, "verze" :4, "minCompatibleVersion" :4, "currentVersion" :5, "clusterId" :ObjectId("53467e59aea36af7b82a75c1")} shards:{ "_id" :"shard0000", "host" :"localh ost:30000" } { "_id" :"shard0001", "host" :"localhost:30001" } { "_id" :"shard0002", "host" :"localhost:30002" } { "_id" :"shard0003 ", "host" :"localhost:30003" } databáze:{ "_id" :"admin", "partitioned" :false, "primary" :"config" } { "_id" :"test", "partitioned" :false, "primary" :"shard0001" } { "_id" :"users", "partitioned" :true, "primary" :"shard0001" } users.userInfo shard key:{ "_id" :1 } chunks:shard0001 513 shard0000 512 shard0002 512 shard0003 512 příliš mnoho bloků pro tisk, pokud chcete vynutit tisk, použijte podrobné informace  

A tady to máte – žádné čekání na balancer, rozložení je již rovnoměrné.




  1. HDFS Disk Balancer Úvod, operace a funkce

  2. Databáze dokumentů:Redundantní data, reference atd. (konkrétně MongoDB)

  3. jak použít naplnit a agregovat ve stejném prohlášení?

  4. Získávání hodnot pomocí potrubí jedis