Jak zwiększyć szybkość tego zapytania?
Mamy około 100 klientów w ciągu 1-2 minutes
wykonywania następującego zapytania. Każdy z tych przebiegów reprezentuje 1 przebieg funkcji zużycia.
TableQuery<T> treanslationsQuery = new TableQuery<T>()
.Where(
TableQuery.CombineFilters(
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, sourceDestinationPartitionKey)
, TableOperators.Or,
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, anySourceDestinationPartitionKey)
)
);
To zapytanie da około 5000 wyników.
Pełny kod:
public static async Task<IEnumerable<T>> ExecuteQueryAsync<T>(this CloudTable table, TableQuery<T> query) where T : ITableEntity, new()
{
var items = new List<T>();
TableContinuationToken token = null;
do
{
TableQuerySegment<T> seg = await table.ExecuteQuerySegmentedAsync(query, token);
token = seg.ContinuationToken;
items.AddRange(seg);
} while (token != null);
return items;
}
public static IEnumerable<Translation> Get<T>(string sourceParty, string destinationParty, string wildcardSourceParty, string tableName) where T : ITableEntity, new()
{
var acc = CloudStorageAccount.Parse(Environment.GetEnvironmentVariable("conn"));
var tableClient = acc.CreateCloudTableClient();
var table = tableClient.GetTableReference(Environment.GetEnvironmentVariable("TableCache"));
var sourceDestinationPartitionKey = $"{sourceParty.ToLowerTrim()}-{destinationParty.ToLowerTrim()}";
var anySourceDestinationPartitionKey = $"{wildcardSourceParty}-{destinationParty.ToLowerTrim()}";
TableQuery<T> treanslationsQuery = new TableQuery<T>()
.Where(
TableQuery.CombineFilters(
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, sourceDestinationPartitionKey)
, TableOperators.Or,
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, anySourceDestinationPartitionKey)
)
);
var over1000Results = table.ExecuteQueryAsync(treanslationsQuery).Result.Cast<Translation>();
return over1000Results.Where(x => x.expireAt > DateTime.Now)
.Where(x => x.effectiveAt < DateTime.Now);
}
Podczas tych egzekucji, gdy jest 100 klientów, widać, że żądania będą się grupować i tworzyć skoki:
Podczas tych skoków żądania często zajmują 1 minutę:
Jak zwiększyć szybkość tego zapytania?
c#
azure
azure-table-storage
azure-virtual-network
azure-tablequery
l - '' '' ---------- '' '' '' '
źródło
źródło
Odpowiedzi:
Oto jeden z problemów: uruchamiasz zapytanie, a następnie filtrujesz je z pamięci za pomocą tych „wheres”. Przenieś filtry na przed uruchomieniem zapytania, co powinno bardzo pomóc.
Po drugie, musisz podać limit wierszy do pobrania z bazy danych
źródło
Można rozważyć 3 rzeczy:
1 . Przede wszystkim pozbądź się
Where
klauzul, które wykonujesz na wyniku zapytania. Lepiej włączać klauzule do zapytania tak często, jak to możliwe (nawet lepiej, jeśli masz jakieś indeksy w swoich tabelach, również je uwzględniają). Na razie możesz zmienić zapytanie w następujący sposób:Ponieważ masz do pobrania dużą ilość danych, lepiej równolegle uruchamiać zapytania. Powinieneś więc zamienić metodę
do while
pętli wewnątrz na napisaną przeze mnie na podstawie Stephen Toub Parallel.While ; W ten sposób skróci czas wykonywania zapytania. Jest to dobry wybór, ponieważ możesz usunąć, kiedy wykonujesz wywołanie za pomocą tej metody, ale ma małe ograniczenie, że powiem o tym po tej części kodu:ExecuteQueryAsync
Parallel.ForEach
Result
Następnie możesz wywołać to w swojej
Get
metodzie:Jak widać, sama metoda nie jest asynchroniczna (należy zmienić jej nazwę) i
Parallel.ForEach
nie jest kompatybilna z przekazywaniem metody asynchronicznej. Właśnie dlatego użyłemExecuteQuerySegmented
zamiast tego. Aby jednak zwiększyć wydajność i wykorzystać wszystkie zalety metody asynchronicznej, można zastąpić powyższąForEach
pętlęActionBlock
metodą w przepływie danych lubParallelForEachAsync
metodą rozszerzenia z pakietu AsyncEnumerator Nuget .2. Dobrym wyborem jest wykonywanie niezależnych równoległych zapytań, a następnie łączenie wyników, nawet jeśli jego poprawa wydajności wynosi co najwyżej 10 procent. To daje czas na znalezienie najlepszego zapytania przyjaznego wydajności. Ale nigdy nie zapominaj o uwzględnieniu wszystkich swoich ograniczeń i przetestuj oba sposoby, aby dowiedzieć się, który z nich najlepiej pasuje do Twojego problemu.
3 . Nie jestem pewien, czy to dobra sugestia, czy nie, ale zrób to i zobacz wyniki. Jak opisano w MSDN :
Możesz więc grać z limitem czasu i sprawdzać, czy nastąpiła poprawa wydajności.
źródło
Niestety poniższe zapytanie wprowadza pełny skan tabeli :
Powinieneś podzielić go na dwa filtry klucza partycji i przesłać je osobno, które staną się dwoma skanami partycji i będą działać wydajniej.
źródło
Sekret tkwi więc nie tylko w kodzie, ale także podczas konfigurowania tabel magazynu Azure.
a) Jedną z najważniejszych opcji optymalizacji zapytań na platformie Azure jest wprowadzenie buforowania. To drastycznie skróci ogólny czas reakcji, a tym samym pozwoli uniknąć wąskiego gardła podczas wspomnianej godziny szczytu.
b) Ponadto podczas wysyłania zapytań do jednostek poza platformą Azure najszybszym możliwym sposobem jest użycie zarówno PartitionKey, jak i RowKey. Są to jedyne pola indeksowane w Table Storage, a każde zapytanie wykorzystujące oba z nich zostanie zwrócone w ciągu kilku milisekund. Upewnij się więc, że używasz zarówno PartitionKey, jak i RowKey.
Zobacz więcej szczegółów tutaj: https://docs.microsoft.com/en-us/azure/storage/tables/table-storage-design-for-query
Mam nadzieję że to pomoże.
źródło
Uwaga: jest to ogólna porada dotycząca optymalizacji zapytań DB.
Możliwe, że ORM robi coś głupiego. Podczas optymalizacji można zejść z warstwy abstrakcji. Proponuję więc przepisać zapytanie w języku zapytań (SQL?), Aby łatwiej było zobaczyć, co się dzieje, a także zoptymalizować.
Kluczem do optymalizacji wyszukiwania jest sortowanie! Utrzymywanie sortowania tabeli jest zwykle znacznie tańsze w porównaniu do skanowania całej tabeli przy każdym zapytaniu! Jeśli to możliwe, tabelę należy posortować według klucza użytego w zapytaniu. W większości rozwiązań bazodanowych osiąga się to poprzez utworzenie klucza indeksu.
Inną strategią, która działa dobrze, jeśli jest kilka kombinacji, jest umieszczenie każdego zapytania w osobnej (tymczasowej w pamięci) tabeli, która jest zawsze aktualna. Kiedy coś jest wstawiane, jest ono również „wstawiane” do tabel „widok”. Niektóre rozwiązania baz danych nazywają to „widokami”.
Bardziej brutalną strategią jest tworzenie replik tylko do odczytu w celu rozłożenia obciążenia.
źródło