In plaats van een heleboel andere documentatie die er in het wild is opnieuw te hashen, zal ik er een paar opsommen voor wat achtergrondinformatie over Redis + ServiceStack's Redis Client:
- Waar moet u aan denken bij het ontwerpen van een NoSQL Redis-toepassing
- Een NoSQL-database ontwerpen met Redis
- Algemeen overzicht van Redis en .NET
- Schemaloos versiebeheer en gegevensmigraties met C# Redis Client
Er is geen magie - Redis is een leeg canvas
Eerst wil ik erop wijzen dat het gebruik van Redis als een gegevensopslag slechts een leeg canvas biedt en op zichzelf geen concept van gerelateerde entiteiten heeft. d.w.z. het biedt alleen toegang tot gedistribueerde comp-sci-gegevensstructuren. Hoe relaties worden opgeslagen, is uiteindelijk aan de clientdriver (d.w.z. ServiceStack C# Redis Client) of de app-ontwikkelaar, door gebruik te maken van de primitieve gegevensstructuurbewerkingen van Redis. Aangezien alle belangrijke gegevensstructuren in Redis zijn geïmplementeerd, heeft u in principe volledige vrijheid over hoe u uw gegevens wilt structureren en opslaan.
Bedenk hoe je relaties in code zou structureren
Dus de beste manier om na te denken over het opslaan van dingen in Redis, is om volledig te negeren hoe gegevens worden opgeslagen in een RDBMS-tabel en na te denken over hoe het in uw code wordt opgeslagen, d.w.z. met behulp van de ingebouwde C#-verzamelingsklassen in het geheugen - die Redis in gedrag spiegelt met hun server-side data-structuren.
Ondanks dat er geen concept is van gerelateerde entiteiten, is de ingebouwde Set . van Redis en SortedSet datastructuren bieden de ideale manier om indexen op te slaan. bijv. Redis's Set collection slaat maximaal 1 exemplaar van een element op. Dit betekent dat je er veilig items/sleutels/id's aan kunt toevoegen en dat het je niet uitmaakt of het item al bestaat, aangezien het eindresultaat hetzelfde zal zijn als je het 1 of 100 keer hebt genoemd - d.w.z. het is idempotent, en uiteindelijk blijft er maar 1 element opgeslagen in de set. Dus een veelvoorkomende use-case is om bij het opslaan van een objectgrafiek (aggregaat root) de onderliggende entiteits-ID's (ook wel Foreign Keys genoemd) in een set op te slaan telkens wanneer u het model opslaat.
Uw gegevens visualiseren
Voor een goede visualisatie van hoe entiteiten worden opgeslagen in Redis, raad ik aan om de Redis Admin UI te installeren die goed werkt met ServiceStack's C# Redis Client, omdat deze de onderstaande sleutelnaamgeving gebruikt om een mooie hiërarchische weergave te bieden, waarbij uw getypte entiteiten worden gegroepeerd (ondanks alle sleutels bestaande in dezelfde globale keyspace).
Om een entiteit te bekijken en te bewerken, klikt u op de Bewerken link om de interne JSON-representatie van de geselecteerde entiteit te zien en te wijzigen. Hopelijk kunt u betere beslissingen nemen over het ontwerpen van uw modellen als u eenmaal kunt zien hoe ze zijn opgeslagen.
Hoe POCO/entiteiten worden opgeslagen
De C# Redis-client werkt met alle POCO's die een enkele primaire sleutel hebben - die standaard wordt verwacht Id
(hoewel deze conventie overschrijfbaar is met ModelConfig). In wezen worden POCO's opgeslagen in Redis als geserialiseerde JSON met zowel de typeof(Poco).Name
en de Id
gebruikt om een unieke sleutel voor die instantie te vormen. Bijv.:
urn:Poco:{Id} => '{"Id":1,"Foo":"Bar"}'
POCO's in de C#-client worden conventioneel geserialiseerd met behulp van de snelle Json Serializer van ServiceStack, waarbij alleen eigenschappen met openbare getters worden geserialiseerd (en openbare setters om de-serialized terug te krijgen).
Standaardwaarden kunnen worden overschreven met [DataMember]
attrs, maar niet aanbevolen omdat het je POCO's lelijk maakt.
Entiteiten worden geblobbed
Dus wetende dat POCO's in Redis gewoon blobbed zijn, wilt u alleen niet-geaggregeerde rootgegevens op uw POCO's als openbare eigendommen bewaren (tenzij u met opzet overtollige gegevens wilt opslaan). Een goede conventie is om methoden te gebruiken om de gerelateerde gegevens op te halen (aangezien deze niet geserialiseerd worden), maar ook om uw app te vertellen welke methoden externe aanroepen doen om gegevens te lezen.
Dus de vraag of de Feed moet worden opgeslagen bij de Gebruiker is of het niet-geaggregeerde root-gegevens zijn, d.w.z. of je al dan niet toegang wilt krijgen tot de gebruikersfeeds buiten de context van de gebruiker? Zo nee, verlaat dan de List<Feed> Feeds
eigenschap op de User
typ.
Aangepaste indexen bijhouden
Als u echter alle feeds onafhankelijk toegankelijk wilt houden, d.w.z. met redisFeeds.GetById(1)
dan wil je het buiten de gebruiker opslaan en een index onderhouden die de 2 entiteiten verbindt.
Zoals je hebt gemerkt, zijn er veel manieren om relaties tussen entiteiten op te slaan en hoe je dat doet, is grotendeels een kwestie van voorkeur. Voor de onderliggende entiteit in een ouder>onderliggend relatie zou je altijd de ParentId . willen opslaan met de onderliggende entiteit. Voor de ouder kun je ervoor kiezen om een verzameling ChildIds op te slaan met het model en voer vervolgens een enkele ophaalactie uit voor alle onderliggende entiteiten om het model opnieuw te hydrateren.
Een andere manier is om de index buiten de bovenliggende dto te houden in zijn eigen Set voor elke ouderinstantie. Enkele goede voorbeelden hiervan zijn in de C#-broncode van de Redis StackOverflow-demo waar de relatie van Users > Questions
en Users > Answers
wordt opgeslagen in:
idx:user>q:{UserId} => [{QuestionId1},{QuestionId2},etc]
idx:user>a:{UserId} => [{AnswerId1},{AnswerId2},etc]
Hoewel de C# RedisClient ondersteuning biedt voor een standaard Parent/Child-conventie via de TParent.StoreRelatedEntities(), TParent.GetRelatedEntities<TChild>()
en TParent.DeleteRelatedEntities()
API's waarbij achter de schermen een index wordt bijgehouden die eruitziet als:
ref:Question/Answer:{QuestionId} => [{answerIds},..]
In feite zijn dit slechts enkele van uw mogelijke opties, waarbij er veel verschillende manieren zijn om hetzelfde doel te bereiken en waarin u ook de vrijheid heeft om uw eigen doel te bereiken.
NoSQL's schemaloze, losse typvrijheden moeten worden omarmd en u hoeft zich geen zorgen te maken over het volgen van een rigide, vooraf gedefinieerde structuur die u misschien kent bij het gebruik van een RDBMS.
Kortom, er is geen echte juiste manier om gegevens op te slaan in Redis, b.v. De C# Redis-client maakt een aantal aannames om een API op hoog niveau rond POCO's te bieden en het kloddert de POCO's in de binair-veilige tekenreekswaarden van Redis - hoewel er andere klanten zijn die de eigenschappen van entiteiten liever in Redis Hashes (woordenboeken) opslaan . Beide zullen werken.