Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Azure AI Search unterstützt zwei grundlegende Methoden zum Importieren von Daten in einen Suchindex: Programmgesteuertes Übertragen ihrer Daten in den Index oder Ziehen Ihrer Daten durch Zeigen eines Indexers auf eine unterstützte Datenquelle.
In diesem Tutorial erfahren Sie, wie Sie Daten effizient mithilfe des Pushmodells indizieren, indem Sie Anforderungen zu einem Batch zusammenfassen und eine Wiederholungsstrategie mit exponentiellem Backoff verwenden. Sie können die Beispielanwendung herunterladen und ausführen. In diesem Lernprogramm werden auch die wichtigsten Aspekte der Anwendung und die Faktoren erläutert, die beim Indizieren von Daten berücksichtigt werden müssen.
In diesem Lernprogramm verwenden Sie C# und die Azure.Search.Documents-Bibliothek aus dem Azure SDK für .NET zu:
- Erstellen eines Index
- Testen verschiedener Batchgrößen, um die effizienteste Größe zu ermitteln
- Asynchrones Indizieren von Batches
- Erhöhen der Indizierungsgeschwindigkeit durch Verwendung mehrerer Threads
- Verwenden einer Wiederholungsstrategie mit exponentiellem Backoff, um die Indizierung für nicht erfolgreiche Dokumente zu wiederholen
Voraussetzungen
- Ein Azure-Konto mit einem aktiven Abonnement. Sie können kostenlos ein Konto erstellen.
- Visual Studio.
Herunterladen von Dateien
Der Quellcode für dieses Tutorial befindet sich im Ordner optimize-data-indexing/v11 im GitHub-Repository Azure-Samples/azure-search-dotnet-scale.
Wichtige Aspekte
Die folgenden Faktoren wirken sich auf die Indizierungsgeschwindigkeit aus. Weitere Informationen finden Sie unter Index large data sets.
- Preisstufe und Anzahl von Partitionen/Replikaten: Das Hinzufügen von Partitionen oder das Upgrade Ihrer Stufe erhöht die Indizierungsgeschwindigkeit.
- Komplexität des Indexschemas: Das Hinzufügen von Feldern und Feldeigenschaften verringert die Indizierungsgeschwindigkeit. Kleinere Indizes werden schneller indiziert.
- Batchgröße: Die optimale Batchgröße hängt von Ihrem Indexschema und Dataset ab.
- Anzahl von Threads/Workern: Ein einzelner Thread nutzt nicht die volle Indizierungsgeschwindigkeit.
- Wiederholungsstrategie: Eine Wiederholungsstrategie mit exponentielle Backoff ist eine bewährte Methode für eine optimale Indizierung.
- Datenübertragungsgeschwindigkeit im Netzwerk: Die Datenübertragungsgeschwindigkeit kann ein limitierender Faktor sein. Indizieren Sie Daten innerhalb Ihrer Azure-Umgebung, um die Datenübertragungsgeschwindigkeit zu erhöhen.
Erstellen eines Suchdiensts
Dieses Lernprogramm erfordert einen Azure AI Search-Dienst, den Sie im Azure-Portal erstellen können. Sie können auch einen vorhandenen Dienst in Ihrem aktuellen Abonnement finden. Um die Indizierungsgeschwindigkeiten genau zu testen und zu optimieren, empfehlen wir, das gleiche Preisniveau zu verwenden, das Sie in der Produktion verwenden möchten.
Abrufen eines Administratorschlüssels und einer URL für Azure KI-Suche
In diesem Tutorial wird die schlüsselbasierte Authentifizierung verwendet. Kopieren Sie einen Administrator-API-Schlüssel, um ihn in die appsettings.json Datei einzufügen.
Melden Sie sich beim Azure-Portal an, und wählen Sie Ihren Suchdienst aus.
Wählen Sie im linken Bereich die Option "Übersicht" aus, und kopieren Sie den Endpunkt. Er sollte in diesem Format vorliegen:
https://my-service.search.windows.netWählen Sie im linken Bereich "Einstellungen" die Option "Einstellungen>" aus, und kopieren Sie einen Administratorschlüssel für vollständige Rechte für den Dienst. Es gibt zwei austauschbare Administratorschlüssel, die zur Gewährleistung der Geschäftskontinuität bereitgestellt wurden, falls ein Schlüssel ausgetauscht werden muss. Sie können einen der Schlüssel für Anforderungen verwenden, um Objekte hinzuzufügen, zu ändern oder zu löschen.
Richten Sie Ihre Umgebung ein
Öffnen Sie die
OptimizeDataIndexing.sln-Datei in Visual Studio.Bearbeiten Sie im Projektmappen-Explorer die
appsettings.jsonDatei mit den Verbindungsinformationen, die Sie im vorherigen Schritt gesammelt haben.{ "SearchServiceUri": "https://{service-name}.search.windows.net", "SearchServiceAdminApiKey": "", "SearchIndexName": "optimize-indexing" }
Erkunden des Codes
Nach dem Aktualisieren von appsettings.jsonsollte das Beispielprogramm OptimizeDataIndexing.sln zum Erstellen und Ausführen bereit sein.
Dieser Code wird vom C#-Abschnitt der Schnellstartanleitung abgeleitet: Volltextsuche, die detaillierte Informationen zu den Grundlagen der Arbeit mit dem .NET SDK enthält.
Diese einfache C#/.NET-Konsolen-App führt folgende Aufgaben aus:
- Dies erstellt einen neuen Index basierend auf der Datenstruktur der C#-Klasse
Hotel(die auch auf die KlasseAddressverweist) - Sie testet verschiedene Batchgrößen, um die effizienteste Größe zu ermitteln.
- Daten werden asynchron indexiert.
- Unter Verwendung mehrerer Threads zur Erhöhung der Indizierungsgeschwindigkeit
- Verwenden einer Wiederholungsstrategie mit exponentiellem Backoff, um nicht erfolgreiche Elemente zu wiederholen
Bevor Sie das Programm ausführen, nehmen Sie sich eine Minute Zeit, um den Code und die Indexdefinitionen für dieses Beispiel zu untersuchen. Der relevante Code befindet sich in verschiedenen Dateien:
-
Hotel.csundAddress.csdas Schema enthalten, das den Index definiert -
DataGenerator.csenthält eine einfache Klasse, um die Erstellung großer Mengen an Hoteldaten zu erleichtern -
ExponentialBackoff.csenthält Code zum Optimieren des Indizierungsprozesses, wie in diesem Artikel beschrieben -
Program.csenthält Funktionen zum Erstellen und Löschen des Azure AI Search-Index, indiziert Datenbatches und testet verschiedene Batchgrößen.
Erstellen des Index
In diesem Beispielprogramm wird das Azure SDK für .NET verwendet, um einen Azure KI Search-Index zu definieren und zu erstellen. Das SDK nutzt die Klasse FieldBuilder, um eine Indexstruktur auf der Grundlage einer C#-Datenmodellklasse zu generieren.
Das Datenmodell wird durch die Klasse Hotel definiert, die auch Verweise auf die Klasse Address enthält.
FieldBuilder führt ein Drilldown durch die verschiedenen Klassendefinitionen aus, um eine komplexe Datenstruktur für den Index zu generieren. Mithilfe von Metadatentags werden die Attribute der einzelnen Felder definiert, um beispielsweise anzugeben, ob das Feld durchsuchbar oder sortierbar ist.
Die folgenden Codeausschnitte aus der Hotel.cs Datei geben ein einzelnes Feld und einen Verweis auf eine andere Datenmodellklasse an.
. . .
[SearchableField(IsSortable = true)]
public string HotelName { get; set; }
. . .
public Address Address { get; set; }
. . .
In der Program.cs Datei wird der Index mit einem Namen und einer feldauflistung definiert, die von der FieldBuilder.Build(typeof(Hotel)) Methode generiert wird, und anschließend wie folgt erstellt:
private static async Task CreateIndexAsync(string indexName, SearchIndexClient indexClient)
{
// Create a new search index structure that matches the properties of the Hotel class.
// The Address class is referenced from the Hotel class. The FieldBuilder
// will enumerate these to create a complex data structure for the index.
FieldBuilder builder = new FieldBuilder();
var definition = new SearchIndex(indexName, builder.Build(typeof(Hotel)));
await indexClient.CreateIndexAsync(definition);
}
Daten generieren
Eine einfache Klasse wird in der DataGenerator.cs Datei implementiert, um Daten für Tests zu generieren. Der Zweck dieser Klasse besteht darin, eine große Anzahl von Dokumenten mit einer eindeutigen ID für die Indizierung zu generieren.
Um eine Liste von 100.000 Hotels mit eindeutigen IDs zu erhalten, führen Sie den folgenden Code aus:
long numDocuments = 100000;
DataGenerator dg = new DataGenerator();
List<Hotel> hotels = dg.GetHotels(numDocuments, "large");
Zu Testzwecken stehen in diesem Beispiel zwei Hotelgrößen zur Verfügung: small (klein) und large (groß).
Das Schema Ihres Indexes wirkt sich auf die Indizierungsgeschwindigkeit aus. Nachdem Sie dieses Lernprogramm abgeschlossen haben, sollten Sie diese Klasse konvertieren, um Daten zu generieren, die ihrem beabsichtigten Indexschema am besten entsprechen.
Testbatchgrößen
Um einzelne oder mehrere Dokumente in einen Index zu laden, unterstützt Azure AI Search die folgenden APIs:
Das Indizieren von Dokumenten in Batches verbessert die Indizierungsleistung erheblich. Diese Batches können bis zu 1.000 Dokumente oder bis zu ca. 16 MB pro Batch sein.
Die Bestimmung der optimalen Batchgröße für Ihre Daten ist ein wichtiger Faktor bei der Optimierung der Indizierungsgeschwindigkeit. Die optimale Batchgröße wird hauptsächlich durch die beiden folgenden Faktoren beeinflusst:
- Schema Ihres Indexes
- Datengröße
Da die optimale Batchgröße von Ihrem Index und Ihren Daten abhängt, besteht der beste Ansatz darin, verschiedene Batchgrößen zu testen, um zu bestimmen, welche Ergebnisse die schnellsten Indizierungsgeschwindigkeiten für Ihr Szenario ergeben.
Die folgende Funktion veranschaulicht einen einfachen Ansatz zum Testen von Batchgrößen:
public static async Task TestBatchSizesAsync(SearchClient searchClient, int min = 100, int max = 1000, int step = 100, int numTries = 3)
{
DataGenerator dg = new DataGenerator();
Console.WriteLine("Batch Size \t Size in MB \t MB / Doc \t Time (ms) \t MB / Second");
for (int numDocs = min; numDocs <= max; numDocs += step)
{
List<TimeSpan> durations = new List<TimeSpan>();
double sizeInMb = 0.0;
for (int x = 0; x < numTries; x++)
{
List<Hotel> hotels = dg.GetHotels(numDocs, "large");
DateTime startTime = DateTime.Now;
await UploadDocumentsAsync(searchClient, hotels).ConfigureAwait(false);
DateTime endTime = DateTime.Now;
durations.Add(endTime - startTime);
sizeInMb = EstimateObjectSize(hotels);
}
var avgDuration = durations.Average(timeSpan => timeSpan.TotalMilliseconds);
var avgDurationInSeconds = avgDuration / 1000;
var mbPerSecond = sizeInMb / avgDurationInSeconds;
Console.WriteLine("{0} \t\t {1} \t\t {2} \t\t {3} \t {4}", numDocs, Math.Round(sizeInMb, 3), Math.Round(sizeInMb / numDocs, 3), Math.Round(avgDuration, 3), Math.Round(mbPerSecond, 3));
// Pausing 2 seconds to let the search service catch its breath
Thread.Sleep(2000);
}
Console.WriteLine();
}
Da nicht alle Dokumente die gleiche Größe haben (auch wenn dies in diesem Beispiel der Fall ist), schätzen wir die Größe der Daten, die wir an den Suchdienst senden. Dazu können Sie die folgende Funktion verwenden, die das Objekt zuerst in JSON konvertiert und dann seine Größe in Byte bestimmt. Dadurch können wir die effizientesten Batchgrößen im Hinblick auf die Indizierungsgeschwindigkeit (MB/s) ermitteln.
// Returns size of object in MB
public static double EstimateObjectSize(object data)
{
// converting object to byte[] to determine the size of the data
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
byte[] Array;
// converting data to json for more accurate sizing
var json = JsonSerializer.Serialize(data);
bf.Serialize(ms, json);
Array = ms.ToArray();
// converting from bytes to megabytes
double sizeInMb = (double)Array.Length / 1000000;
return sizeInMb;
}
Für die Funktion sind ein Element vom Typ SearchClient sowie die Anzahl von Versuchen erforderlich, die für die einzelnen Batchgrößen getestet werden sollen. Da die Indizierungszeiten bei den einzelnen Batches variieren können, testen Sie jeden Batch standardmäßig dreimal, um die Ergebnisse statistisch relevanter zu machen.
await TestBatchSizesAsync(searchClient, numTries: 3);
Wenn Sie die Funktion ausführen, sollten Sie in der Konsole eine Ausgabe sehen, die dem folgenden Beispiel ähnelt:
Ermitteln Sie, welche Batchgröße am effizientesten ist, und verwenden Sie diese Batchgröße im nächsten Schritt dieses Lernprogramms. Unter Umständen ist bei verschiedenen Batchgrößen eine Stabilisierung auf einem bestimmten Niveau in MB/s zu beobachten.
Indizierung der Daten
Nachdem Sie nun die zu verwendenden Batchgröße identifiziert haben, die Sie verwenden wollen, besteht der nächste Schritt darin, mit der Indizierung der Daten zu beginnen. In diesem Beispiel werden folgende Schritte unternommen, um Daten effizient zu indizieren:
- Verwendet mehrere Threads/Worker
- Implementiert eine Wiederholungsstrategie mit exponentiellem Backoff
Heben Sie die Auskommentierung der Zeilen 41 bis 49 auf, und führen Sie dann das Programm erneut aus. Bei dieser Ausführung generiert und sendet das Beispiel Batches von Dokumenten, bis zu 100.000, wenn Sie den Code ausführen, ohne die Parameter zu ändern.
Verwenden Sie mehrere Threads/Arbeiter
Um die Indizierungsgeschwindigkeiten von Azure AI Search zu nutzen, verwenden Sie mehrere Threads, um Batchindizierungsanforderungen gleichzeitig an den Dienst zu senden.
Mehrere der wichtigsten Überlegungen können sich auf die optimale Anzahl von Threads auswirken. Sie können dieses Beispiel ändern und mit einer anderen Threadanzahl testen, um die optimale Threadanzahl für Ihr Szenario zu ermitteln. Solange Sie jedoch mehrere Threads parallel ausführen, sollten Sie in der Lage sein, von einem Großteil der Effizienzsteigerungen zu profitieren.
Wenn Sie die Anfragen an den Suchdienst erhöhen, werden möglicherweise HTTP-Statuscodes angezeigt, die darauf hinweisen, dass die Anfrage nicht erfolgreich war. Zwei gängige HTTP-Statuscodes im Zusammenhang mit der Indizierung sind:
- 503 Dienst nicht verfügbar: Dieser Fehler bedeutet, dass die Auslastung des Systems sehr hoch ist und Ihre Anforderung aktuell nicht verarbeitet werden kann.
- 207 Multi-Status: Dieser Fehler bedeutet, dass der Vorgang für einige Dokumente erfolgreich war, bei mindestens einem Dokument aber ein Fehler aufgetreten ist.
Implementieren einer Wiederholungsstrategie mit exponentiellem Backoff
Wenn ein Fehler auftritt, müssen Sie Anforderungen mithilfe einer exponentiellen Backoff-Wiederholungsstrategie wiederholen.
Das .NET SDK von Azure AI Search wiederholt automatisch 503s und andere fehlgeschlagene Anforderungen, Aber Sie sollten Ihre eigene Logik implementieren, um 207s erneut zu versuchen. Open-Source-Tools wie Polly können in einer Wiederholungsstrategie nützlich sein.
In diesem Beispiel wird eine eigene Wiederholungsstrategie mit exponentiellem Backoff implementiert. Zunächst definieren wir einige Variablen, einschließlich der maxRetryAttempts und der Initiale delay für eine fehlgeschlagene Anforderung.
// Create batch of documents for indexing
var batch = IndexDocumentsBatch.Upload(hotels);
// Create an object to hold the result
IndexDocumentsResult result = null;
// Define parameters for exponential backoff
int attempts = 0;
TimeSpan delay = delay = TimeSpan.FromSeconds(2);
int maxRetryAttempts = 5;
Die Ergebnisse des Indizierungsvorgangs werden in der Variable IndexDocumentResult result gespeichert. Mit dieser Variablen können Sie überprüfen, ob Dokumente im Batch fehlgeschlagen sind, wie im folgenden Beispiel gezeigt. Bei partiellen Fehlern wird anhand der IDs der fehlerhaften Dokumente ein neues Batch erstellt.
RequestFailedException Ausnahmen sollten ebenfalls abgefangen werden, da sie darauf hinweisen, dass die Anforderung vollständig fehlgeschlagen ist und erneut überprüft wird.
// Implement exponential backoff
do
{
try
{
attempts++;
result = await searchClient.IndexDocumentsAsync(batch).ConfigureAwait(false);
var failedDocuments = result.Results.Where(r => r.Succeeded != true).ToList();
// handle partial failure
if (failedDocuments.Count > 0)
{
if (attempts == maxRetryAttempts)
{
Console.WriteLine("[MAX RETRIES HIT] - Giving up on the batch starting at {0}", id);
break;
}
else
{
Console.WriteLine("[Batch starting at doc {0} had partial failure]", id);
Console.WriteLine("[Retrying {0} failed documents] \n", failedDocuments.Count);
// creating a batch of failed documents to retry
var failedDocumentKeys = failedDocuments.Select(doc => doc.Key).ToList();
hotels = hotels.Where(h => failedDocumentKeys.Contains(h.HotelId)).ToList();
batch = IndexDocumentsBatch.Upload(hotels);
Task.Delay(delay).Wait();
delay = delay * 2;
continue;
}
}
return result;
}
catch (RequestFailedException ex)
{
Console.WriteLine("[Batch starting at doc {0} failed]", id);
Console.WriteLine("[Retrying entire batch] \n");
if (attempts == maxRetryAttempts)
{
Console.WriteLine("[MAX RETRIES HIT] - Giving up on the batch starting at {0}", id);
break;
}
Task.Delay(delay).Wait();
delay = delay * 2;
}
} while (true);
Schließen Sie von hier aus den Code für das exponentielle Backoff in eine Funktion ein, damit er mühelos aufgerufen werden kann.
Anschließend wird eine weitere Funktion erstellt, um die aktiven Threads zu verwalten. Der Einfachheit halber ist diese Funktion hier nicht enthalten. Sie finden sie aber in ExponentialBackoff.cs. Sie können die Funktion mit dem folgenden Befehl aufrufen. Dabei hotels handelt es sich um die Daten, die wir hochladen möchten, 1000 um die Batchgröße und 8 die Anzahl der gleichzeitigen Threads.
await ExponentialBackoff.IndexData(indexClient, hotels, 1000, 8);
Wenn Sie die Funktion ausführen, sollte eine Ausgabe ähnlich dem folgenden Beispiel angezeigt werden:
Wenn eine Reihe von Dokumenten fehlschlägt, wird ein Fehler ausgegeben, der den Fehler angibt und dass der Batch erneut versucht wird.
[Batch starting at doc 6000 had partial failure]
[Retrying 560 failed documents]
Nach Abschluss der Ausführung der Funktion können Sie überprüfen, ob alle Dokumente dem Index hinzugefügt wurden.
Erkunden des Indexes
Nachdem die Ausführung des Programms abgeschlossen ist, können Sie den ausgefüllten Suchindex entweder programmgesteuert oder mit dem Such-Explorer im Azure-Portal erkunden.
Programmgesteuert
Bei der Überprüfung der Dokumentanzahl in einem Index stehen Ihnen hauptsächlich zwei Optionen zur Verfügung: die API zum Zählen der Dokumente und die API zum Abrufen der Indexstatistik. Beide Pfade erfordern Zeit zum Verarbeiten, daher sollten Sie nicht alarmiert werden, wenn die Anzahl der zurückgegebenen Dokumente anfänglich niedriger ist als erwartet.
Dokumentenanzahl
Der Vorgang "Dokumente zählen" ruft die Anzahl der Dokumente in einem Suchindex ab.
long indexDocCount = await searchClient.GetDocumentCountAsync();
Indexstatistiken abrufen
Mit dem Vorgang zum Abrufen der Indexstatistik werden Informationen zur Dokumentanzahl für den aktuellen Index sowie zur Speichernutzung zurückgegeben. Die Aktualisierung der Indexstatistik dauert länger als die der Dokumentanzahl.
var indexStats = await indexClient.GetIndexStatisticsAsync(indexName);
Azure-Portal
Suchen Sie im Azure-Portal im linken Bereich den Optimierindex in der Liste " Indizes ".
Die Dokumentanzahl und die Speichergröße basieren auf der API zum Abrufen von Indexstatistiken und können mehrere Minuten dauern, bis sie aktualisiert werden.
Zurücksetzen und erneut ausführen
In den frühen experimentellen Phasen der Entwicklung ist es am praktischsten, die Objekt aus Azure KI-Suche zu löschen und von Ihrem Code neu erstellen zu lassen. Ressourcennamen sind eindeutig. Wenn Sie ein Objekt löschen, können Sie es unter dem gleichen Namen neu erstellen.
Im Beispielcode dieses Tutorials wird eine Überprüfung auf bereits vorhandene Indizes durchgeführt. Diese werden gelöscht, damit Sie Ihren Code erneut ausführen können.
Die Indizes können auch über das Azure-Portal gelöscht werden.
Bereinigen von Ressourcen
Wenn Sie in Ihrem eigenen Abonnement arbeiten, ist es ratsam, nach Abschluss eines Projekts die nicht mehr benötigten Ressourcen zu entfernen. Ressourcen, die weiterhin ausgeführt werden, können Sie Geld kosten. Sie können einzelne Ressourcen oder die gesamte Ressourcengruppe mit allen darin enthaltenen Ressourcen löschen.
Sie können Ressourcen im Azure-Portal über den Link Alle Ressourcen oder Ressourcengruppen im linken Navigationsbereich suchen und verwalten.
Nächster Schritt
Weitere Informationen zum Indizieren großer Mengendaten finden Sie im folgenden Lernprogramm: