sql >> Databasteknik >  >> NoSQL >> Redis

ServiceStack.Net Redis:Lagring av relaterade objekt vs. relaterade objekt-ID

Istället för att omhasha en massa annan dokumentation som finns ute i naturen, kommer jag att lista ett par runt för lite bakgrundsinformation om Redis + ServiceStacks Redis-klient:

  • Vad man ska tänka på när man designar en NoSQL Redis-applikation
  • Designa en NoSQL-databas med Redis
  • Allmän översikt över Redis och .NET
  • Schemalös versionshantering och datamigreringar med C# Redis Client

Det finns ingen magi – Redis är en tom duk

Först vill jag påpeka att att använda Redis som ett datalager bara ger en tom duk och inte har något koncept av relaterade enheter i sig. det vill säga det ger bara tillgång till distribuerade comp-sci datastrukturer. Hur relationer lagras är i slutändan upp till klientdrivrutinen (dvs ServiceStack C# Redis Client) eller apputvecklaren, genom att använda Redis primitiva datastrukturoperationer. Eftersom alla större datastrukturer är implementerade i Redis, har du i princip full frihet om hur du vill strukturera och lagra din data.

Tänk hur du skulle strukturera relationer i kod

Så det bästa sättet att tänka på hur man lagrar saker i Redis är att helt bortse från hur data lagras i en RDBMS-tabell och tänka på hur den lagras i din kod, d.v.s. att använda de inbyggda C#-samlingsklasserna i minnet - som Redis speglar beteendet med deras datastrukturer på serversidan.

Trots att Redis inte har ett koncept med relaterade enheter, har Redis inbyggda uppsättning och SortedSet datastrukturer ger det perfekta sättet att lagra index. T.ex. Redis's Set samling lagrar endast max 1 förekomst av ett element. Detta betyder att du säkert kan lägga till objekt/nycklar/id till det och inte bryr dig om objektet redan finns eftersom slutresultatet kommer att bli detsamma om du hade ringt det 1 eller 100 gånger - det vill säga det är idempotent, och i slutändan finns bara ett element kvar i uppsättningen. Så ett vanligt användningsfall är när man lagrar en objektgraf (aggregerad rot) är att lagra underordnade enhets-ID:n (aka främmande nycklar) i en uppsättning varje gång du sparar modellen.

Visualisera din data

För en bra visualisering av hur Entities lagras i Redis rekommenderar jag att du installerar Redis Admin UI som fungerar bra med ServiceStacks C# Redis Client eftersom den använder nyckelnamnkonventionen nedan för att ge en snygg hierarkisk vy, och grupperar dina inskrivna enheter tillsammans (trots alla nycklar) finns i samma globala tangentutrymme).

För att visa och redigera en enhet, klicka på Redigera länk för att se och ändra den valda enhetens interna JSON-representation. Förhoppningsvis kommer du att kunna fatta bättre beslut om hur du ska designa dina modeller när du kan se hur de lagras.

Hur lagras POCO/entiteter

C# Redis-klienten fungerar med alla POCO:er som har en enda primärnyckel - som som standard förväntas vara Id (även om denna konvention kan åsidosättas med ModelConfig). I huvudsak lagras POCOs i Redis som serialiserad JSON med både typeof(Poco).Name och Id används för att bilda en unik nyckel för den instansen. T.ex.:

urn:Poco:{Id} => '{"Id":1,"Foo":"Bar"}'

POCO:er i C#-klienten serialiseras konventionellt med ServiceStacks snabba Json Serializer där endast egenskaper med publika getters serialiseras (och offentliga sättare för att få avserialiseras tillbaka).

Standardinställningar kan åsidosättas med [DataMember] attrs men rekommenderas inte eftersom det fular dina POCOs.

Entiteter är blobbade

Så när du vet att POCOs i Redis bara är blobbade, vill du bara behålla icke-aggregerad rotdata på dina POCOs som offentliga egenskaper (såvida du inte medvetet vill lagra redundant data). En bra konvention är att använda metoder för att hämta relaterade data (eftersom den inte kommer att serialiseras) men talar också om för din app vilka metoder som gör fjärranrop för att läsa data.

Så frågan om huruvida Flödet bör lagras med Användare är om det är icke-aggregerad rotdata eller inte, dvs om du vill komma åt användarnas flöden utanför användarens sammanhang eller inte? Om nej, lämna sedan List<Feed> Feeds egenskap på User typ.

Underhålla anpassade index

Om du däremot vill behålla alla flöden tillgängliga oberoende av varandra, d.v.s. med redisFeeds.GetById(1) då vill du lagra det utanför användaren och underhålla ett index som länkar de två enheterna.

Som du har märkt finns det många sätt att lagra relationer mellan enheter och hur du gör det är till stor del en fråga om preferenser. För den underordnade enheten i ett förälder>barn förhållande som du alltid vill lagra PrentId med den underordnade enheten. För föräldern kan du antingen välja att lagra en samling ChildIds med modellen och gör sedan en enda hämtning för alla underordnade enheter för att återhydrera modellen.

Ett annat sätt är att behålla indexet utanför den överordnade dto i sin egen uppsättning för varje föräldrainstans. Några bra exempel på detta finns i C#-källkoden för Redis StackOverflow-demon där förhållandet mellan Users > Questions och Users > Answers lagras i:

idx:user>q:{UserId} => [{QuestionId1},{QuestionId2},etc]
idx:user>a:{UserId} => [{AnswerId1},{AnswerId2},etc]

Även om C# RedisClient inkluderar stöd för en standardförälder/barn-konvention via dess TParent.StoreRelatedEntities(), TParent.GetRelatedEntities<TChild>() och TParent.DeleteRelatedEntities() API:er där ett index underhålls bakom scenen som ser ut så här:

ref:Question/Answer:{QuestionId} => [{answerIds},..]

I själva verket är dessa bara några av dina möjliga alternativ, där det finns många olika sätt att uppnå samma mål och där du också har friheten att rulla ditt eget.

NoSQL:s schemalösa, lösskrivningsfriheter bör omfattas och du bör inte vara orolig över att försöka följa en stel, fördefinierad struktur som du kanske känner till när du använder ett RDBMS.

Sammanfattningsvis finns det inget riktigt rätt sätt att lagra data i Redis, t.ex. C# Redis-klienten gör några antaganden för att tillhandahålla ett högnivå-API runt POCO:er och den blubbar POCO:erna i Redis binärt säkra strängvärden - även om det finns andra klienter som föredrar att lagra entitetsegenskaper i Redis Hashes (Dictionaries) istället . Båda kommer att fungera.




  1. Argument som skickas in måste vara en enda sträng på 12 byte

  2. MongoDB-aggregation med $lookup inkluderar (eller projekterar) endast några fält att returnera från frågan

  3. Hur man använder kryptering för att skydda dina MongoDB-data

  4. 3 enkla steg för att skapa MongoDB Sharded Clusters