Porady dotyczące wydajności usługi Azure Cosmos DB i platformy .NET
DOTYCZY: NoSQL
Usługa Azure Cosmos DB to szybka, elastyczna rozproszona baza danych, która bezproblemowo skaluje się z gwarantowanymi opóźnieniami i poziomami przepływności. Nie musisz wprowadzać istotnych zmian architektury ani pisać złożonego kodu w celu skalowania bazy danych za pomocą usługi Azure Cosmos DB. Skalowanie w górę i w dół jest tak proste, jak tworzenie pojedynczego wywołania interfejsu API. Aby dowiedzieć się więcej, zobacz Aprowizowanie przepływności kontenera lub aprowizowanie przepływności bazy danych.
Ponieważ usługa Azure Cosmos DB jest uzyskiwana za pośrednictwem wywołań sieciowych, można dokonać optymalizacji po stronie klienta, aby osiągnąć szczytową wydajność podczas korzystania z zestawu SQL .NET SDK.
Jeśli próbujesz poprawić wydajność bazy danych, rozważ opcje przedstawione w poniższych sekcjach.
Zalecenia dotyczące hostingu
Włączanie odzyskiwania pamięci po stronie serwera
Zmniejszenie częstotliwości odzyskiwania pamięci może pomóc w niektórych przypadkach. Na platformie .NET ustaw wartość gcServer na true
.
Skalowanie obciążenia klienta w poziomie
Jeśli testujesz na wysokim poziomie przepływności lub przy szybkościach przekraczających 50 000 jednostek żądań na sekundę (RU/s), aplikacja kliencka może stać się wąskim gardłem obciążenia. Jest to spowodowane tym, że maszyna może ograniczyć wykorzystanie procesora CPU lub sieci. Jeśli osiągniesz ten punkt, możesz kontynuować wypychanie konta usługi Azure Cosmos DB, skalując aplikacje klienckie na wielu serwerach.
Uwaga
Wysokie użycie procesora CPU może spowodować zwiększone opóźnienie i wyjątki limitu czasu żądania.
Operacje na metadanych
Nie należy weryfikować, czy baza danych i/lub kontener istnieje, wywołując Create...IfNotExistsAsync
i/lub Read...Async
w ścieżce gorącej i/lub przed wykonaniem operacji elementu. Walidację należy wykonać tylko podczas uruchamiania aplikacji, jeśli jest to konieczne, jeśli spodziewasz się ich usunięcia (w przeciwnym razie nie jest to konieczne). Te operacje metadanych będą generować dodatkowe kompleksowe opóźnienia, nie mają umowy SLA i własnych oddzielnych ograniczeń, które nie są skalowane, takie jak operacje na danych.
Rejestrowanie i śledzenie
Niektóre środowiska mają włączony element DefaultTraceListener platformy .NET. Element DefaultTraceListener stwarza problemy z wydajnością w środowiskach produkcyjnych powodujących wąskie gardła procesora CPU i we/wy. Sprawdź i upewnij się, że element DefaultTraceListener jest wyłączony dla aplikacji, usuwając go ze środowiska produkcyjnego TraceListeners .
Najnowsze wersje zestawu SDK (nowsze niż 3.23.0) automatycznie usuwają je po wykryciu, ze starszymi wersjami, można je usunąć, wykonując następujące czynności:
if (!Debugger.IsAttached)
{
Type defaultTrace = Type.GetType("Microsoft.Azure.Cosmos.Core.Trace.DefaultTrace,Microsoft.Azure.Cosmos.Direct");
TraceSource traceSource = (TraceSource)defaultTrace.GetProperty("TraceSource").GetValue(null);
traceSource.Listeners.Remove("Default");
// Add your own trace listeners
}
Sieć
Zasady połączeń: użyj trybu połączenia bezpośredniego
Domyślny tryb połączenia zestawu SDK platformy .NET w wersji 3 jest bezpośredni z protokołem TCP. Tryb połączenia można skonfigurować podczas tworzenia CosmosClient
wystąpienia w programie CosmosClientOptions
. Aby dowiedzieć się więcej o różnych opcjach łączności, zobacz artykuł Tryby łączności.
CosmosClient client = new CosmosClient(
"<nosql-account-endpoint>",
tokenCredential
new CosmosClientOptions
{
ConnectionMode = ConnectionMode.Gateway // ConnectionMode.Direct is the default
}
);
Wyczerpanie portów efemerycznych
Jeśli w wystąpieniach zostanie wyświetlony duży wolumin połączenia lub wysokie użycie portów, najpierw sprawdź, czy wystąpienia klientów są pojedynczymi wystąpieniami. Innymi słowy, wystąpienia klienta powinny być unikatowe przez cały okres istnienia aplikacji.
Gdy jest uruchomiony w protokole TCP, klient optymalizuje opóźnienia przy użyciu długotrwałych połączeń. Jest to w przeciwieństwie do protokołu HTTPS, który przerywa połączenia po dwóch minutach braku aktywności.
W scenariuszach, w których masz dostęp rozrzedny, a jeśli zauważysz większą liczbę połączeń w porównaniu z dostępem w trybie bramy, możesz:
- Skonfiguruj właściwość CosmosClientOptions.PortReuseMode na
PrivatePortPool
(obowiązującą w wersjach 4.6.1 i nowszych oraz .NET Core w wersji 2.0 lub nowszej). Ta właściwość umożliwia zestawowi SDK używanie małej puli portów efemerycznych dla różnych docelowych punktów końcowych usługi Azure Cosmos DB. - Skonfiguruj właściwość CosmosClientOptions.IdleTcpConnectionTimeout jako większą lub równą 10 minut. Zalecane wartości to od 20 minut do 24 godzin.
W celu uzyskania wydajności posuń klientów w tym samym regionie świadczenia usługi Azure
Jeśli to możliwe, umieść wszystkie aplikacje wywołujące usługę Azure Cosmos DB w tym samym regionie co baza danych usługi Azure Cosmos DB. Oto przybliżone porównanie: wywołania usługi Azure Cosmos DB w tym samym regionie kończą się w ciągu 1 milisekund (ms) do 2 ms, ale opóźnienie między zachodnim i wschodnim wybrzeżem USA wynosi ponad 50 ms. To opóźnienie może się różnić od żądania, w zależności od trasy podjętej przez żądanie, gdy przechodzi od klienta do granicy centrum danych platformy Azure.
Możesz uzyskać najmniejsze możliwe opóźnienie, upewniając się, że aplikacja wywołująca znajduje się w tym samym regionie świadczenia usługi Azure, co aprowizowany punkt końcowy usługi Azure Cosmos DB. Aby uzyskać listę dostępnych regionów, zobacz Regiony świadczenia usługi Azure.
Zwiększanie liczby wątków/zadań
Ponieważ wywołania usługi Azure Cosmos DB są wykonywane za pośrednictwem sieci, może być konieczne zmianę stopnia współbieżności żądań, aby aplikacja kliencka spędzała minimalny czas oczekiwania między żądaniami. Jeśli na przykład używasz biblioteki równoległej zadań platformy .NET, utwórz je w kolejności setek zadań odczytywanych z usługi Azure Cosmos DB lub zapisu.
Włączanie przyspieszonej sieci w celu zmniejszenia opóźnienia i zakłócenia procesora CPU
Zaleca się wykonanie instrukcji dotyczących włączania przyspieszonej sieci w systemie Windows (kliknij, aby uzyskać instrukcje) lub Linux (kliknij, aby uzyskać instrukcje) maszyny wirtualnej platformy Azure, aby zmaksymalizować wydajność.
Bez przyspieszonej sieci we/wy przesyłane między maszyną wirtualną platformy Azure i innymi zasobami platformy Azure mogą być niepotrzebnie kierowane przez hosta i przełącznik wirtualny znajdujący się między maszyną wirtualną a jej kartą sieciową. Posiadanie wbudowanego hosta i przełącznika wirtualnego w ścieżce danych nie tylko zwiększa opóźnienie i zakłócenia w kanale komunikacyjnym, ale także kradnie cykle procesora CPU z maszyny wirtualnej. W przypadku przyspieszonej sieci interfejsy maszyn wirtualnych bezpośrednio z kartą sieciową bez pośredników; wszystkie szczegóły zasad sieci, które były obsługiwane przez hosta i przełącznik wirtualny, są teraz obsługiwane w sprzęcie na karcie sieciowej; hosta i przełącznika wirtualnego są pomijane. Ogólnie rzecz biorąc, można oczekiwać mniejszego opóźnienia i większej przepływności, a także bardziej spójnego opóźnienia i mniejszego wykorzystania procesora CPU po włączeniu przyspieszonej sieci.
Ograniczenia: przyspieszona sieć musi być obsługiwana w systemie operacyjnym maszyny wirtualnej i może być włączona tylko wtedy, gdy maszyna wirtualna zostanie zatrzymana i cofnięto przydział. Nie można wdrożyć maszyny wirtualnej za pomocą usługi Azure Resource Manager. Usługa App Service nie ma włączonej przyspieszonej sieci.
Aby uzyskać więcej informacji, zobacz instrukcje dotyczące systemów Windows i Linux .
SDK usage (Użycie zestawu SDK)
Instalowanie najnowszego zestawu SDK
Zestawy SDK usługi Azure Cosmos DB są stale ulepszane, aby zapewnić najlepszą wydajność. Aby określić najnowszy zestaw SDK i zapoznać się z ulepszeniami, zobacz Zestaw SDK usługi Azure Cosmos DB.
Korzystanie z interfejsów API strumienia
Zestaw .NET SDK w wersji 3 zawiera interfejsy API strumienia, które mogą odbierać i zwracać dane bez serializacji.
Aplikacje warstwy środkowej, które nie korzystają z odpowiedzi bezpośrednio z zestawu SDK, ale przekazują je do innych warstw aplikacji, mogą korzystać z interfejsów API strumienia. Przykłady obsługi strumieni można znaleźć w przykładach zarządzania elementami .
Używanie pojedynczego klienta usługi Azure Cosmos DB przez cały okres istnienia aplikacji
Każde CosmosClient
wystąpienie jest bezpieczne wątkowo i wykonuje wydajne zarządzanie połączeniami i buforowanie adresów, gdy działa w trybie bezpośrednim. Aby umożliwić wydajne zarządzanie połączeniami i lepszą wydajność klienta zestawu SDK, zalecamy użycie pojedynczego wystąpienia AppDomain
na okres istnienia aplikacji dla każdego konta, z którym aplikacja wchodzi w interakcję.
W przypadku aplikacji wielodostępnych obsługujących wiele kont zobacz powiązane najlepsze rozwiązania.
Podczas pracy z usługą Azure Functions wystąpienia powinny również postępować zgodnie z istniejącymi wytycznymi i obsługiwać pojedyncze wystąpienie.
Unikaj blokowania wywołań
Zestaw SDK usługi Azure Cosmos DB powinien być zaprojektowany tak, aby przetwarzał wiele żądań jednocześnie. Asynchroniczne interfejsy API umożliwiają małą pulę wątków do obsługi tysięcy współbieżnych żądań, nie czekając na wywołania blokujące. Zamiast czekać na wykonanie długotrwałego zadania synchronicznego, wątek może pracować nad innym żądaniem.
Typowym problemem z wydajnością aplikacji korzystających z zestawu SDK usługi Azure Cosmos DB jest blokowanie wywołań, które mogą być asynchroniczne. Wiele synchronicznych wywołań blokowania prowadzi do głodu puli wątków i obniżonych czasów odpowiedzi.
Nie:
- Blokuj wykonywanie asynchroniczne, wywołując metodę Task.Wait lub Task.Result.
- Użyj elementu Task.Run , aby utworzyć asynchroniczny interfejs API synchroniczny.
- Uzyskiwanie blokad w typowych ścieżkach kodu. Zestaw .NET SDK usługi Azure Cosmos DB jest najbardziej wydajny podczas tworzenia architektury do równoległego uruchamiania kodu.
- Wywołaj polecenie Task.Run i natychmiast go zaczekaj. ASP.NET Core uruchamia już kod aplikacji w normalnych wątkach puli wątków, więc wywołanie task.Run powoduje jedynie dodatkowe niepotrzebne planowanie puli wątków. Nawet jeśli zaplanowany kod zablokuje wątek, polecenie Task.Run nie zapobiega temu.
- Nie należy używać metody ToList(), w
Container.GetItemLinqQueryable<T>()
której używane są wywołania blokujące w celu synchronicznego opróżniania zapytania. Użyj funkcji ToFeedIterator(), aby opróżnić zapytanie asynchronicznie.
Część praktyczna:
- Asynchronicznie wywołaj interfejsy API platformy .NET usługi Azure Cosmos DB.
- Cały stos wywołań jest asynchroniczny, aby korzystać z wzorców asynchronicznych/await .
Profiler, taki jak PerfView, może służyć do znajdowania wątków często dodawanych do puli wątków. Zdarzenie Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThread/Start
wskazuje wątek dodany do puli wątków.
Wyłączanie odpowiedzi na zawartość w operacjach zapisu
W przypadku obciążeń, które mają duże ładunki tworzenia, ustaw EnableContentResponseOnWrite
opcję żądania na false
wartość . Usługa nie zwróci już utworzonego ani zaktualizowanego zasobu do zestawu SDK. Zwykle, ponieważ aplikacja ma tworzony obiekt, nie potrzebuje usługi, aby ją zwrócić. Wartości nagłówka są nadal dostępne, na przykład opłata za żądanie. Wyłączenie odpowiedzi na zawartość może pomóc zwiększyć wydajność, ponieważ zestaw SDK nie musi już przydzielać pamięci ani serializować treści odpowiedzi. Zmniejsza również użycie przepustowości sieci w celu zwiększenia wydajności.
ItemRequestOptions requestOptions = new ItemRequestOptions() { EnableContentResponseOnWrite = false };
ItemResponse<Book> itemResponse = await this.container.CreateItemAsync<Book>(book, new PartitionKey(book.pk), requestOptions);
// Resource will be null
itemResponse.Resource
Włącz zbiorcze optymalizowanie pod kątem przepływności zamiast opóźnienia
Włącz zbiorczo w scenariuszach, w których obciążenie wymaga dużej przepływności, a opóźnienie nie jest tak ważne. Aby uzyskać więcej informacji na temat włączania funkcji zbiorczej i dowiedzieć się, które scenariusze powinny być używane, zobacz Wprowadzenie do obsługi zbiorczej.
Zwiększ System.Net maxConnections na hosta podczas korzystania z trybu bramy
Żądania usługi Azure Cosmos DB są wysyłane za pośrednictwem protokołu HTTPS/REST w przypadku korzystania z trybu bramy. Podlegają one domyślnemu limitowi połączeń na nazwę hosta lub adres IP. Może być konieczne ustawienie MaxConnections
wartości wyższej (od 100 do 1000), aby biblioteka kliencka mogła używać wielu równoczesnych połączeń z usługą Azure Cosmos DB. W zestawie .NET SDK 1.8.0 lub nowszym wartość domyślna parametru ServicePointManager.DefaultConnectionLimit to 50. Aby zmienić wartość, możesz ustawić Documents.Client.ConnectionPolicy.MaxConnectionLimit
wartość wyższą.
Zwiększanie liczby wątków/zadań
Zobacz Zwiększanie liczby wątków/zadań w sekcji Sieć w tym artykule.
Operacje zapytań
Aby uzyskać informacje o operacjach zapytań, zobacz porady dotyczące wydajności zapytań.
Zasady indeksowania
Wyklucz nieużywane ścieżki z indeksowania w celu przyspieszenia operacji zapisu
Zasady indeksowania usługi Azure Cosmos DB umożliwiają również określenie ścieżek dokumentów do uwzględnienia lub wykluczenia z indeksowania przy użyciu ścieżek indeksowania (IndexingPolicy.IncludedPaths i IndexingPolicy.ExcludedPaths).
Indeksowanie tylko potrzebnych ścieżek może poprawić wydajność zapisu, zmniejszyć opłaty za jednostki RU w operacjach zapisu i zmniejszyć magazyn indeksów w scenariuszach, w których wzorce zapytań są znane wcześniej. Wynika to z faktu, że koszty indeksowania są skorelowane bezpośrednio z liczbą indeksowanych ścieżek unikatowych. Na przykład poniższy kod pokazuje, jak wykluczyć całą sekcję dokumentów (poddrzewo) z indeksowania przy użyciu symbolu wieloznakowego "*":
var containerProperties = new ContainerProperties(id: "excludedPathCollection", partitionKeyPath: "/pk" );
containerProperties.IndexingPolicy.IncludedPaths.Add(new IncludedPath { Path = "/*" });
containerProperties.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/nonIndexedContent/*");
Container container = await this.cosmosDatabase.CreateContainerAsync(containerProperties);
Aby uzyskać więcej informacji, zobacz Zasady indeksowania usługi Azure Cosmos DB.
Produktywność
Mierzenie i dostrajanie pod kątem mniejszego użycia jednostek RU/s
Usługa Azure Cosmos DB oferuje bogaty zestaw operacji bazy danych. Te operacje obejmują zapytania relacyjne i hierarchiczne z plikami formatu uniwersalnego dysku (UDF), procedurami składowanymi i wyzwalaczami, wszystkie operacje na dokumentach w kolekcji bazy danych.
Koszty związane z każdą z tych operacji różnią się w zależności od procesora CPU, operacji we/wy i pamięci, które są wymagane do ukończenia operacji. Zamiast myśleć o zasobach sprzętowych i zarządzaniu nimi, możesz traktować jednostkę żądania jako pojedynczą miarę dla zasobów wymaganych do wykonywania różnych operacji bazy danych i obsługi żądania aplikacji.
Przepływność jest aprowizowana na podstawie liczby jednostek żądań ustawionych dla każdego kontenera. Użycie jednostek żądania jest oceniane jako liczba jednostek na sekundę. Aplikacje, które przekraczają aprowizowaną stawkę jednostek żądań dla kontenera, są ograniczone do momentu spadku szybkości poniżej aprowizowanego poziomu dla kontenera. Jeśli aplikacja wymaga wyższego poziomu przepływności, możesz zwiększyć przepływność, aprowizując dodatkowe jednostki żądań.
Złożoność zapytania wpływa na liczbę jednostek żądań używanych dla operacji. Liczba predykatów, charakter predykatów, liczba plików UDF i rozmiar źródłowego zestawu danych wpływają na koszt operacji zapytań.
Aby zmierzyć obciążenie dowolnej operacji (tworzenie, aktualizowanie lub usuwanie), sprawdź nagłówek x-ms-request-charge (lub równoważną RequestCharge
właściwość w ResourceResponse<T>
zestawie FeedResponse<T>
SDK platformy .NET), aby zmierzyć liczbę jednostek żądań używanych przez operacje:
// Measure the performance (Request Units) of writes
ItemResponse<Book> response = await container.CreateItemAsync<Book>(myBook, new PartitionKey(myBook.PkValue));
Console.WriteLine("Insert of item consumed {0} request units", response.RequestCharge);
// Measure the performance (Request Units) of queries
FeedIterator<Book> queryable = container.GetItemQueryIterator<ToDoActivity>(queryString);
while (queryable.HasMoreResults)
{
FeedResponse<Book> queryResponse = await queryable.ExecuteNextAsync<Book>();
Console.WriteLine("Query batch consumed {0} request units", queryResponse.RequestCharge);
}
Opłata za żądanie zwrócona w tym nagłówku to ułamek aprowizowanej przepływności (czyli 2000 RU/s). Jeśli na przykład poprzednie zapytanie zwróci 1000 dokumentów o wartości 1000 KB, koszt operacji wynosi 1000. Dlatego w ciągu jednej sekundy serwer honoruje tylko dwa takie żądania, zanim ograniczy liczbę żądań później. Aby uzyskać więcej informacji, zobacz Jednostki żądań i kalkulator jednostki żądania.
Obsługa ograniczania szybkości/szybkości żądań jest zbyt duża
Gdy klient próbuje przekroczyć zarezerwowaną przepływność dla konta, nie ma spadku wydajności na serwerze i nie ma użycia pojemności przepływności poza poziomem zarezerwowanym. Serwer z preemptively kończy żądanie RequestRateTooLarge (kod stanu HTTP 429). Zwraca nagłówek x-ms-retry-after-ms , który wskazuje czas (w milisekundach), który użytkownik musi czekać przed ponownym podjęciem próby wykonania żądania.
HTTP Status 429,
Status Line: RequestRateTooLarge
x-ms-retry-after-ms :100
Zestawy SDK przechwytują tę odpowiedź niejawnie, przestrzegają określonego przez serwer nagłówka ponawiania próby i ponów próbę żądania. Jeśli twoje konto nie jest używane współbieżnie przez wielu klientów, następne ponowienie próby powiedzie się.
Jeśli masz więcej niż jednego klienta skumulowanie operacyjnego powyżej szybkości żądań, domyślna liczba ponownych prób, która jest obecnie ustawiona na 9 wewnętrznie przez klienta, może nie wystarczyć. W takim przypadku klient zgłasza wyjątek CosmosException z kodem stanu 429 do aplikacji.
Domyślną liczbę ponownych prób można zmienić, ustawiając RetryOptions
wartość w wystąpieniu CosmosClientOptions
. Domyślnie wyjątek CosmosException z kodem stanu 429 jest zwracany po skumulowanym czasie oczekiwania wynoszącym 30 sekund, jeśli żądanie nadal działa powyżej szybkości żądania. Ten błąd jest zwracany nawet wtedy, gdy bieżąca liczba ponownych prób jest mniejsza niż maksymalna liczba ponownych prób, niezależnie od tego, czy bieżąca wartość jest wartością domyślną 9, czy wartością zdefiniowaną przez użytkownika.
Automatyczne zachowanie ponawiania prób pomaga zwiększyć odporność i użyteczność większości aplikacji. Jednak może to nie być najlepsze zachowanie podczas wykonywania testów porównawczych wydajności, zwłaszcza podczas mierzenia opóźnienia. Obserwowane przez klienta opóźnienie wzrośnie, jeśli eksperyment osiągnie ograniczenie przepustowości serwera i spowoduje, że zestaw SDK klienta ponawia próbę w trybie dyskretnym. Aby uniknąć skoków opóźnień podczas eksperymentów wydajności, zmierz opłatę zwracaną przez każdą operację i upewnij się, że żądania działają poniżej zarezerwowanej stawki żądań.
Aby uzyskać więcej informacji, zobacz Request Units (Jednostki żądań).
W przypadku większej przepływności projektowanie mniejszych dokumentów
Opłata za żądanie (czyli koszt przetwarzania żądań) określonej operacji jest skorelowana bezpośrednio z rozmiarem dokumentu. Operacje na dużych dokumentach kosztują więcej niż operacje na małych dokumentach.
Następne kroki
Aby zapoznać się z przykładową aplikacją używaną do oceny usługi Azure Cosmos DB pod kątem scenariuszy o wysokiej wydajności na kilku maszynach klienckich, zobacz Performance and scale testing with Azure Cosmos DB (Wydajność i skalowanie przy użyciu usługi Azure Cosmos DB).
Aby dowiedzieć się więcej na temat projektowania aplikacji pod kątem skalowania i wysokiej wydajności, zobacz Partycjonowanie i skalowanie w usłudze Azure Cosmos DB.