Souhlasím s obecnou představou ostatních odpovědí, že se jedná o hraniční vztahový problém.
Klíčem k datovým modelům MongoDB je náročnost zápisu, ale to může být pro tento případ použití složité, většinou kvůli vedení účetnictví, které by bylo vyžadováno, pokud byste chtěli uživatele propojit přímo s položkami (změna skupiny, po které následuje spousta uživatelů by si vyžádalo obrovské množství zápisů a na to potřebujete nějakého pracovníka).
Pojďme prozkoumat, zda je zde model pro náročné čtení nepoužitelný, nebo zda provádíme předčasnou optimalizaci.
The Read Heavy Approach
Vaším hlavním zájmem je následující případ použití:
skutečný problém s výkonem může nastat, když chci získat všechny skupiny, které uživatel sleduje pro konkrétní položku [...], protože pak musím najít všechny skupiny, které uživatel sleduje, az toho najít všechny item_groups s group_id
$in
a ID položky.
Pojďme to rozpitvat:
-
Získejte všechny skupiny, které uživatel sleduje
To je jednoduchý dotaz:
db.followers.find({userId : userId})
. Budeme potřebovat index nauserId
což způsobí, že běh této operace bude O(log n), neboli bleskově rychlý i pro velké n. -
z toho najděte všechny item_groups s group_id
$in
a ID položkyTohle je ta složitější část. Předpokládejme na chvíli, že je nepravděpodobné, aby položky byly součástí velkého počtu skupin. Potom složený index
{ itemId, groupId }
by fungovalo nejlépe, protože množinu kandidátů můžeme dramaticky zredukovat pomocí prvního kritéria – pokud je položka sdílena pouze v 800 skupinách a uživatel sleduje 220 skupin, mongodb potřebuje pouze najít průsečík těchto skupin, což je poměrně snadné, protože obojí sady jsou malé.
Budeme však muset jít hlouběji:
Struktura vašich dat je pravděpodobně v složité síti . Složité sítě přicházejí v mnoha variantách, ale dává smysl předpokládat, že váš sledující graf je téměř bez měřítka, což je také v podstatě ten nejhorší případ. V síti bez škálování přitahuje velmi malý počet uzlů (celebrity, super bowl, Wikipedia) spoustu „pozornosti“ (tj. mají mnoho spojení), zatímco mnohem větší počet uzlů má problém získat stejné množství pozornosti. kombinované .
Malé uzly nejsou důvodem k obavám , výše uvedené dotazy, včetně zpátečních cest do databáze, jsou v rozsahu 2 ms na mém vývojovém stroji na datové sadě s desítkami milionů připojení a> 5 GB dat. Nyní, když soubor dat není obrovský, ale bez ohledu na to, jakou technologii si vyberete, bude vázána na RAM, protože indexy musí být v každém případě v RAM (lokality dat a oddělitelnost v sítích jsou obecně špatné) a nastavená velikost průsečíku je malý z definice. Jinými slovy:tomuto režimu dominují hardwarová úzká hrdla.
A co superuzly však?
Vzhledem k tomu, že by to byly dohady a hodně mě zajímají síťové modely, dovolil jsem si implementovat dramaticky zjednodušený síťový nástroj založený na vašem datovém modelu, abych provedl nějaká měření. (Omlouvám se, že je to v C#, ale generování dobře strukturovaných sítí je dost těžké v jazyce, který ovládám nejplynuleji...).
Při dotazu na supernody dostávám výsledky v rozsahu 7 ms vrcholů (to je na 12 milionů záznamů v 1,3 GB db, přičemž největší skupina má 133 000 položek a uživatel, který sleduje 143 skupin.)
Předpoklad v tomto kódu je, že počet skupin, které uživatel následuje, není velký, ale zdá se to rozumné. Pokud tomu tak není, zvolil bych přístup náročný na zápis.
Neváhejte a hrajte si s kódem. Bohužel to bude potřebovat trochu optimalizace, pokud to chcete zkusit s více než pár GB dat, protože to prostě není optimalizováno a sem tam dělá nějaké velmi neefektivní výpočty (zejména beta-vážené náhodné míchání by se dalo zlepšit ).
Jinými slovy:o výkon přístupu náročného na čtení bych se zatím nebál . Problém často není ani tak v tom, že počet uživatelů roste, ale v tom, že uživatelé používají systém neočekávaným způsobem.
The Write Heavy Approach
Alternativním přístupem je pravděpodobně obrátit pořadí propojování:
UserItemLinker
{
userId,
itemId,
groupIds[] // for faster retrieval of the linker. It's unlikely that this grows large
}
Toto je pravděpodobně nejškálovatelnější datový model, ale nešel bych do toho, pokud se nebavíme o VELKÉM množství dat, kde je sharding klíčovým požadavkem. Klíčový rozdíl je v tom, že nyní můžeme efektivně rozdělit data pomocí userId jako součásti shard klíče. To pomáhá paralelizovat dotazy, efektivně shardovat a zlepšovat datovou lokalitu ve scénářích s více datovými centry.
Dalo by se to otestovat pomocí propracovanější verze testovacího prostředí, ale zatím jsem si nenašel čas a upřímně řečeno, myslím, že je to pro většinu aplikací přehnané.