Udostępnij za pośrednictwem


Diagnozowanie i rozwiązywanie problemów z powolnymi żądaniami w zestawie SDK platformy .NET usługi Azure Cosmos DB

DOTYCZY: NoSQL

W usłudze Azure Cosmos DB możesz zauważyć powolne żądania. Opóźnienia mogą wystąpić z wielu powodów, takich jak ograniczanie żądań lub sposób projektowania aplikacji. W tym artykule wyjaśniono różne główne przyczyny tego problemu.

Zbyt duża liczba żądań

Ograniczanie żądań jest najczęstszą przyczyną powolnych żądań. Usługa Azure Cosmos DB ogranicza żądania, jeśli przekraczają przydzielone jednostki żądania dla bazy danych lub kontenera. Zestaw SDK ma wbudowaną logikę, aby ponowić próby tych żądań. W artykule dotyczącym rozwiązywania problemów z zbyt dużą szybkością żądań wyjaśniono, jak sprawdzić, czy żądania są ograniczane. W tym artykule omówiono również sposób skalowania konta w celu uniknięcia tych problemów w przyszłości.

Projekt aplikacji

Podczas projektowania aplikacji postępuj zgodnie z najlepszymi rozwiązaniami dotyczącymi zestawu SDK platformy .NET, aby uzyskać najlepszą wydajność. Jeśli aplikacja nie jest zgodna z najlepszymi rozwiązaniami dotyczącymi zestawu SDK, żądania mogą być powolne lub nieudane.

Podczas tworzenia aplikacji należy wziąć pod uwagę następujące kwestie:

  • Aplikacja powinna znajdować się w tym samym regionie co konto usługi Azure Cosmos DB.
  • Region aplikacji lub ApplicationPreferredRegions powinien odzwierciedlać preferencje regionalne i wskazać region, w którym aplikacja jest wdrożona.
  • Może wystąpić wąskie gardło w interfejsie sieciowym z powodu dużego ruchu. Jeśli aplikacja jest uruchomiona w usłudze Azure Virtual Machines, istnieją możliwe obejścia:
  • Preferuj tryb łączności bezpośredniej.
  • Unikaj wysokiego użycia procesora CPU. Pamiętaj, aby przyjrzeć się maksymalnemu procesorowi CPU, a nie średniej, która jest wartością domyślną dla większości systemów rejestrowania. Wszystkie elementy powyżej około 40 procent mogą zwiększyć opóźnienie.

Operacje na metadanych

Jeśli musisz sprawdzić, czy baza danych lub kontener istnieje, nie należy tego robić przez wywołanie Create...IfNotExistsAsync lub Read...Async przed wykonaniem operacji elementu. Walidację należy wykonać tylko podczas uruchamiania aplikacji, jeśli jest to konieczne, jeśli spodziewasz się ich usunięcia. Te operacje metadanych generują dodatkowe opóźnienia, nie mają umowy dotyczącej poziomu usług (SLA) i mają własne oddzielne ograniczenia. Nie są skalowane tak jak operacje na danych.

Wolne żądania w trybie zbiorczym

Tryb zbiorczy jest trybem zoptymalizowanym pod kątem przepływności przeznaczonym dla operacji o dużej ilości danych, a nie w trybie zoptymalizowanym pod kątem opóźnień. Ma to na celu nasycenie dostępnej przepływności. Jeśli podczas korzystania z trybu zbiorczego występują powolne żądania, upewnij się, że:

  • Aplikacja jest kompilowana w konfiguracji wydania.
  • Nie mierzysz opóźnienia podczas debugowania aplikacji (bez dołączonych debugerów).
  • Liczba operacji jest duża, nie używaj zbiorczo dla mniej niż 1000 operacji. Aprowizowana przepływność określa, ile operacji na sekundę można przetworzyć. Celem operacji zbiorczych byłoby wykorzystanie jak największej ilości operacji.
  • Monitoruj kontener pod kątem scenariuszy ograniczania przepustowości. Jeśli kontener jest coraz intensywnie ograniczany, oznacza to, że ilość danych jest większa niż aprowizowana przepływność, musisz skalować kontener w górę lub zmniejszyć ilość danych (może utworzyć mniejsze partie danych naraz).
  • Prawidłowo używasz async/await wzorca do przetwarzania wszystkich współbieżnych zadań i nie blokuje żadnej operacji asynchronicznych.

Przechwytywanie danych diagnostycznych

Wszystkie odpowiedzi w zestawie SDK, w tym CosmosException, mają Diagnostics właściwość . Ta właściwość rejestruje wszystkie informacje związane z pojedynczym żądaniem, w tym w przypadku ponownych prób lub błędów przejściowych.

Diagnostyka jest zwracana jako ciąg. Ciąg zmienia się wraz z każdą wersją, ponieważ został ulepszony do rozwiązywania problemów z różnymi scenariuszami. W przypadku każdej wersji zestawu SDK ciąg będzie miał zmiany powodujące niezgodność w formatowaniu. Nie analizuj ciągu, aby uniknąć zmian powodujących niezgodność. Poniższy przykładowy kod pokazuje, jak odczytywać dzienniki diagnostyczne przy użyciu zestawu SDK platformy .NET:

try
{
    ItemResponse<Book> response = await this.Container.CreateItemAsync<Book>(item: testItem);
    if (response.Diagnostics.GetClientElapsedTime() > ConfigurableSlowRequestTimeSpan)
    {
        // Log the response.Diagnostics.ToString() and add any additional info necessary to correlate to other logs 
    }
}
catch (CosmosException cosmosException)
{
    // Log the full exception including the stack trace with: cosmosException.ToString()
    
    // The Diagnostics can be logged separately if required with: cosmosException.Diagnostics.ToString()
}

// When using Stream APIs
ResponseMessage response = await this.Container.CreateItemStreamAsync(partitionKey, stream);
if (response.Diagnostics.GetClientElapsedTime() > ConfigurableSlowRequestTimeSpan || !response.IsSuccessStatusCode)
{
    // Log the diagnostics and add any additional info necessary to correlate to other logs with: response.Diagnostics.ToString()
}

Diagnostyka w wersji 3.19 lub nowszej

Struktura JSON zawiera zmiany powodujące niezgodność z każdą wersją zestawu SDK. To sprawia, że niebezpieczne jest analizowanie. Kod JSON reprezentuje strukturę drzewa żądania przechodzącego przez zestaw SDK. W poniższych sekcjach omówiono kilka kluczowych kwestii, na które należy zwrócić uwagę.

Historia procesora CPU

Wysokie wykorzystanie procesora CPU jest najczęstszą przyczyną wolnych żądań. Aby uzyskać optymalne opóźnienie, użycie procesora powinno wynosić około 40%. Użyj interwału 10 sekund, aby monitorować maksymalne (a nie średnie) użycie procesora. Skoki użycia procesora CPU są częściej spotykane w przypadku zapytań obejmujących wiele partycji, w których żądania mogą wykonywać wiele połączeń dla pojedynczego zapytania.

Limity czasu obejmują diagnostykę, która zawiera następujące elementy, na przykład:

"systemHistory": [
{
"dateUtc": "2021-11-17T23:38:28.3115496Z",
"cpu": 16.731,
"memory": 9024120.000,
"threadInfo": {
"isThreadStarving": "False",
....
}

},
{
"dateUtc": "2021-11-17T23:38:38.3115496Z",
"cpu": 16.731,
"memory": 9024120.000,
"threadInfo": {
"isThreadStarving": "False",
....
}

},
...
]
  • cpu Jeśli wartości są ponad 70 procent, limit czasu prawdopodobnie będzie spowodowany wyczerpaniem procesora CPU. W takim przypadku rozwiązaniem jest zbadanie źródła dużego użycia procesora CPU i zmniejszenie go lub przeskalowanie maszyny do większego rozmiaru zasobów.
  • Jeśli węzły threadInfo/isThreadStarving mają True wartości, przyczyną jest głodowanie wątku. W takim przypadku rozwiązaniem jest zbadanie źródła lub źródeł głodu wątku (potencjalnie zablokowanych wątków) lub skalowanie maszyny do większego rozmiaru zasobów.
  • dateUtc Jeśli czas między pomiarami nie wynosi około 10 sekund, wskazuje również rywalizację w puli wątków. Procesor CPU jest mierzony jako niezależne zadanie, które jest w kolejce w puli wątków co 10 sekund. Jeśli czas między pomiarami jest dłuższy, oznacza to, że zadania asynchroniczne nie mogą być przetwarzane w odpowiednim czasie. Najbardziej typowym scenariuszem jest to, że kod aplikacji blokuje wywołania za pośrednictwem kodu asynchronicznego.

Rozwiązanie

Aplikacja kliencka korzystająca z zestawu SDK powinna zostać przeskalowana w górę lub w poziomie.

HttpResponseStats

HttpResponseStatsto żądania, które przechodzą do bramy. Nawet w trybie bezpośrednim zestaw SDK pobiera wszystkie informacje o metadanych z bramy.

Jeśli żądanie jest powolne, najpierw sprawdź, czy żadna z poprzednich sugestii nie daje pożądanych wyników. Jeśli nadal działa wolno, różne wzorce wskazują na różne problemy. Poniższa tabela zawiera więcej szczegółów.

Liczba żądań Scenariusz opis
Jeden do wszystkich Limit czasu żądania lub HttpRequestExceptions Wskazuje na wyczerpanie portów SNAT lub brak zasobów na maszynie w celu przetworzenia żądania w czasie.
Pojedyncza lub niewielka wartość procentowa (umowa SLA nie jest naruszona) wszystkie Pojedynczy lub niewielki procent wolnych żądań może być spowodowany kilkoma różnymi przejściowymi problemami i powinien być oczekiwany.
wszystkie wszystkie Wskazuje na problem z infrastrukturą lub siecią.
Naruszono umowę SLA Nie wprowadzono żadnych zmian w aplikacji i umowa SLA została porzucona. Wskazuje na problem z usługą Azure Cosmos DB.
"HttpResponseStats": [
    {
        "StartTimeUTC": "2021-06-15T13:53:09.7961124Z",
        "EndTimeUTC": "2021-06-15T13:53:09.7961127Z",
        "RequestUri": "https://127.0.0.1:8081/dbs/347a8e44-a550-493e-88ee-29a19c070ecc/colls/4f72e752-fa91-455a-82c1-bf253a5a3c4e",
        "ResourceType": "Collection",
        "HttpMethod": "GET",
        "ActivityId": "e16e98ec-f2e3-430c-b9e9-7d99e58a4f72",
        "StatusCode": "OK"
    }
]

StoreResult

StoreResult reprezentuje pojedyncze żądanie do usługi Azure Cosmos DB przy użyciu trybu bezpośredniego z protokołem TCP.

Jeśli nadal działa wolno, różne wzorce wskazują na różne problemy. Poniższa tabela zawiera więcej szczegółów.

Liczba żądań Scenariusz opis
Jeden do wszystkich StoreResult Contains TransportException Wskazuje na wyczerpanie portów SNAT lub brak zasobów na maszynie w celu przetworzenia żądania w czasie.
Pojedyncza lub niewielka wartość procentowa (umowa SLA nie jest naruszona) wszystkie Pojedynczy lub niewielki procent wolnych żądań może być spowodowany kilkoma różnymi przejściowymi problemami i powinien być oczekiwany.
wszystkie wszystkie Problem z infrastrukturą lub siecią.
Naruszono umowę SLA Żądania zawierają wiele kodów błędów błędów, takich jak 410 Wskazuje na problem z usługą Azure Cosmos DB lub maszyną kliencką.
Naruszono umowę SLA StorePhysicalAddress są takie same, bez kodu stanu błędu. Prawdopodobnie występuje problem z usługą Azure Cosmos DB.
Naruszono umowę SLA StorePhysicalAddress mają ten sam identyfikator partycji, ale różne identyfikatory replik, bez kodu stanu błędu. Prawdopodobnie występuje problem z usługą Azure Cosmos DB.
Naruszono umowę SLA StorePhysicalAddress jest losowy, bez kodu stanu błędu. Wskazuje na problem z maszyną.

W przypadku wielu wyników magazynu dla jednego żądania należy pamiętać o następujących kwestiach:

  • Silna spójność i powiązana spójność nieaktualności zawsze mają co najmniej dwa wyniki przechowywania.
  • Sprawdź kod stanu każdego StoreResultelementu . Zestaw SDK ponawia próby automatycznie na wielu różnych błędach przejściowych. Zestaw SDK jest stale ulepszany, aby uwzględnić więcej scenariuszy.

RequestTimeline

Pokaż czas dla różnych etapów wysyłania i odbierania żądania w warstwie transportu.

  • ChannelAcquisitionStarted: czas na pobranie lub utworzenie nowego połączenia. Połączenia można utworzyć z wielu powodów, takich jak: Poprzednie połączenie zostało zamknięte z powodu braku aktywności przy użyciu elementu CosmosClientOptions.IdleTcpConnectionTimeout, liczba współbieżnych żądań przekracza wartość CosmosClientOptions.MaxRequestsPerTcpConnection, połączenie zostało zamknięte z powodu błędu sieciowego lub aplikacja nie jest stale tworzona zgodnie ze wzorcem singleton, a nowe wystąpienia są stale tworzone. Po nawiązaniu połączenia jest ono ponownie używane dla kolejnych żądań, więc nie powinno to mieć wpływu na opóźnienie P99, chyba że występują wcześniej wymienione problemy.
  • Pipelined: czas spędzony na zapisaniu żądania do gniazda TCP. Żądanie można zapisywać tylko na gniazdach TCP pojedynczo. Duża wartość wskazuje wąskie gardło gniazda TCP, które jest często skojarzone z zablokowanymi wątkami przez kod aplikacji lub duże żądania.
  • Transit time: czas spędzony w sieci po zapisaniu żądania w gniazdie TCP. Porównaj tę liczbę z .BELatencyInMs Jeśli BELatencyInMs jest mały, to czas spędzony w sieci, a nie w usłudze Azure Cosmos DB. Jeśli żądanie nie powiodło się z przekroczeniem limitu czasu, wskazuje, jak długo klient czekał bez odpowiedzi, a źródłem jest opóźnienie sieci.
  • Received: czas między odebraniem odpowiedzi a przetworzeniem przez zestaw SDK. Duża wartość jest zwykle spowodowana głodem wątku lub zablokowanymi wątkami.

ServiceEndpointStatistics

Informacje o określonym serwerze zaplecza. Zestaw SDK może otwierać wiele połączeń z jednym serwerem zaplecza w zależności od liczby oczekujących żądań i maxConcurrentRequestsPerConnection.

  • inflightRequests Liczba oczekujących żądań na serwer zaplecza (może z różnych partycji). Duża liczba może prowadzić do większego ruchu i większych opóźnień.
  • openConnections to całkowita liczba połączeń otwartych dla pojedynczego serwera zaplecza. Może to być przydatne, aby pokazać wyczerpanie portów SNAT, jeśli ta liczba jest bardzo wysoka.

ConnectionStatistics

Informacje o konkretnym połączeniu (nowym lub starym) do którego zostanie przypisane żądanie.

  • waitforConnectionInit: Bieżące żądanie czekało na ukończenie nowej inicjowania połączenia. Doprowadzi to do wyższych opóźnień.
  • callsPendingReceive: liczba wywołań oczekujących na odebranie przed wysłaniem tego połączenia. Duża liczba może pokazać nam, że było wiele wywołań przed tym wywołaniem i może to prowadzić do wyższych opóźnień. Jeśli ta liczba jest duża, wskazuje na problem z blokowaniem wiersza prawdopodobnie spowodowany przez inne żądanie, takie jak operacja zapytania lub kanału informacyjnego, która trwa długo. Spróbuj zmniejszyć liczbę kanałów CosmosClientOptions.MaxRequestsPerTcpConnection.
  • LastSentTime: Czas ostatniego żądania wysłanego na ten serwer. Ta funkcja wraz z elementem LastReceivedTime może służyć do wyświetlenia problemów z łącznością lub punktem końcowym. Jeśli na przykład istnieje wiele limitów czasu odbierania, czas wysłania będzie znacznie większy niż czas odbierania.
  • lastReceive: Czas ostatniego żądania odebranego z tego serwera
  • lastSendAttempt: Godzina ostatniej próby wysłania

Rozmiary żądań i odpowiedzi

  • requestSizeInBytes: całkowity rozmiar żądania wysłanego do usługi Azure Cosmos DB
  • responseMetadataSizeInBytes: rozmiar nagłówków zwracanych z usługi Azure Cosmos DB
  • responseBodySizeInBytes: rozmiar zawartości zwracanej z usługi Azure Cosmos DB
"StoreResult": {
    "ActivityId": "bab6ade1-b8de-407f-b89d-fa2138a91284",
    "StatusCode": "Ok",
    "SubStatusCode": "Unknown",
    "LSN": 453362,
    "PartitionKeyRangeId": "1",
    "GlobalCommittedLSN": 0,
    "ItemLSN": 453358,
    "UsingLocalLSN": true,
    "QuorumAckedLSN": -1,
    "SessionToken": "-1#453362",
    "CurrentWriteQuorum": -1,
    "CurrentReplicaSetSize": -1,
    "NumberOfReadRegions": 0,
    "IsValid": true,
    "StorePhysicalAddress": "rntbd://127.0.0.1:10253/apps/DocDbApp/services/DocDbServer92/partitions/a4cb49a8-38c8-11e6-8106-8cdcd42c33be/replicas/1s/",
    "RequestCharge": 1,
    "RetryAfterInMs": null,
    "BELatencyInMs": "0.304",
    "transportRequestTimeline": {
        "requestTimeline": [
            {
                "event": "Created",
                "startTimeUtc": "2022-05-25T12:03:36.3081190Z",
                "durationInMs": 0.0024
            },
            {
                "event": "ChannelAcquisitionStarted",
                "startTimeUtc": "2022-05-25T12:03:36.3081214Z",
                "durationInMs": 0.0132
            },
            {
                "event": "Pipelined",
                "startTimeUtc": "2022-05-25T12:03:36.3081346Z",
                "durationInMs": 0.0865
            },
            {
                "event": "Transit Time",
                "startTimeUtc": "2022-05-25T12:03:36.3082211Z",
                "durationInMs": 1.3324
            },
            {
                "event": "Received",
                "startTimeUtc": "2022-05-25T12:03:36.3095535Z",
                "durationInMs": 12.6128
            },
            {
                "event": "Completed",
                "startTimeUtc": "2022-05-25T12:03:36.8621663Z",
                "durationInMs": 0
            }
        ],
        "serviceEndpointStats": {
            "inflightRequests": 1,
            "openConnections": 1
        },
        "connectionStats": {
            "waitforConnectionInit": "False",
            "callsPendingReceive": 0,
            "lastSendAttempt": "2022-05-25T12:03:34.0222760Z",
            "lastSend": "2022-05-25T12:03:34.0223280Z",
            "lastReceive": "2022-05-25T12:03:34.0257728Z"
        },
        "requestSizeInBytes": 447,
        "responseMetadataSizeInBytes": 438,
        "responseBodySizeInBytes": 604
    },
    "TransportException": null
}

Współczynnik niepowodzeń narusza umowę SLA usługi Azure Cosmos DB

Skontaktuj się z pomoc techniczna platformy Azure.

Następne kroki