Modelowanie danych w usłudze Azure Cosmos DB

DOTYCZY: NoSQL

Bazy danych bez schematu, takie jak Usługa Azure Cosmos DB, ułatwiają przechowywanie danych bez struktury i częściowo ustrukturyzowanych oraz wykonywanie zapytań o nieustrukturyzowane, dlatego należy poświęcić trochę czasu na zastanowienie się nad modelem danych, aby jak najlepiej wykorzystać możliwości usługi pod względem wydajności i skalowalności i najniższych kosztów.

Jak będą przechowywane dane? Jak aplikacja będzie pobierać dane i wykonywać zapytania o nie? Czy aplikacja jest ciężka do odczytu, czy z dużą liczbą operacji zapisu?

Po przeczytaniu tego artykułu będziesz w stanie odpowiedzieć na następujące pytania:

  • Co to jest modelowanie danych i dlaczego należy się tym przejmować?
  • W jaki sposób modelowanie danych w usłudze Azure Cosmos DB różni się od relacyjnej bazy danych?
  • Jak mogę wyrażać relacje danych w nierelacyjnej bazie danych?
  • Kiedy osadzam dane i kiedy mogę połączyć się z danymi?

Liczby w formacie JSON

Usługa Azure Cosmos DB zapisuje dokumenty w formacie JSON. Oznacza to, że przed zapisaniem ich w formacie JSON należy dokładnie określić, czy konieczne jest przekonwertowanie liczb na ciągi. Wszystkie liczby powinny być w idealnym przypadku przekonwertowane na String, jeśli istnieje prawdopodobieństwo, że znajdują się poza granicami liczby podwójnej precyzji zgodnie z IEEE 754 binary64. Specyfikacja JSON określa przyczyny, dla których używanie liczb poza tą granicą jest w ogóle złą praktyką w formacie JSON z powodu prawdopodobnych problemów ze współdziałaniem. Te problemy są szczególnie istotne dla kolumny klucza partycji, ponieważ jest niezmienna i wymaga migracji danych do późniejszej zmiany.

Osadzanie danych

Po rozpoczęciu modelowania danych w usłudze Azure Cosmos DB spróbuj traktować jednostki jako elementy samodzielne reprezentowane jako dokumenty JSON.

Dla porównania najpierw zobaczmy, jak możemy modeluje dane w relacyjnej bazie danych. W poniższym przykładzie pokazano, jak osoba może być przechowywana w relacyjnej bazie danych.

Model relacyjnej bazy danych

Strategia, podczas pracy z relacyjnymi bazami danych, polega na normalizacji wszystkich danych. Normalizacja danych zwykle obejmuje pobranie jednostki, takiej jak osoba, i podzielenie ich na odrębne składniki. W powyższym przykładzie osoba może mieć wiele rekordów szczegółów kontaktu i wiele rekordów adresów. Szczegóły kontaktu można dalej podzielić, wyodrębniając typowe pola, takie jak typ. To samo dotyczy adresu, każdy rekord może być typu Home lub Business.

Założeniem przewodnim podczas normalizacji danych jest unikanie przechowywania nadmiarowych danych na każdym rekordzie i raczej odwoływanie się do danych. W tym przykładzie, aby odczytać osobę ze wszystkimi swoimi danymi kontaktowymi i adresami, musisz użyć funkcji JOINS, aby efektywnie tworzyć dane (lub denormalizować) dane w czasie wykonywania.

SELECT p.FirstName, p.LastName, a.City, cd.Detail
FROM Person p
JOIN ContactDetail cd ON cd.PersonId = p.Id
JOIN ContactDetailType cdt ON cdt.Id = cd.TypeId
JOIN Address a ON a.PersonId = p.Id

Operacje zapisu w wielu pojedynczych tabelach są wymagane do zaktualizowania danych kontaktowych i adresów pojedynczej osoby.

Teraz przyjrzyjmy się modelowi tych samych danych co jednostka samodzielna w usłudze Azure Cosmos DB.

{
    "id": "1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "addresses": [
        {
            "line1": "100 Some Street",
            "line2": "Unit 1",
            "city": "Seattle",
            "state": "WA",
            "zip": 98012
        }
    ],
    "contactDetails": [
        {"email": "thomas@andersen.com"},
        {"phone": "+1 555 555-5555", "extension": 5555}
    ]
}

Korzystając z powyższego podejścia, zdenormalizowaliśmy rekord osoby, osadzając wszystkie informacje związane z tą osobą, takie jak ich dane kontaktowe i adresy, w jednym dokumencie JSON . Ponadto, ponieważ nie ograniczamy się do stałego schematu, mamy elastyczność wykonywania takich czynności, jak posiadanie szczegółów kontaktowych różnych kształtów całkowicie.

Pobieranie pełnego rekordu osoby z bazy danych jest teraz pojedynczą operacją odczytu dla pojedynczego kontenera i pojedynczego elementu. Aktualizowanie danych kontaktowych i adresów rekordu osoby jest również pojedynczą operacją zapisu dla pojedynczego elementu.

Dzięki denormalizacji danych aplikacja może wymagać wydania mniejszej liczby zapytań i aktualizacji w celu ukończenia typowych operacji.

Kiedy należy osadzić

Ogólnie rzecz biorąc, używaj wbudowanych modeli danych, gdy:

  • Istnieją relacje między jednostkami.
  • Istnieją relacje jeden do kilku między jednostkami.
  • Istnieją osadzone dane, które zmieniają się rzadko.
  • Istnieją osadzone dane, które nie będą rosnąć bez ograniczenia.
  • Istnieją osadzone dane, do których często są wykonywane zapytania.

Uwaga

Zazwyczaj zdenormalizowane modele danych zapewniają lepszą wydajność odczytu .

Kiedy nie należy osadzać

Chociaż regułą kciuka w usłudze Azure Cosmos DB jest denormalizacja wszystkiego i osadzanie wszystkich danych w jednym elemencie, może to prowadzić do niektórych sytuacji, których należy unikać.

Weźmy ten fragment kodu JSON.

{
    "id": "1",
    "name": "What's new in the coolest Cloud",
    "summary": "A blog post by someone real famous",
    "comments": [
        {"id": 1, "author": "anon", "comment": "something useful, I'm sure"},
        {"id": 2, "author": "bob", "comment": "wisdom from the interwebs"},
        …
        {"id": 100001, "author": "jane", "comment": "and on we go ..."},
        …
        {"id": 1000000001, "author": "angry", "comment": "blah angry blah angry"},
        …
        {"id": ∞ + 1, "author": "bored", "comment": "oh man, will this ever end?"},
    ]
}

Może to być tak, jak wyglądałaby jednostka wpisu z osadzonymi komentarzami, gdyby modelowaliśmy typowy blog lub system CMS. Problem z tym przykładem polega na tym, że tablica komentarzy jest niezwiązana, co oznacza, że nie ma (praktycznego) limitu liczby komentarzy, które może zawierać dowolny pojedynczy wpis. Może to stać się problemem, ponieważ rozmiar elementu może być nieskończenie duży, więc jest to projekt, którego należy unikać.

Wraz ze wzrostem rozmiaru elementu będzie mieć wpływ na możliwość przesyłania danych za pośrednictwem przewodu i odczytywania i aktualizowania elementu na dużą skalę.

W takim przypadku lepszym rozwiązaniem byłoby rozważenie następującego modelu danych.

Post item:
{
    "id": "1",
    "name": "What's new in the coolest Cloud",
    "summary": "A blog post by someone real famous",
    "recentComments": [
        {"id": 1, "author": "anon", "comment": "something useful, I'm sure"},
        {"id": 2, "author": "bob", "comment": "wisdom from the interwebs"},
        {"id": 3, "author": "jane", "comment": "....."}
    ]
}

Comment items:
[
    {"id": 4, "postId": "1", "author": "anon", "comment": "more goodness"},
    {"id": 5, "postId": "1", "author": "bob", "comment": "tails from the field"},
    ...
    {"id": 99, "postId": "1", "author": "angry", "comment": "blah angry blah angry"},
    {"id": 100, "postId": "2", "author": "anon", "comment": "yet more"},
    ...
    {"id": 199, "postId": "2", "author": "bored", "comment": "will this ever end?"}   
]

Ten model zawiera dokument dla każdego komentarza z właściwością zawierającą identyfikator wpisu. Dzięki temu wpisy mogą zawierać dowolną liczbę komentarzy i mogą być wydajnie powiększane. Użytkownicy, którzy chcą zobaczyć więcej niż najnowsze komentarze, wysyłają zapytanie do tego kontenera, przekazując identyfikator postId, który powinien być kluczem partycji dla kontenera komentarzy.

Innym przypadkiem, w którym osadzanie danych nie jest dobrym pomysłem, jest to, że osadzane dane są często używane między elementami i często się zmieniają.

Weźmy ten fragment kodu JSON.

{
    "id": "1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "holdings": [
        {
            "numberHeld": 100,
            "stock": { "symbol": "zbzb", "open": 1, "high": 2, "low": 0.5 }
        },
        {
            "numberHeld": 50,
            "stock": { "symbol": "xcxc", "open": 89, "high": 93.24, "low": 88.87 }
        }
    ]
}

Może to reprezentować portfel akcji danej osoby. Postanowiliśmy osadzić informacje o zapasach w każdym dokumencie portfela. W środowisku, w którym powiązane dane zmieniają się często, jak aplikacja do handlu akcjami, osadzanie danych, które zmieniają się często, oznacza to, że stale aktualizujesz każdy dokument portfela za każdym razem, gdy akcje są notowane.

Akcje zbzb mogą być notowane wiele setek razy w ciągu jednego dnia, a tysiące użytkowników może mieć zbzb na swoim portfelu. W przypadku modelu danych, takiego jak powyżej, musielibyśmy aktualizować wiele tysięcy dokumentów portfolio wiele razy dziennie, co prowadzi do systemu, który nie będzie dobrze skalowany.

Dane referencyjne

Osadzanie danych działa ładnie w wielu przypadkach, ale istnieją scenariusze, w których denormalizacja danych spowoduje więcej problemów niż warto. Więc co teraz robimy?

Relacyjne bazy danych nie są jedynym miejscem, w którym można tworzyć relacje między jednostkami. W bazie danych dokumentów mogą znajdować się informacje w jednym dokumencie, które odnoszą się do danych w innych dokumentach. Nie zalecamy kompilowania systemów, które byłyby lepiej dostosowane do relacyjnej bazy danych w usłudze Azure Cosmos DB, ani innych baz danych dokumentów, ale proste relacje są poprawne i mogą być przydatne.

W poniższym kodzie JSON wybraliśmy użycie przykładu portfela akcji z wcześniejszej wersji, ale tym razem odwołujemy się do pozycji akcji w portfelu zamiast jej osadzania. W ten sposób, gdy pozycja zapasów zmienia się często w ciągu dnia jedynym dokumentem, który należy zaktualizować, jest pojedynczy dokument giełdowy.

Person document:
{
    "id": "1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "holdings": [
        { "numberHeld":  100, "stockId": 1},
        { "numberHeld":  50, "stockId": 2}
    ]
}

Stock documents:
{
    "id": "1",
    "symbol": "zbzb",
    "open": 1,
    "high": 2,
    "low": 0.5,
    "vol": 11970000,
    "mkt-cap": 42000000,
    "pe": 5.89
},
{
    "id": "2",
    "symbol": "xcxc",
    "open": 89,
    "high": 93.24,
    "low": 88.87,
    "vol": 2970200,
    "mkt-cap": 1005000,
    "pe": 75.82
}

Bezpośrednią wadą tego podejścia jest jednak to, że aplikacja jest wymagana do wyświetlania informacji o każdym magazynie przechowywanym podczas wyświetlania portfela danej osoby; W takim przypadku należy wykonać wiele podróży do bazy danych, aby załadować informacje dla każdego dokumentu magazynowego. W tym miejscu podjęliśmy decyzję o poprawie wydajności operacji zapisu, które występują często przez cały dzień, ale z kolei naruszone w przypadku operacji odczytu, które potencjalnie mają mniejszy wpływ na wydajność tego konkretnego systemu.

Uwaga

Znormalizowane modele danych mogą wymagać większej liczby rund na serwerze.

A co z kluczami obcymi?

Ponieważ obecnie nie ma pojęcia ograniczenia, klucza obcego lub w inny sposób, wszystkie relacje między dokumentami, które znajdują się w dokumentach, są skutecznie "słabymi linkami" i nie zostaną zweryfikowane przez samą bazę danych. Jeśli chcesz mieć pewność, że dane, do których odwołuje się dokument, rzeczywiście istnieją, musisz to zrobić w aplikacji lub za pomocą wyzwalaczy po stronie serwera lub procedur składowanych w usłudze Azure Cosmos DB.

Kiedy należy się odwołać

Ogólnie rzecz biorąc, używaj znormalizowanych modeli danych, gdy:

  • Reprezentowanie relacji jeden-do-wielu .
  • Reprezentowanie relacji wiele-do-wielu .
  • Powiązane dane często się zmieniają.
  • Przywoływane dane mogą być niezwiązane.

Uwaga

Zwykle normalizacja zapewnia lepszą wydajność zapisu .

Gdzie mogę umieścić relację?

Rozwój relacji pomoże określić, w którym dokumencie ma być przechowywane odwołanie.

Jeśli przyjrzymy się poniższej kodzie JSON, modelujemy wydawców i książki.

Publisher document:
{
    "id": "mspress",
    "name": "Microsoft Press",
    "books": [ 1, 2, 3, ..., 100, ..., 1000]
}

Book documents:
{"id": "1", "name": "Azure Cosmos DB 101" }
{"id": "2", "name": "Azure Cosmos DB for RDBMS Users" }
{"id": "3", "name": "Taking over the world one JSON doc at a time" }
...
{"id": "100", "name": "Learn about Azure Cosmos DB" }
...
{"id": "1000", "name": "Deep Dive into Azure Cosmos DB" }

Jeśli liczba książek na wydawcę jest niewielka z ograniczonym wzrostem, przechowywanie dokumentacji książki w dokumencie wydawcy może być przydatne. Jeśli jednak liczba książek na wydawcę jest niezwiązana, ten model danych doprowadzi do modyfikowalnego, rosnącej tablicy, jak w powyższym przykładowym dokumencie wydawcy.

Przełączenie elementów wokół bitu spowodowałoby model, który nadal reprezentuje te same dane, ale teraz pozwala uniknąć tych dużych kolekcji modyfikowalnych.

Publisher document:
{
    "id": "mspress",
    "name": "Microsoft Press"
}

Book documents:
{"id": "1","name": "Azure Cosmos DB 101", "pub-id": "mspress"}
{"id": "2","name": "Azure Cosmos DB for RDBMS Users", "pub-id": "mspress"}
{"id": "3","name": "Taking over the world one JSON doc at a time", "pub-id": "mspress"}
...
{"id": "100","name": "Learn about Azure Cosmos DB", "pub-id": "mspress"}
...
{"id": "1000","name": "Deep Dive into Azure Cosmos DB", "pub-id": "mspress"}

W powyższym przykładzie usunięto niepowiązaną kolekcję w dokumencie wydawcy. Zamiast tego mamy tylko odwołanie do wydawcy w każdym dokumencie książki.

Jak mogę modelu relacji wiele do wielu?

W relacyjnej bazie danych relacje wiele do wielu są często modelowane za pomocą tabel sprzężenia, które po prostu łączą rekordy z innych tabel.

Tabele sprzężenia

Możesz być kuszony, aby zreplikować to samo przy użyciu dokumentów i utworzyć model danych, który wygląda podobnie do poniższego.

Author documents:
{"id": "a1", "name": "Thomas Andersen" }
{"id": "a2", "name": "William Wakefield" }

Book documents:
{"id": "b1", "name": "Azure Cosmos DB 101" }
{"id": "b2", "name": "Azure Cosmos DB for RDBMS Users" }
{"id": "b3", "name": "Taking over the world one JSON doc at a time" }
{"id": "b4", "name": "Learn about Azure Cosmos DB" }
{"id": "b5", "name": "Deep Dive into Azure Cosmos DB" }

Joining documents:
{"authorId": "a1", "bookId": "b1" }
{"authorId": "a2", "bookId": "b1" }
{"authorId": "a1", "bookId": "b2" }
{"authorId": "a1", "bookId": "b3" }

To zadziała. Jednak załadowanie autora za pomocą książek lub załadowanie książki z jego autorem zawsze wymagałoby co najmniej dwóch dodatkowych zapytań względem bazy danych. Jedno zapytanie do dokumentu dołączania, a następnie inne zapytanie, aby pobrać rzeczywisty dokument, który jest przyłączony.

Jeśli to sprzężenie jest przyklejane tylko do dwóch fragmentów danych, dlaczego nie upuść go całkowicie? Rozważmy następujący przykład.

Author documents:
{"id": "a1", "name": "Thomas Andersen", "books": ["b1", "b2", "b3"]}
{"id": "a2", "name": "William Wakefield", "books": ["b1", "b4"]}

Book documents:
{"id": "b1", "name": "Azure Cosmos DB 101", "authors": ["a1", "a2"]}
{"id": "b2", "name": "Azure Cosmos DB for RDBMS Users", "authors": ["a1"]}
{"id": "b3", "name": "Learn about Azure Cosmos DB", "authors": ["a1"]}
{"id": "b4", "name": "Deep Dive into Azure Cosmos DB", "authors": ["a2"]}

Teraz, gdybym miał autora, natychmiast wiem, które książki napisali, i odwrotnie, gdybym miał załadowany dokument książki, wiedziałbym identyfikatory autorów. Spowoduje to zapisanie zapytania pośredniczącego względem tabeli sprzężenia, co zmniejsza liczbę rund serwera, które aplikacja musi wykonać.

Modele danych hybrydowych

Zapoznaliśmy się teraz z osadzaniem (lub denormalizacją) i odwoływaniem się (lub normalizacją) danych. Każde podejście ma wady i kompromisy.

To nie zawsze musi być albo lub, nie boi się mieszać rzeczy trochę.

Na podstawie konkretnych wzorców użycia i obciążeń aplikacji mogą występować przypadki, w których mieszanie osadzonych i przywołynych danych ma sens i może prowadzić do prostszej logiki aplikacji z mniejszą liczbą rund serwera przy jednoczesnym zachowaniu dobrego poziomu wydajności.

Rozważmy następujący kod JSON.

Author documents:
{
    "id": "a1",
    "firstName": "Thomas",
    "lastName": "Andersen",
    "countOfBooks": 3,
    "books": ["b1", "b2", "b3"],
    "images": [
        {"thumbnail": "https://....png"}
        {"profile": "https://....png"}
        {"large": "https://....png"}
    ]
},
{
    "id": "a2",
    "firstName": "William",
    "lastName": "Wakefield",
    "countOfBooks": 1,
    "books": ["b1"],
    "images": [
        {"thumbnail": "https://....png"}
    ]
}

Book documents:
{
    "id": "b1",
    "name": "Azure Cosmos DB 101",
    "authors": [
        {"id": "a1", "name": "Thomas Andersen", "thumbnailUrl": "https://....png"},
        {"id": "a2", "name": "William Wakefield", "thumbnailUrl": "https://....png"}
    ]
},
{
    "id": "b2",
    "name": "Azure Cosmos DB for RDBMS Users",
    "authors": [
        {"id": "a1", "name": "Thomas Andersen", "thumbnailUrl": "https://....png"},
    ]
}

W tym miejscu (głównie) wykonaliśmy model osadzony, w którym dane z innych jednostek są osadzone w dokumencie najwyższego poziomu, ale przywoływane są inne dane.

Jeśli spojrzysz na dokument książki, zobaczymy kilka interesujących pól, gdy przyjrzymy się tablicy autorów. id Istnieje pole, które jest polem używanym do odwoływania się do dokumentu autora, standardowej praktyki w znormalizowanym modelu, ale mamy również i namethumbnailUrl. Moglibyśmy zablokować id i pozostawić aplikację, aby uzyskać dodatkowe informacje potrzebne z odpowiedniego dokumentu autora przy użyciu "linku", ale ponieważ nasza aplikacja wyświetla nazwę autora i obraz miniatury z każdą wyświetloną książką, możemy zapisać rundę na serwerze na liście, denormalizując niektóre dane od autora.

Oczywiście, jeśli nazwa autora zmieniła się lub chcieli zaktualizować swoje zdjęcie, musielibyśmy zaktualizować każdą książkę, którą kiedykolwiek opublikowali, ale dla naszej aplikacji, na podstawie założenia, że autorzy nie zmieniają swoich nazw często, jest to akceptowalna decyzja projektowa.

W tym przykładzie istnieją wstępnie obliczone wartości agregacji w celu zaoszczędzenia kosztownego przetwarzania podczas operacji odczytu. W tym przykładzie niektóre dane osadzone w dokumencie autora to dane obliczane w czasie wykonywania. Za każdym razem, gdy zostanie opublikowana nowa książka, zostanie utworzony dokument książki , a pole countOfBooks ma wartość obliczeniową na podstawie liczby dokumentów książki, które istnieją dla określonego autora. Ta optymalizacja byłaby dobra w systemach o dużym obciążeniu odczytu, w których możemy sobie pozwolić na wykonywanie obliczeń na zapisach w celu zoptymalizowania operacji odczytu.

Możliwość posiadania modelu ze wstępnie obliczonymi polami jest możliwa, ponieważ usługa Azure Cosmos DB obsługuje transakcje obejmujące wiele dokumentów. Wiele sklepów NoSQL nie może wykonywać transakcji w dokumentach i dlatego opowiada się za decyzjami projektowymi, takimi jak "zawsze osadź wszystko", ze względu na to ograniczenie. Za pomocą usługi Azure Cosmos DB można używać wyzwalaczy po stronie serwera lub procedur składowanych, które wstawią książki i aktualizują autorów w ramach transakcji ACID. Teraz nie musisz osadzać wszystkiego w jednym dokumencie, aby upewnić się, że dane pozostają spójne.

Rozróżnianie różnych typów dokumentów

W niektórych scenariuszach warto mieszać różne typy dokumentów w tej samej kolekcji; Zwykle jest tak, gdy chcesz, aby wiele powiązanych dokumentów siedziało w tej samej partycji. Można na przykład umieścić zarówno książki, jak i recenzje książek w tej samej kolekcji i podzielić ją na partycje według .bookId W takiej sytuacji zwykle chcesz dodać do dokumentów pole identyfikujące ich typ w celu ich odróżnienia.

Book documents:
{
    "id": "b1",
    "name": "Azure Cosmos DB 101",
    "bookId": "b1",
    "type": "book"
}

Review documents:
{
    "id": "r1",
    "content": "This book is awesome",
    "bookId": "b1",
    "type": "review"
},
{
    "id": "r2",
    "content": "Best book ever!",
    "bookId": "b1",
    "type": "review"
}

Azure Synapse Link dla usługi Azure Cosmos DB to natywna dla chmury funkcja hybrydowego przetwarzania transakcyjnego i analitycznego (HTAP), która umożliwia uruchamianie analizy niemal w czasie rzeczywistym na danych operacyjnych w usłudze Azure Cosmos DB. Usługa Azure Synapse Link tworzy ścisłą i bezproblemową integrację między usługą Azure Cosmos DB i usługą Azure Synapse Analytics.

Ta integracja odbywa się za pośrednictwem magazynu analitycznego usługi Azure Cosmos DB, kolumnowej reprezentacji danych transakcyjnych, która umożliwia analizę na dużą skalę bez wpływu na obciążenia transakcyjne. Ten magazyn analityczny jest odpowiedni dla szybkich, tanich zapytań dotyczących dużych zestawów danych operacyjnych bez kopiowania danych i wpływania na wydajność obciążeń transakcyjnych. Po utworzeniu kontenera z włączonym magazynem analitycznym lub włączeniu magazynu analitycznego w istniejącym kontenerze wszystkie transakcyjne wstawki, aktualizacje i usunięcia są synchronizowane z magazynem analitycznym niemal w czasie rzeczywistym, nie są wymagane żadne zadania zestawienia zmian ani zadania ETL.

Dzięki usłudze Azure Synapse Link możesz teraz bezpośrednio łączyć się z kontenerami usługi Azure Cosmos DB z usługi Azure Synapse Analytics i uzyskiwać dostęp do magazynu analitycznego bez kosztów jednostek żądań (jednostek żądania). usługa Azure Synapse Analytics obecnie obsługuje usługę Azure Synapse Link z usługą Synapse Apache Spark i bezserwerowymi pulami SQL. Jeśli masz globalnie rozproszone konto usługi Azure Cosmos DB, po włączeniu magazynu analitycznego dla kontenera będzie ono dostępne we wszystkich regionach dla tego konta.

Automatyczne wnioskowanie schematu magazynu analitycznego

Magazyn transakcyjny usługi Azure Cosmos DB jest uważany za dane częściowo ustrukturyzowane zorientowane na wiersz, magazyn analityczny ma format kolumnowy i ustrukturyzowany. Ta konwersja jest automatycznie dokonana dla klientów przy użyciu reguł wnioskowania schematu dla magazynu analitycznego. Istnieją limity w procesie konwersji: maksymalna liczba zagnieżdżonych poziomów, maksymalna liczba właściwości, nieobsługiwane typy danych i nie tylko.

Uwaga

W kontekście magazynu analitycznego uważamy następujące struktury za właściwość:

  • JSON "elements" lub "string-value pairs rozdzielone przez : ".
  • Obiekty JSON, rozdzielane przez { i }.
  • Tablice JSON, rozdzielane przez [ i ].

Możesz zminimalizować wpływ konwersji wnioskowania schematu i zmaksymalizować możliwości analityczne, korzystając z poniższych technik.

Normalizacja

Normalizacja staje się bez znaczenia, ponieważ za pomocą Azure Synapse Link można połączyć się między kontenerami przy użyciu języka T-SQL lub Spark SQL. Oczekiwane korzyści wynikające z normalizacji to:

  • Mniejszy ślad danych w magazynie transakcyjnym i analitycznym.
  • Mniejsze transakcje.
  • Mniej właściwości na dokument.
  • Struktury danych z mniejszą liczbą poziomów zagnieżdżonych.

Należy pamiętać, że te dwa ostatnie czynniki, mniejsze właściwości i mniej poziomów, pomagają w wydajności zapytań analitycznych, ale także zmniejszają prawdopodobieństwo, że części danych nie są reprezentowane w magazynie analitycznym. Zgodnie z opisem w artykule dotyczącym reguł automatycznego wnioskowania schematu istnieją ograniczenia dotyczące liczby poziomów i właściwości reprezentowanych w magazynie analitycznym.

Innym ważnym czynnikiem normalizacji jest to, że pule bezserwerowe SQL w Azure Synapse obsługują zestawy wyników z maksymalnie 1000 kolumnami i uwidacznianie zagnieżdżonych kolumn również liczy się w kierunku tego limitu. Innymi słowy, zarówno magazyn analityczny, jak i pule bezserwerowe usługi Synapse SQL mają limit 1000 właściwości.

Ale co zrobić, ponieważ denormalizacja jest ważną techniką modelowania danych dla usługi Azure Cosmos DB? Odpowiedź polega na tym, że musisz znaleźć właściwą równowagę dla obciążeń transakcyjnych i analitycznych.

Klucz partycji

Klucz partycji usługi Azure Cosmos DB (PK) nie jest używany w magazynie analitycznym. Teraz możesz użyć partycjonowania niestandardowego magazynu analitycznego do kopiowania magazynu analitycznego przy użyciu dowolnego klucza PK. Ze względu na tę izolację można wybrać klucz PK dla danych transakcyjnych z fokusem na pozyskiwaniu danych i odczytach punktów, podczas gdy zapytania obejmujące wiele partycji można wykonać za pomocą usługi Azure Synapse Link. Zobaczmy przykład:

W hipotetycznym globalnym scenariuszu IoT jest dobrym kluczem PK, device id ponieważ wszystkie urządzenia mają podobny wolumin danych i że nie będziesz mieć problemu z gorącą partycją. Jeśli jednak chcesz przeanalizować dane więcej niż jednego urządzenia, na przykład "wszystkie dane z wczoraj" lub "sumy na miasto", mogą wystąpić problemy, ponieważ są to zapytania obejmujące wiele partycji. Te zapytania mogą zaszkodzić wydajności transakcyjnej, ponieważ używają części przepływności w jednostkach żądań do uruchomienia. Jednak za pomocą usługi Azure Synapse Link można uruchamiać te zapytania analityczne bez kosztów jednostek żądań. Format kolumnowy magazynu analitycznego jest zoptymalizowany pod kątem zapytań analitycznych i Azure Synapse Link stosuje tę charakterystykę, aby zapewnić doskonałą wydajność w środowiskach uruchomieniowych usługi Azure Synapse Analytics.

Typy danych i nazwy właściwości

Artykuł dotyczący reguł wnioskowania automatycznego schematu zawiera listę obsługiwanych typów danych. Chociaż nieobsługiwany typ danych blokuje reprezentację w magazynie analitycznym, obsługiwane typy danych mogą być przetwarzane inaczej przez środowiska uruchomieniowe Azure Synapse. Jednym z przykładów jest: W przypadku używania ciągów typu DateTime, które są zgodne ze standardem ISO 8601 UTC, pule platformy Spark w Azure Synapse będą reprezentować te kolumny jako ciągi i pule bezserwerowe SQL w Azure Synapse będą reprezentować te kolumny jako varchar(8000).

Innym wyzwaniem jest to, że nie wszystkie znaki są akceptowane przez platformę Azure Synapse Spark. Podczas akceptowania białych spacji znaki, takie jak dwukropek, akcent grobowy i przecinek, nie są. Załóżmy, że dokument ma właściwość o nazwie "Imię, Nazwisko". Ta właściwość będzie reprezentowana w magazynie analitycznym, a pula bezserwerowa usługi Synapse SQL może ją odczytać bez problemu. Ponieważ jednak znajduje się ona w magazynie analitycznym, Azure Synapse platforma Spark nie może odczytać żadnych danych z magazynu analitycznego, w tym wszystkich innych właściwości. Na koniec dnia nie można używać Azure Synapse Spark, gdy masz jedną właściwość, używając nieobsługiwanych znaków w nazwach.

Spłaszczanie danych

Wszystkie właściwości na poziomie głównym danych usługi Azure Cosmos DB będą reprezentowane w magazynie analitycznym jako kolumna i wszystkie inne właściwości, które są na głębszych poziomach modelu danych dokumentu, będą reprezentowane jako dane JSON, również w strukturach zagnieżdżonych. Zagnieżdżone struktury wymagają dodatkowego przetwarzania ze środowiska uruchomieniowego Azure Synapse w celu spłaszczenia danych w formacie ustrukturyzowanym, co może być wyzwaniem w scenariuszach dotyczących danych big data.

Poniższy dokument będzie miał tylko dwie kolumny w magazynie id analitycznym i contactDetails. Wszystkie inne dane email i phone, będą wymagały dodatkowego przetwarzania za pomocą funkcji SQL, które będą odczytywane indywidualnie.


{
    "id": "1",
    "contactDetails": [
        {"email": "thomas@andersen.com"},
        {"phone": "+1 555 555-5555"}
    ]
}

Poniższy dokument będzie zawierać trzy kolumny w magazynie analitycznym, id, emaili phone. Wszystkie dane są bezpośrednio dostępne jako kolumny.


{
    "id": "1",
    "email": "thomas@andersen.com",
    "phone": "+1 555 555-5555"
}

Warstwy danych

Azure Synapse Link umożliwia zmniejszenie kosztów z następujących perspektyw:

  • Mniej zapytań uruchomionych w transakcyjnej bazie danych.
  • Klucz PK zoptymalizowany pod kątem pozyskiwania danych i odczytów punktów, zmniejszając ślad danych, scenariusze gorącej partycji i podziały partycji.
  • Obsługa warstw danych od czasu wygaśnięcia analitycznego (attl) jest niezależna od transakcyjnego czasu wygaśnięcia (tttl). Dane transakcyjne można przechowywać w magazynie transakcyjnym przez kilka dni, tygodni, miesięcy i przechowywać dane w magazynie analitycznym przez lata lub na zawsze. Format kolumnowy magazynu analitycznego zapewnia kompresję danych naturalnych z zakresu od 50% do 90%. A koszt za GB wynosi ok. 10% rzeczywistej ceny sklepu transakcyjnego. Aby uzyskać więcej informacji na temat bieżących ograniczeń kopii zapasowych, zobacz Omówienie magazynu analitycznego.
  • Brak zadań ETL uruchomionych w środowisku, co oznacza, że nie trzeba aprowizować dla nich jednostek żądań.

Kontrolowana nadmiarowość

Jest to świetna alternatywa dla sytuacji, w których model danych już istnieje i nie można go zmienić. Istniejący model danych nie pasuje do magazynu analitycznego ze względu na reguły automatycznego wnioskowania schematu, takie jak limit zagnieżdżonych poziomów lub maksymalna liczba właściwości. Jeśli tak jest, możesz użyć zestawienia zmian usługi Azure Cosmos DB, aby replikować dane do innego kontenera, stosując wymagane przekształcenia dla modelu danych przyjaznego Azure Synapse Link. Zobaczmy przykład:

Scenariusz

Kontener CustomersOrdersAndItems służy do przechowywania zamówień w wierszu, w tym szczegółów klienta i elementów: adres rozliczeniowy, adres dostawy, metoda dostawy, stan dostawy, cena elementów itp. Tylko pierwsze 1000 właściwości są reprezentowane, a informacje o kluczu nie są uwzględniane w magazynie analitycznym, blokując użycie Azure Synapse Link. Kontener zawiera rekordy PB, których nie można zmienić i zmienić modelu danych.

Kolejną perspektywą problemu jest ilość danych big data. Miliardy wierszy są stale używane przez dział analizy, co uniemożliwia im użycie czasu wygaśnięcia do starego usunięcia danych. Obsługa całej historii danych w transakcyjnej bazie danych z powodu potrzeb analitycznych wymusza na nich ciągłe zwiększanie aprowizacji jednostek żądań, co wpływa na koszty. Obciążenia transakcyjne i analityczne konkurują o te same zasoby w tym samym czasie.

Postępowanie

Rozwiązanie ze źródłem zmian

  • Zespół inżynierów postanowił użyć zestawienia zmian, aby wypełnić trzy nowe kontenery: Customers, Ordersi Items. W przypadku zestawienia zmian normalizują i spłaszczają dane. Niepotrzebne informacje są usuwane z modelu danych, a każdy kontener ma blisko 100 właściwości, unikając utraty danych z powodu automatycznych limitów wnioskowania schematu.
  • Te nowe kontenery mają włączony magazyn analityczny, a teraz dział analizy używa usługi Synapse Analytics do odczytywania danych, zmniejszając użycie jednostek żądań, ponieważ zapytania analityczne są wykonywane w usłudze Synapse Apache Spark i bezserwerowych pulach SQL.
  • Kontener CustomersOrdersAndItems ma teraz ustawiony czas wygaśnięcia, aby przechowywać dane tylko przez sześć miesięcy, co pozwala na zmniejszenie użycia kolejnych jednostek żądań, ponieważ w usłudze Azure Cosmos DB istnieje co najmniej 10 jednostek żądań na GB. Mniej danych, mniejsza liczba jednostek żądania.

Wnioski

Największymi wynos z tego artykułu są zrozumienie, że modelowanie danych w świecie bez schematu jest tak ważne, jak zawsze.

Tak jak nie ma jednego sposobu reprezentowania kawałka danych na ekranie, nie ma jednego sposobu modelowania danych. Musisz zrozumieć aplikację i jak będzie ona tworzyć, zużywać i przetwarzać dane. Następnie, stosując niektóre z przedstawionych tutaj wytycznych, można ustawić tworzenie modelu, który odpowiada bezpośrednim potrzebom aplikacji. Gdy aplikacje muszą ulec zmianie, możesz łatwo wykorzystać elastyczność bazy danych bez schematu, aby łatwo przekształcić ten model danych i rozwinąć go.

Następne kroki