Redis
 sql >> Baza danych >  >> NoSQL >> Redis

Migracje danych z Redis

Ta strona zawiera typowy przykład, aby pokazać, jak bezbolesne mogą być typowe migracje danych podczas korzystania z Redis i innych magazynów danych bez schematów NoSQL.

Wszystkie strony aplikacji Redis Blog #

  • Projektowanie bazy danych NoSQL przy użyciu Redis
  • Bezproblemowe migracje danych przy użyciu Redis i innych nieschematycznych magazynów danych NoSQL

Bezproblemowe migracje danych z nieschematycznymi magazynami danych NoSQL i Redis #

Tworzenie nowych Systemy baz danych greenfield wykorzystujące back-end RDBMS są w większości bezproblemowe. Zanim system zostanie uruchomiony, możesz łatwo modyfikować schemat przez nukowanie całej bazy danych aplikacji i ponowne tworzenie jej za pomocą zautomatyzowanych skryptów DDL, które stworzą go i zapełnią danymi testowymi, które pasują do nowego schematu.

Prawdziwe problemy w życiu IT pojawiają się już po pierwszym wdrożeniu i uruchomieniu systemu. W tym momencie nie masz już możliwości nukeingu bazy danych i ponownego tworzenia jej od zera. Jeśli masz szczęście, masz skrypt, który może automatycznie wywnioskować wymagane instrukcje DDL do migracji ze starego schematu do nowego. Jednak wszelkie znaczące zmiany w schemacie mogą obejmować późne noce, przestoje i nietrywialny wysiłek, aby zapewnić pomyślną migrację do nowego schematu bazy danych.

Ten proces jest znacznie mniej bolesny w przypadku magazynów danych bez schematów. W rzeczywistości w większości przypadków, gdy tylko dodajesz i usuwasz pola, w ogóle nie istnieje. Ponieważ magazyn danych nie rozumie wewnętrznych szczegółów schematu, oznacza to, że nie jest to już problem na poziomie infrastruktury i w razie potrzeby można go łatwo obsłużyć za pomocą logiki aplikacji.

Bezobsługowość, brak schematów i brak ingerencji to podstawowe cechy projektowe wpisane w Redis i jego operacje. Na przykład zapytanie o listę ostatnich wpisów na blogu zwraca ten sam wynik dla pustej listy tak jak w pustej bazie danych Redis - 0 wyników. Ponieważ wartości w Redis są ciągami bezpiecznymi dla plików binarnych, możesz przechowywać w nich wszystko, co chcesz, a co najważniejsze, oznacza to, że wszystkie operacje Redis mogą obsługiwać wszystkie typy aplikacji bez potrzeby „języka pośredniego”, takiego jak DDL, aby zapewnić sztywny schemat tego, czego się spodziewać. Bez wcześniejszej inicjalizacji Twój kod może komunikować się bezpośrednio z magazynem danych Redis w sposób naturalny, tak jakby był zbiorem w pamięci.

Aby zilustrować, co można osiągnąć w praktyce, przedstawię dwie różne strategie postępowania ze zmianami schematu.

  • Podejście „nie robić nic” – gdzie dodawanie, usuwanie pól i nieniszcząca zmiana typów pól są obsługiwane automatycznie.
  • Korzystanie z niestandardowego tłumaczenia — używanie logiki poziomu aplikacji do dostosowywania tłumaczenia między starymi i nowymi typami.

Pełny możliwy do uruchomienia kod źródłowy tego przykładu jest dostępny tutaj.

Przykładowy kod #

Aby zademonstrować typowy scenariusz migracji, używam BlogPost typ zdefiniowany na poprzedniej stronie, aby wyświetlić go na zasadniczo innym New.BlogPost rodzaj. Pełna definicja starego i nowego typu jest pokazana poniżej:

Stary schemat #

public class BlogPost
{
    public BlogPost()
    {
        this.Categories = new List<string>();
        this.Tags = new List<string>();
        this.Comments = new List<BlogPostComment>();
    }

    public int Id { get; set; }
    public int BlogId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public List<string> Categories { get; set; }
    public List<string> Tags { get; set; }
    public List<BlogPostComment> Comments { get; set; }
}

public class BlogPostComment
{
    public string Content { get; set; }
    public DateTime CreatedDate { get; set; }
}

Nowy schemat #

„Nowa wersja” zawiera większość zmian, które możesz napotkać podczas normalnego tworzenia aplikacji:

  • Dodane, usunięte i zmienione nazwy pól
  • Nieniszcząca zmiana int na long i double pola
  • Zmieniono typ kolekcji tagów z List do HashSet
  • Zmieniono silnie wpisany BlogPostComment wpisz do luźno wpisanego ciągu Dictionary
  • Wprowadzono nowe enum wpisz
  • Dodano pole obliczeniowe dopuszczające wartość null

Nowe typy schematów #

public class BlogPost
{
    public BlogPost()
    {
        this.Labels = new List<string>();
        this.Tags = new HashSet<string>();
        this.Comments = new List<Dictionary<string, string>>();
    }

    //Changed int types to both a long and a double type
    public long Id { get; set; }
    public double BlogId { get; set; }

    //Added new field
    public BlogPostType PostType { get; set; }

    public string Title { get; set; }
    public string Content { get; set; }

    //Renamed from 'Categories' to 'Labels'
    public List<string> Labels { get; set; }

    //Changed from List to a HashSet
    public HashSet<string> Tags { get; set; }

    //Changed from List of strongly-typed 'BlogPostComment' to loosely-typed string map
    public List<Dictionary<string, string>> Comments { get; set; }

    //Added pointless calculated field
    public int? NoOfComments { get; set; }
}

public enum BlogPostType
{
    None,
    Article,
    Summary,
}

1. Podejście „nic nie rób” – używanie starych danych z nowymi typami #

Choć trudno w to uwierzyć, bez dodatkowego wysiłku możesz po prostu udawać, nie dokonano żadnych zmian i swobodnie uzyskuj dostęp do nowych typów, patrząc na stare dane. Jest to możliwe w przypadku nieniszczących zmian (tj. bez utraty informacji) z nowymi typami pól. Poniższy przykład wykorzystuje repozytorium z poprzedniego przykładu do wypełnienia Redis danymi testowymi ze starych typów. Tak jakby nic się nie stało, możesz odczytać stare dane za pomocą nowego typu:

var repository = new BlogRepository(redisClient);

//Populate the datastore with the old schema from the 'BlogPostBestPractice'
BlogPostBestPractice.InsertTestData(repository);

//Create a typed-client based on the new schema
using (var redisBlogPosts = redisClient.GetTypedClient<New.BlogPost>())
{
    //Automatically retrieve blog posts
    IList<New.BlogPost> allBlogPosts = redisBlogPosts.GetAll();

    //Print out the data in the list of 'New.BlogPost' populated from old 'BlogPost' type
    Console.WriteLine(allBlogPosts.Dump());
    /*Output:
    [
        {
            Id: 3,
            BlogId: 2,
            PostType: None,
            Title: Redis,
            Labels: [],
            Tags: 
            [
                Redis,
                NoSQL,
                Scalability,
                Performance
            ],
            Comments: 
            [
                {
                    Content: First Comment!,
                    CreatedDate: 2010-04-28T21:42:03.9484725Z
                }
            ]
        },
        {
            Id: 4,
            BlogId: 2,
            PostType: None,
            Title: Couch Db,
            Labels: [],
            Tags: 
            [
                CouchDb,
                NoSQL,
                JSON
            ],
            Comments: 
            [
                {
                    Content: First Comment!,
                    CreatedDate: 2010-04-28T21:42:03.9484725Z
                }
            ]
        },
        {
            Id: 1,
            BlogId: 1,
            PostType: None,
            Title: RavenDB,
            Labels: [],
            Tags: 
            [
                Raven,
                NoSQL,
                JSON,
                .NET
            ],
            Comments: 
            [
                {
                    Content: First Comment!,
                    CreatedDate: 2010-04-28T21:42:03.9004697Z
                },
                {
                    Content: Second Comment!,
                    CreatedDate: 2010-04-28T21:42:03.9004697Z
                }
            ]
        },
        {
            Id: 2,
            BlogId: 1,
            PostType: None,
            Title: Cassandra,
            Labels: [],
            Tags: 
            [
                Cassandra,
                NoSQL,
                Scalability,
                Hashing
            ],
            Comments: 
            [
                {
                    Content: First Comment!,
                    CreatedDate: 2010-04-28T21:42:03.9004697Z
                }
            ]
        }
    ]

     */
}

2. Używanie niestandardowego tłumaczenia do migracji danych przy użyciu logiki aplikacji #

Niektóre wady powyższego podejścia „nic nie rób” polegają na utracie danych z „pól o zmienionej nazwie”. Może się również zdarzyć, że nowo zmigrowane dane będą miały określone wartości, które różnią się od wbudowanych wartości domyślnych platformy .NET. Jeśli chcesz mieć większą kontrolę nad migracją starych danych, dodanie niestandardowego tłumaczenia jest trywialnym ćwiczeniem, gdy możesz to zrobić natywnie w kodzie:

var repository = new BlogRepository(redisClient);

//Populate the datastore with the old schema from the 'BlogPostBestPractice'
BlogPostBestPractice.InsertTestData(repository);

//Create a typed-client based on the new schema
using (var redisBlogPosts = redisClient.GetTypedClient<BlogPost>())
using (var redisNewBlogPosts = redisClient.GetTypedClient<New.BlogPost>())
{
    //Automatically retrieve blog posts
    IList<BlogPost> oldBlogPosts = redisBlogPosts.GetAll();

    //Write a custom translation layer to migrate to the new schema
    var migratedBlogPosts = oldBlogPosts.ConvertAll(old => new New.BlogPost
    {
        Id = old.Id,
        BlogId = old.BlogId,
        Title = old.Title,
        Content = old.Content,
        Labels = old.Categories, //populate with data from renamed field
        PostType = New.BlogPostType.Article, //select non-default enum value
        Tags = old.Tags,
        Comments = old.Comments.ConvertAll(x => new Dictionary<string, string> 
            { { "Content", x.Content }, { "CreatedDate", x.CreatedDate.ToString() }, }),
        NoOfComments = old.Comments.Count, //populate using logic from old data
    });

    //Persist the new migrated blogposts 
    redisNewBlogPosts.StoreAll(migratedBlogPosts);

    //Read out the newly stored blogposts
    var refreshedNewBlogPosts = redisNewBlogPosts.GetAll();
    //Note: data renamed fields are successfully migrated to the new schema
    Console.WriteLine(refreshedNewBlogPosts.Dump());
    /*
    [
        {
            Id: 3,
            BlogId: 2,
            PostType: Article,
            Title: Redis,
            Labels: 
            [
                NoSQL,
                Cache
            ],
            Tags: 
            [
                Redis,
                NoSQL,
                Scalability,
                Performance
            ],
            Comments: 
            [
                {
                    Content: First Comment!,
                    CreatedDate: 28/04/2010 22:58:35
                }
            ],
            NoOfComments: 1
        },
        {
            Id: 4,
            BlogId: 2,
            PostType: Article,
            Title: Couch Db,
            Labels: 
            [
                NoSQL,
                DocumentDB
            ],
            Tags: 
            [
                CouchDb,
                NoSQL,
                JSON
            ],
            Comments: 
            [
                {
                    Content: First Comment!,
                    CreatedDate: 28/04/2010 22:58:35
                }
            ],
            NoOfComments: 1
        },
        {
            Id: 1,
            BlogId: 1,
            PostType: Article,
            Title: RavenDB,
            Labels: 
            [
                NoSQL,
                DocumentDB
            ],
            Tags: 
            [
                Raven,
                NoSQL,
                JSON,
                .NET
            ],
            Comments: 
            [
                {
                    Content: First Comment!,
                    CreatedDate: 28/04/2010 22:58:35
                },
                {
                    Content: Second Comment!,
                    CreatedDate: 28/04/2010 22:58:35
                }
            ],
            NoOfComments: 2
        },
        {
            Id: 2,
            BlogId: 1,
            PostType: Article,
            Title: Cassandra,
            Labels: 
            [
                NoSQL,
                Cluster
            ],
            Tags: 
            [
                Cassandra,
                NoSQL,
                Scalability,
                Hashing
            ],
            Comments: 
            [
                {
                    Content: First Comment!,
                    CreatedDate: 28/04/2010 22:58:35
                }
            ],
            NoOfComments: 1
        }
    ]

     */
}

Efektem końcowym jest magazyn danych wypełniony nowymi danymi wypełnionymi dokładnie tak, jak chcesz - gotowy do obsługi funkcji nowej aplikacji. W przeciwieństwie do tego, próba wykonania powyższego w typowym rozwiązaniu RDBMS bez przestojów jest w rzeczywistości wyczynem magii, który jest nagradzany 999 punktami Stack Overflow i osobistymi kondolencjami od wielkiego kanclerza @JonSkeet 😃

Mam nadzieję, że to wyraźnie ilustruje różnice między tymi dwiema technologiami. W praktyce będziesz zdumiony wzrostem produktywności możliwym, gdy nie będziesz musiał modelować swojej aplikacji tak, aby pasowała do ORM i RDBMS i możesz zapisywać obiekty tak, jak była to pamięć.

Zawsze dobrze jest wystawić się na nowe technologie, więc jeśli jeszcze tego nie zrobiłeś, zapraszam do rozpoczęcia rozwoju z Redis już dziś, aby zobaczyć korzyści dla siebie. Aby rozpocząć wszystko, czego potrzebujesz, to instancja serwera redis (nie jest wymagana konfiguracja, po prostu rozpakuj i uruchom) oraz wolny od zależności klient C# Redis ServiceStack i gotowe!


No
  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Uzyskaj dostęp do redis lokalnie w dockerze — docker compose

  2. Resque na stos cedru Heroku Liczba robotników nadal istnieje po zakończeniu pracy robotnika

  3. Czy możliwe jest posiadanie pamięci podręcznej Linux VFS z systemem plików FUSE?

  4. Jak skonfigurować seler-redis w projekcie django na microsoft azure?

  5. Subskrybenci i wydawcy Redis pub sub max