ServiceStack.Net Redis: Przechowywanie powiązanych obiektów a powiązanych identyfikatorów obiektów

Mój zespół zdecydował się na współpracę z Redis poprzez ServiceStack.net Klient Redis jako podstawowe repozytorium dla nowej witryny o dużej objętości, nad którą pracujemy. Nie jestem do końca pewien, gdzie szukać dokumentacji dla tego pytania (albo dla ogólnych dokumentów Redis lub konkretnych ServiceStack.Net docs or both) - czy rzeczywiście istnieje ostateczne źródło dokumentacji jak wdrożyć Redis poprzez ServiceStack.Net obejmuje to wszystko, co musisz wiedzieć o koncepcjach Redis i ServiceStack.Net koncepcje, czy też musimy zintegrować dokumentację z obu aspektów osobno,aby uzyskać pełny obraz?.

Zastanawiam się, jak dokładnie przechowywać powiązane obiekty w wykresie obiektu naszego modelu. Oto prosty scenariusz, z którym chcę pracować:

W systemie są dwa obiekty: User i Feed. W terminologii RDBMS te dwa obiekty mają relację jeden do wielu, to znaczy User ma zbiór Feed obiektów i kanał może należeć tylko do jednego User. Pasze będą zawsze być dostępne z Redis przez ich użytkownika, ale od czasu do czasu będziemy chcieli uzyskać dostęp do Użytkownika za pośrednictwem instancji feeda.

Więc mam pytanie, czy powinniśmy przechowywać powiązane obiekty jako właściwości, czy powinniśmy przechowywać Id wartości powiązanych obiektów? Do zilustrowania:

Podejście A :

public class User
{
    public User()
    {
        Feeds = new List<Feed>();
    }

    public int Id { get; set; }

    public List<Feed> Feeds { get; set; }

    // Other properties
}

public class Feed
{
    public long Id { get; set; }

    public User User { get; set; }
}

Podejście B :

public class User
{
    public User()
    {
        FeedIds = new List<long>();
    }

    public long Id { get; set; }

    public List<long> FeedIds { get; set; } 

    public List<Feed> GetFeeds()
    {
        return repository.GetFeeds( FeedIds );
    }
}

public class Feed
{
    public long Id { get; set; }

    public long UserId { get; set; }

    public User GetUser()
    {
        return repository.GetUser( UserId );
    }
}

Które z powyższych podejść zadziała najlepiej? Widziałem oba podejścia stosowane w różnych przykładach, ale Mam wrażenie, że niektóre z przykładów, które widziałem, mogą nie być najlepszymi praktykami.

Kilka prostych pytań:

  • jeśli dokonam zmiany w obiekcie, czy zostanie ona automatycznie odzwierciedlona w Redis czy będzie wymagała zapisu? Zakładam, że to drugie, ale musi być absolutnie jasne.
  • jeśli (mogę) użyć podejścia A, czy aktualizacja obiektu użytkownika X zostanie odzwierciedlona w całym wykresie obiektu, niezależnie od tego, czy konieczne będzie zapisanie zmian przez wykres?
  • czy jest problem z przechowywaniem obiektu przez jego interfejs (np. użycie IList<Feed> w przeciwieństwie do List<Feed>?

Przepraszam, jeśli te pytania są trochę podstawowe - jeszcze 2 tygodnie temu nigdy nie słyszałem o Redis - a co dopiero ServiceStack - (nie miałem nikogo w moim zespole) więc naprawdę zaczynamy od zera tutaj...

Author: Zac Seth, 2012-01-18

1 answers

Zamiast ponownie hashować wiele innych dokumentacji, które są tam w dziczy, wymienię kilka informacji o kliencie Redis + ServiceStack:

Nie ma magii-Redis to puste płótno

Najpierw chcę zauważyć, że używanie Redis jako magazynu danych zapewnia tylko puste płótno i nie ma żadnej koncepcji powiązanych podmiotów. tzn. zapewnia tylko dostęp do rozproszonych struktur danych comp-sci. Sposób przechowywania relacji zależy ostatecznie od Sterownika klienta (np. ServiceStack C# Redis Client) lub twórcy aplikacji, korzystając z prymitywnych operacji struktury danych Redis. Ponieważ wszystkie główne struktury danych są zaimplementowane w Redis, w zasadzie masz pełną swobodę w zakresie struktury i przechowywania danych.

Pomyśl, jak uporządkujesz relacje w kodzie]}

Więc najlepszym sposobem, aby myśleć o tym, jak przechowywać rzeczy w Redis, jest całkowite pominięcie tego, jak dane są przechowywane w tabeli RDBMS i zastanowienie się, jak są przechowywane w kodzie, tj. używając wbudowanych klas kolekcji C# w pamięci-które Redis odzwierciedla w zachowaniu z ich danymi. struktury danych po stronie serwera.

Pomimo braku koncepcji podmiotów powiązanych, wbudowane struktury danych RedisSet iSortedSet zapewniają idealny sposób przechowywania indeksów. Na przykład zbiór Redisa przechowuje tylko maksymalnie 1 wystąpienie elementu. Oznacza to, że możesz bezpiecznie dodać do niego elementy/klucze/identyfikatory i nie dbać o to, czy Element już istnieje, ponieważ efekt końcowy będzie taki sam, jeśli nazwałeś go 1 lub 100 razy - tzn. jest idempotentny i ostatecznie tylko W zestawie pozostaje 1 element. Tak więc powszechnym przypadkiem użycia jest przechowywanie wykresu obiektowego (zagregowanego katalogu głównego), polegające na przechowywaniu identyfikatorów jednostek potomnych (aka kluczy obcych) w zestawie za każdym razem, gdy zapisujesz model.

Wizualizacja danych

Aby uzyskać dobrą wizualizację tego, jak encje są przechowywane w Redis, zalecam zainstalowanie Redis Admin UI , który działa dobrze z Klientem C# Redis ServiceStack, ponieważ używa konwencji nazw kluczy poniżej, aby zapewnić ładny widok hierarchiczny, grupowanie wpisanych elementów razem (pomimo wszystkich kluczy istniejących w tej samej globalnej przestrzeni kluczy).

Aby wyświetlić i edytować obiekt, kliknij łącze Edytuj , aby zobaczyć i zmodyfikować wewnętrzną reprezentację JSON wybranego obiektu. Mam nadzieję, że będziesz w stanie podejmować lepsze decyzje dotyczące projektowania modeli, gdy zobaczysz, jak są przechowywane.

Jak są przechowywane poco / Entity

Klient C# Redis działa z każdym POCOs, który ma jeden klucz główny - który domyślnie oczekuje się, że Id (chociaż ta konwencja jest nadpisywana przez ModelConfig ). Zasadniczo POCOs jest przechowywany w Redis jako serializowany JSON z typeof(Poco).Name i Id używanymi do tworzenia unikalnego klucza dla tej instancji. Np.:

urn:Poco:{Id} => '{"Id":1,"Foo":"Bar"}'
Poco w kliencie C# są standardowo serializowane przy użyciu usługi ServiceStack ' s fast JSON Serializer gdzie serializowane są tylko właściwości z publicznymi geterami (i publiczne setery, aby uzyskać z powrotem de-serialized).

domyślne wartości są nadpisywalne przez [DataMember] attrs, ale nie zalecane, ponieważ brzydki POCOs.

Byty są blobbed

Więc wiedząc, że POCOs w Redis są po prostu blobbed, chcesz tylko zachować nie zagregowane dane główne w POCOs jako właściwości publiczne (chyba że celowo chcesz przechowywać nadmiarowe dane). Dobrą konwencją jest używanie metod do pobierania powiązanych danych (ponieważ nie będą one serializowane), ale także mówi aplikacji, które metody sprawiają, że zdalne połączenia do odczytu danych.

Więc pytanie, czyFeed powinien być przechowywany uużytkownika , brzmi, czy nie są to zagregowane dane root, tzn. czy chcesz uzyskać dostęp do kanałów użytkowników poza kontekstem użytkownika? Jeśli nie, to pozostaw właściwość List<Feed> Feeds na typie User.

Utrzymanie Własnych Indeksów

Jeśli jednak chcesz, aby wszystkie kanały były dostępne niezależnie, tj. za pomocą redisFeeds.GetById(1), będziesz chciał przechowywać je poza użytkownika i utrzymania indeksu łączącego 2 podmioty.

Jak zauważyłeś, istnieje wiele sposobów przechowywania relacji między podmiotami, a sposób, w jaki to robisz, jest w dużej mierze kwestią preferencji. W przypadku podmiotu potomnego w relacji rodzic>dziecko zawsze chcesz zapisać ParentId z podmiotem potomnym. Dla rodzica możesz albo zapisać kolekcję ChildIds z modelem, a następnie wykonać pojedyncze pobieranie dla wszystkich jednostek potomnych do nawodnić model.

Innym sposobem jest utrzymanie indeksu poza nadrzędnym dto w jego własnym zestawie dla każdej instancji nadrzędnej. Kilka dobrych przykładów znajduje się w C# kod źródłowy Z Redis StackOverflow demo gdzie relacja Users > Questions i Users > Answers jest przechowywana w:

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

Chociaż C# RedisClient zawiera wsparcie dla domyślnej konwencji rodzica / dziecka poprzez TParent.Magazynier(), TParent.GetRelatedEntities<TChild>() oraz TParent.DeleteRelatedEntities() API, gdzie indeks jest utrzymywany za sceną, która wygląda następująco:

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

W praktyce to tylko niektóre z Twoich możliwych opcji, gdzie istnieje wiele różnych sposobów osiągnięcia tego samego celu i w którym masz również swobodę obracania własnych.

Wolności NoSQL bez schematycznego, luźnego pisania powinny zostać zaakceptowane i nie powinieneś martwić się o próbę podążania za sztywną, wstępnie zdefiniowaną strukturą, z którą możesz być zaznajomiony podczas korzystania z RDBMS.

Podsumowując, nie ma prawdziwego właściwego sposobu do przechowywania danych w Redis, np. Klient C# Redis przyjmuje pewne założenia w celu zapewnienia wysokiego poziomu API wokół POCOs i blokuje POCOs w bezpiecznych binarnie wartościach łańcuchowych Redis-chociaż są inni klienci wolą przechowywać właściwości encji w Hashach Redis (słownikach) zamiast. Oba się sprawdzą.

 65
Author: mythz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:17:37