Udostępnij za pośrednictwem


Antywzorzec monolitycznej trwałości

Umieszczanie wszystkich danych aplikacji w jednym magazynie danych może obniżyć wydajność, ponieważ prowadzi do rywalizacji zasobów lub magazyn danych nie jest odpowiedni dla niektórych danych.

Opis problemu

W przeszłości aplikacje często miały jeden magazyn danych niezależnie od różnych typów danych, które aplikacja mogła potrzebować przechowywać. Zwykle było to robione w celu uproszczenia projektu aplikacji lub, w przeciwnym razie, dopasowania istniejącego zestawu umiejętności zespołu deweloperów.

Nowoczesne systemy oparte na chmurze często mają dodatkowe wymagania funkcjonalne i niefunkcjonalne oraz potrzebują przechowywać wiele heterogenicznych typów danych, takich jak dokumenty, obrazy, dane z pamięci podręcznej, wiadomości w kolejce, dzienniki aplikacji i dane telemetryczne. Postępowanie zgodnie z tradycyjnym podejściem i umieszczanie wszystkich tych informacji w tym samym magazynie danych może pogarszać wydajność z dwóch głównych przyczyn:

  • Przechowywanie i pobieranie dużych ilości niepowiązanych danych w tym samym magazynie danych może powodować rywalizację, co z kolei prowadzi do wydłużenia czasu odpowiedzi i błędów połączenia.
  • Niezależnie od wybranego magazynu danych może on nie być najlepiej dopasowany do wszystkich różnych typów danych lub może nie być zoptymalizowany dla operacji, które wykonuje aplikacja.

W poniższym przykładzie przedstawiono kontroler internetowego interfejsu API platformy ASP.NET, który dodaje nowy rekord do bazy danych oraz rejestruje wynik w dzienniku. Dziennik jest przechowywany w tej samej bazie danych co dane biznesowe. Pełny przykład można znaleźć tutaj.

public class MonoController : ApiController
{
    private static readonly string ProductionDb = ...;

    public async Task<IHttpActionResult> PostAsync([FromBody]string value)
    {
        await DataAccess.InsertPurchaseOrderHeaderAsync(ProductionDb);
        await DataAccess.LogAsync(ProductionDb, LogTableName);
        return Ok();
    }
}

Szybkość generowania rekordów dziennika prawdopodobnie będzie miała wpływ na wydajność operacji biznesowych. A jeśli inny składnik, taki jak monitor procesu aplikacji, regularnie odczytuje i przetwarza dane dziennika, to może również mieć wpływ na operacje biznesowe.

Jak rozwiązać ten problem

Odseparuj dane zgodnie z ich użyciem. Dla każdego zestawu danych wybierz magazyn danych, który najlepiej odpowiada sposobowi użycia tego zestawu danych. W poprzednim przykładzie aplikacja powinna się zalogować do oddzielnego magazynu z bazy danych, która przechowuje dane biznesowe:

public class PolyController : ApiController
{
    private static readonly string ProductionDb = ...;
    private static readonly string LogDb = ...;

    public async Task<IHttpActionResult> PostAsync([FromBody]string value)
    {
        await DataAccess.InsertPurchaseOrderHeaderAsync(ProductionDb);
        // Log to a different data store.
        await DataAccess.LogAsync(LogDb, LogTableName);
        return Ok();
    }
}

Kwestie wymagające rozważenia

  • Odseparuj dane według sposobu ich używania i dostępu do nich. Na przykład nie przechowuj informacji dziennika i danych biznesowych w tym samym magazynie danych. Te typy danych mają znacząco różne wymagania i wzorce dostępu. Rekordy dziennika są z założenia sekwencyjne, zaś dane biznesowe najprawdopodobniej wymagają swobodnego dostępu i często są relacyjne.

  • Rozważ wzorzec dostępu do danych dla każdego typu danych. Na przykład przechowuj sformatowane raporty i dokumenty w bazie danych dokumentów, takiej jak Azure Cosmos DB, ale użyj usługi Azure Cache for Redis do buforowania danych tymczasowych.

  • Jeśli zastosujesz tę wskazówkę, ale nadal będziesz osiągać limity bazy danych, może być konieczne skalowanie w górę bazy danych. Należy również rozważyć skalowanie w poziomie i partycjonowanie obciążenia między serwerami bazy danych. Jednak partycjonowanie może wymagać przeprojektowania aplikacji. Aby uzyskać więcej informacji, zobacz Data partitioning (Partycjonowanie danych).

Jak wykryć problem

System będzie prawdopodobnie znacznie zwalniał i ostatecznie ulegnie awarii, ponieważ system wyczerpie zasoby, takie jak połączenia z bazą danych.

Możesz wykonać następujące kroki, aby ułatwić zidentyfikowanie przyczyny.

  1. Przygotuj instrumentację systemu do rejestracji kluczowych statystyk wydajności. Przechwytuj informacje o synchronizacji dla każdej operacji oraz punkty, w których aplikacja odczytuje i zapisuje dane.
  2. Jeśli to możliwe, monitoruj działający system przez kilka dni w środowisku produkcyjnym, aby uzyskać rzeczywisty obraz sposobu korzystania z systemu. Jeśli nie jest to możliwe, uruchom testy obciążenia przy użyciu realistycznej liczby wirtualnych użytkowników wykonujących serię typowych operacji.
  3. Użyj danych telemetrycznych do identyfikacji okresów pogorszenia wydajności.
  4. Zidentyfikuj, do których magazynów danych miał miejsce dostęp w tych okresach.
  5. Zidentyfikuj zasoby magazynu danych, dla których mogła występować rywalizacja.

Przykładowa diagnostyka

W poniższych sekcjach zastosowano te kroki do opisanej wcześniej przykładowej aplikacji.

Instrumentowanie i monitorowanie systemu

Na poniższym wykresie przedstawiono wyniki testów obciążeniowych na opisanej wcześniej przykładowej aplikacji. Test używał stopniowego obciążenia do 1000 równoczesnych użytkowników.

Wyniki testu obciążenia dla kontrolera opartego na języku SQL

W miarę wzrostu obciążenia do 700 użytkowników rośnie również przepływność. Ale w tym momencie przepływność się poziomuje i system wydaje się działać z maksymalną wydajnością. Średnia odpowiedź stopniowo zwiększa się wraz z obciążeniem użytkownikami, pokazując, że system nie może nadążyć za zapotrzebowaniem.

Identyfikacja okresów pogorszenia wydajności

Jeśli monitorujesz system produkcyjny, możesz zauważyć wzorce. Na przykład czas odpowiedzi może znacznie spaść w tym samym czasie każdego dnia. Może to być spowodowane przez zwykłe obciążenie lub zaplanowane zadanie wsadowe, lub po prostu, ponieważ system ma więcej użytkowników w określonym czasie. Należy skoncentrować się na danych telemetrycznych dla tych zdarzeń.

Poszukaj korelacji między zwiększonymi czasami odpowiedzi i zwiększoną aktywnością bazy danych lub operacji we/wy do udostępnionych zasobów. Jeśli istnieją korelacje, oznacza to, że baza danych może być wąskim gardłem.

Identyfikacja, do których magazynów danych miał miejsce dostęp w tych okresach

Następny wykres przedstawia wykorzystanie jednostek przepływności bazy danych (DTU) podczas testu obciążeniowego. (Jednostka DTU to miara dostępnej pojemności i jest kombinacją wykorzystania procesora CPU, alokacji pamięci, szybkości operacji we/wy). Wykorzystanie jednostek DTU szybko osiągnęło 100%. Jest to w przybliżeniu punkt, gdzie nastąpił skok przepływności na poprzednim wykresie. Użycie bazy danych pozostało bardzo duże do momentu zakończenia testu. Istnieje niewielki spadek przy końcu, który może być spowodowany przez ograniczanie, rywalizację o połączenia z bazą danych lub inne czynniki.

Monitor bazy danych w klasycznym portalu Azure przedstawiający wykorzystanie zasobów bazy danych

Sprawdzanie danych telemetrycznych dotyczących magazynów danych

Przygotuj instrumentację magazynów danych do przechwytywania niskopoziomowych szczegółów działania. W przykładowej aplikacji statystyki dostępu do danych wykazały dużą liczbę operacji wstawiania zarówno dla tabeli PurchaseOrderHeader, jak i tabeli MonoLog.

Statystyki dostępu do danych w przykładowej aplikacji

Identyfikacja rywalizacji o zasoby

W tym momencie możesz przejrzeć kod źródłowy, koncentrując się na punktach, gdzie aplikacja uzyskiwała dostęp do zasobów, o które była rywalizacja. Szukaj takich sytuacji, jak:

  • Dane, które są logicznie oddzielone, były zapisywane do tego samego magazynu. Dane, takie jak dzienniki, raporty i wiadomości w kolejce, nie powinny być przechowywane w tej samej bazie danych co informacje biznesowe.
  • Niezgodność między wyborem magazynu danych i typem danych, takich jak duże obiekty blob lub dokumenty XML w relacyjnej bazie danych.
  • Dane o znacząco odmiennych wzorcach użycia, które współużytkują ten sam magazyn, takie jak dane o wysokim zapisie/niskim odczycie przechowywane z danymi o niskim zapisie/wysokim odczycie.

Implementowanie rozwiązania i weryfikowanie wyniku

Aplikacja została zmieniona tak, aby zapisywała dzienniki do oddzielnego magazynu danych. Oto wyniki testu obciążenia:

Wyniki wydajności testu obciążenia przy użyciu kontrolera Polyglot

Wzorzec przepływności jest podobny do wcześniejszego wykresu, ale punkt, w którym wydajność gwałtownie skacze występuje dla około 500 żądań na sekundę więcej. Średni czas odpowiedzi jest pomijalnie mniejszy. Jednak te statystyki nie informują o wszystkim. Dane telemetryczne dla biznesowej bazy danych pokazują, że użycie jednostek DTU skacze dla około 75%, a nie 100%.

Monitor bazy danych w klasycznym portalu Azure przedstawiający wykorzystanie zasobów bazy danych w scenariuszu Polyglot

Podobnie maksymalne wykorzystanie jednostek DTU w bazie danych dziennika osiąga około 70%. Bazy danych nie są już czynnikiem ograniczającym wydajność systemu.

Monitor bazy danych w klasycznym portalu Azure przedstawiający wykorzystanie zasobów bazy danych dziennika w scenariuszu Polyglot