Wie erhöhen wir die Geschwindigkeit dieser Abfrage?
Wir haben ungefähr 100 Verbraucher innerhalb der Zeitspanne, in der 1-2 minutes
die folgende Abfrage ausgeführt wird. Jeder dieser Läufe repräsentiert einen Lauf einer Verbrauchsfunktion.
TableQuery<T> treanslationsQuery = new TableQuery<T>()
.Where(
TableQuery.CombineFilters(
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, sourceDestinationPartitionKey)
, TableOperators.Or,
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, anySourceDestinationPartitionKey)
)
);
Diese Abfrage liefert ungefähr 5000 Ergebnisse.
Vollständiger Code:
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);
}
Während dieser Ausführungen werden bei 100 Verbrauchern, wie Sie sehen können, die Anforderungen gruppiert und bilden Spitzen:
Während dieser Spitzen dauern die Anfragen oft länger als 1 Minute:
Wie erhöhen wir die Geschwindigkeit dieser Abfrage?
c#
azure
azure-table-storage
azure-virtual-network
azure-tablequery
l - '' '' '-' '' '' '' '' '' '' '
quelle
quelle
Antworten:
Hier ist eines der Probleme: Sie führen die Abfrage aus und filtern sie dann mit diesen "Wheres" aus dem Speicher. Verschieben Sie die Filter auf, bevor die Abfrage ausgeführt wird, was sehr hilfreich sein sollte.
Zweitens müssen Sie eine bestimmte Anzahl von Zeilen angeben, die aus der Datenbank abgerufen werden sollen
quelle
Es gibt 3 Dinge, die Sie berücksichtigen können:
1 .
Where
Entfernen Sie zunächst Ihre Klauseln, die Sie für das Abfrageergebnis ausführen. Es ist besser, Klauseln so weit wie möglich in die Abfrage aufzunehmen (noch besser, wenn Sie Indizes in Ihren Tabellen haben, die diese ebenfalls enthalten). Im Moment können Sie Ihre Abfrage wie folgt ändern:Da Sie eine große Datenmenge abrufen müssen, ist es besser, Ihre Abfragen parallel auszuführen. Sie sollten also die
do while
Loop-Inside-ExecuteQueryAsync
Methode durch eine Methode ersetzen,Parallel.ForEach
die auf Stephen Toub Parallel.While basiert . Auf diese Weise wird die Ausführungszeit für Abfragen verkürzt. Dies ist eine gute Wahl, da Sie diese entfernen können,Result
wenn Sie diese Methode aufrufen. Es gibt jedoch eine kleine Einschränkung, dass ich nach diesem Teil des Codes darüber sprechen werde:Und dann können Sie es in Ihrer
Get
Methode aufrufen :Wie Sie sehen können, ist die Methode itselft nicht asynchron (Sie sollten ihren Namen ändern) und
Parallel.ForEach
nicht mit der Übergabe einer asynchronen Methode kompatibel. Deshalb habe ichExecuteQuerySegmented
stattdessen verwendet. Um die Leistung zu steigern und alle Vorteile der asynchronen Methode zu nutzen, können Sie die obigeForEach
Schleife durch dieActionBlock
Methode im Datenfluss oder dieParallelForEachAsync
Erweiterungsmethode aus dem AsyncEnumerator Nuget-Paket ersetzen .2. Es ist eine gute Wahl, unabhängige parallele Abfragen auszuführen und die Ergebnisse dann zusammenzuführen, selbst wenn die Leistungsverbesserung höchstens 10 Prozent beträgt. Dies gibt Ihnen Zeit, um die leistungsstärkste Abfrage zu finden. Aber nie vergessen , alle Ihre Einschränkungen darin aufzunehmen und testen beiden Richtungen zu wissen , was man besser Suiten Ihr Problem.
3 . Ich bin mir nicht sicher, ob es ein guter Vorschlag ist oder nicht, aber mach es und sieh dir die Ergebnisse an. Wie in MSDN beschrieben :
Sie können also mit Timeout spielen und prüfen, ob es Leistungsverbesserungen gibt.
quelle
Leider führt die folgende Abfrage einen vollständigen Tabellenscan ein :
Sie sollten es in zwei Partitionsschlüsselfilter aufteilen und separat abfragen, wodurch zwei Partitionsscans erstellt werden und eine effizientere Leistung erzielt wird.
quelle
Das Geheimnis liegt also nicht nur im Code, sondern auch beim Einrichten Ihrer Azure-Speichertabellen.
a) Eine der wichtigsten Optionen zur Optimierung Ihrer Abfragen in Azure ist die Einführung des Caching. Dies reduziert Ihre Gesamtreaktionszeiten drastisch und vermeidet so Engpässe während der von Ihnen genannten Spitzenzeit.
b) Wenn Sie Entitäten aus Azure abfragen, können Sie dies am schnellsten sowohl mit dem PartitionKey als auch mit dem RowKey tun. Dies sind die einzigen indizierten Felder im Tabellenspeicher, und jede Abfrage, die beide verwendet, wird innerhalb weniger Millisekunden zurückgegeben. Stellen Sie also sicher, dass Sie sowohl PartitionKey als auch RowKey verwenden.
Weitere Details finden Sie hier: https://docs.microsoft.com/en-us/azure/storage/tables/table-storage-design-for-query
Hoffe das hilft.
quelle
Hinweis: Dies ist eine allgemeine Empfehlung zur Optimierung von DB-Abfragen.
Es ist möglich, dass der ORM etwas Dummes tut. Bei Optimierungen ist es in Ordnung, eine Abstraktionsschicht herunterzufahren. Ich schlage daher vor, die Abfrage in der Abfragesprache (SQL?) Umzuschreiben, damit Sie leichter sehen können, was gerade passiert, und auch einfacher optimieren können.
Der Schlüssel zur Optimierung der Suche ist das Sortieren! Das Sortieren einer Tabelle ist normalerweise viel billiger als das Scannen der gesamten Tabelle bei jeder Abfrage! Wenn möglich, sortieren Sie die Tabelle nach dem in der Abfrage verwendeten Schlüssel. In den meisten Datenbanklösungen wird dies durch Erstellen eines Indexschlüssels erreicht.
Eine andere Strategie, die bei wenigen Kombinationen gut funktioniert, besteht darin, jede Abfrage als separate (temporär im Speicher befindliche) Tabelle zu haben, die immer auf dem neuesten Stand ist. Wenn also etwas eingefügt wird, wird es auch in die "Ansicht" -Tabellen "eingefügt". Einige Datenbanklösungen nennen dies "Ansichten".
Eine brutalere Strategie besteht darin, schreibgeschützte Replikate zu erstellen, um die Last zu verteilen.
quelle