Sdílet prostřednictvím


Kurz: Optimalizace indexování v rozhraní Push API

Azure AI Search podporuje dva základní přístupy k importu dat do indexu vyhledávání: nasdílení dat do indexu prostřednictvím kódu programu nebo nasměrování indexeru Azure AI Search na podporovaný zdroj dat, který data načítá.

V tomto kurzu se dozvíte, jak efektivně indexovat data pomocí modelu nabízení pomocí dávkových požadavků a použití exponenciální strategie opakování opakování. Ukázkovou aplikaci si můžete stáhnout a spustit. Tento článek vysvětluje klíčové aspekty aplikace a faktory, které je potřeba vzít v úvahu při indexování dat.

Tento kurz používá jazyk C# a knihovnu Azure.Search.Documents ze sady Azure SDK pro .NET k provádění následujících úloh:

  • Vytvoření indexu
  • Otestujte různé velikosti dávek, abyste zjistili nejúčinnější velikost.
  • Asynchronně indexové dávky
  • Použití více vláken ke zvýšení rychlosti indexování
  • Použití exponenciální strategie opakování opakování k opakování neúspěšných dokumentů

Pokud ještě nemáte předplatné Azure, vytvořte si napřed bezplatný účet.

Požadavky

Pro účely tohoto kurzu jsou vyžadovány následující služby a nástroje.

Stažení souborů

Zdrojový kód pro tento kurz je ve složce optimize-data-indexing/v11 v úložišti GitHub Azure-Samples/azure-search-dotnet-samples .

Klíčové aspekty

Dále jsou uvedeny faktory ovlivňující rychlost indexování. Další informace najdete v indexování velkých datových sad.

  • Úroveň služby a počet oddílů/replik – Přidání oddílů nebo upgrade úrovně zvyšuje rychlost indexování.
  • Složitost schématu indexu – Přidávání polí a vlastností polí snižuje rychlost indexování. Menší indexy jsou rychlejší k indexování.
  • Velikost dávky – optimální velikost dávky se liší v závislosti na schématu indexu a datové sadě.
  • Počet vláken/pracovních procesů – jedno vlákno nebude plně využívat rychlost indexování.
  • Strategie opakování – Strategie opakování exponenciálního opakování je osvědčeným postupem pro optimální indexování.
  • Rychlosti přenosu dat v síti – Rychlost přenosu dat může být omezujícím faktorem. Indexujte data z prostředí Azure, abyste zvýšili rychlost přenosu dat.

1. Vytvoření Search Azure AI

K dokončení tohoto kurzu potřebujete Search Azure AI, kterou můžete vytvořit na portálu. Doporučujeme použít stejnou úroveň, kterou plánujete použít v produkčním prostředí, abyste mohli přesně testovat a optimalizovat rychlost indexování.

V tomto kurzu se používá ověřování založené na klíčích. Zkopírujte klíč rozhraní API správce a vložte ho do souboru appsettings.json .

  1. Přihlaste se k webu Azure Portal a na stránce Přehled vyhledávací služby získejte adresu URL. Příkladem koncového bodu může být https://mydemo.search.windows.net.

  2. V části Klíče nastavení>získejte klíč správce pro úplná práva ke službě. Existují dva zaměnitelné klíče správce, které jsou k dispozici pro zajištění kontinuity podnikových procesů v případě, že potřebujete jeden převést. Primární nebo sekundární klíč můžete použít u požadavků pro přidávání, úpravy a odstraňování objektů.

    Získání koncového bodu HTTP a přístupového klíče

2. Nastavení prostředí

  1. Spusťte Visual Studio a otevřete OptimizeDataIndexing.sln.
  2. V Průzkumník řešení otevřete appsettings.json a zadejte informace o připojení.
{
  "SearchServiceUri": "https://{service-name}.search.windows.net",
  "SearchServiceAdminApiKey": "",
  "SearchIndexName": "optimize-indexing"
}

3. Prozkoumání kódu

Po aktualizaci appsettings.json by měl být ukázkový program v OptimizeDataIndexing.sln připravený k sestavení a spuštění.

Tento kód je odvozený z části Rychlý start jazyka C#: Fulltextové vyhledávání pomocí sad Azure SDK. Podrobnější informace o základech práce se sadou .NET SDK najdete v tomto článku.

Tato jednoduchá konzolová aplikace C#/.NET provádí následující úlohy:

  • Vytvoří nový index založený na datové struktuře třídy C# Hotel (která také odkazuje na třídu Address).
  • Testuje různé velikosti dávek, aby bylo možné určit nejúčinnější velikost.
  • Indexuje data asynchronně.
    • Zvýšení rychlosti indexování pomocí více vláken
    • Použití exponenciální strategie opakování opakování k opakování neúspěšných položek

Než program spustíte, prostudujte si kód a definice indexu pro tuto ukázku. Příslušný kód je v několika souborech:

  • Hotel.cs a Address.cs obsahují schéma, které definuje index.
  • DataGenerator.cs obsahuje jednoduchou třídu, která usnadňuje vytváření velkých objemů hotelových dat.
  • ExponentialBackoff.cs obsahuje kód pro optimalizaci procesu indexování, jak je popsáno v tomto článku.
  • Program.cs obsahuje funkce, které vytvářejí a odstraňuje index služby Azure AI Search, indexuje dávky dat a testuje různé velikosti dávek.

Vytvoření indexu

Tento ukázkový program používá sadu Azure SDK pro .NET k definování a vytvoření indexu Azure AI Search. Využívá třídu FieldBuilder k vygenerování struktury indexu z třídy datového modelu jazyka C#.

Datový model je definován třídou Hotel, která obsahuje také odkazy na třídu Address. FieldBuilder přejde k podrobnostem prostřednictvím více definic tříd a vygeneruje složitou datovou strukturu indexu. Značky metadat slouží k definování atributů jednotlivých polí, jako je například to, jestli je prohledávatelné nebo řaditelné.

Následující fragmenty kódu ze souboru Hotel.cs ukazují, jak lze zadat jedno pole a odkaz na jinou třídu datového modelu.

. . .
[SearchableField(IsSortable = true)]
public string HotelName { get; set; }
. . .
public Address Address { get; set; }
. . .

V souboru Program.cs je index definován s názvem a kolekcí polí vygenerovanou FieldBuilder.Build(typeof(Hotel)) metodou a pak vytvořen následujícím způsobem:

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);
}

Generování dat

Jednoduchá třída je implementována v souboru DataGenerator.cs pro generování dat pro testování. Jediným účelem této třídy je usnadnit generování velkého počtu dokumentů s jedinečným ID pro indexování.

Pokud chcete získat seznam 100 000 hotelů s jedinečnými ID, spusťte následující řádky kódu:

long numDocuments = 100000;
DataGenerator dg = new DataGenerator();
List<Hotel> hotels = dg.GetHotels(numDocuments, "large");

Pro testování v této ukázce jsou k dispozici dvě velikosti hotelů: malé a velké.

Schéma indexu má vliv na rychlosti indexování. Z tohoto důvodu dává smysl převést tuto třídu tak, aby generovala data, která nejlépe odpovídají zamýšlenému schématu indexu po spuštění tohoto kurzu.

4. Velikosti testovacích dávek

Azure AI Search podporuje následující rozhraní API pro načtení jednoho nebo více dokumentů do indexu:

Indexování dokumentů v dávkách výrazně zlepší výkon indexování. Tyto dávky můžou být až 1 000 dokumentů nebo přibližně 16 MB na dávku.

Určení optimální velikosti dávky pro vaše data je klíčovou součástí optimalizace rychlosti indexování. Optimální velikost dávky ovlivňují dva primární faktory:

  • Schéma indexu
  • Velikost dat

Vzhledem k tomu, že optimální velikost dávky závisí na indexu a vašich datech, nejlepším přístupem je otestovat různé velikosti dávek, abyste zjistili, jaké výsledky mají pro váš scénář nejrychlejší rychlost indexování.

Následující funkce ukazuje jednoduchý přístup k testování velikostí dávek.

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();
}

Vzhledem k tomu, že ne všechny dokumenty mají stejnou velikost (i když jsou v této ukázce), odhadujeme velikost dat, která odesíláme do vyhledávací služby. Provedeme to pomocí níže uvedené funkce, která nejprve převede objekt na json a pak určí jeho velikost v bajtech. Tato technika nám umožňuje určit, které velikosti dávek jsou nejúčinnější z hlediska rychlosti indexování MB/s.

// 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;
}

Funkce vyžaduje SearchClient plus počet pokusů, které chcete otestovat pro každou velikost dávky. Vzhledem k tomu, že pro každou dávku může být proměnlivost časů indexování, vyzkoušíme ve výchozím nastavení každou dávku třikrát, aby výsledky byly statisticky významnější.

await TestBatchSizesAsync(searchClient, numTries: 3);

Když funkci spustíte, měl by se v konzole zobrazit výstup podobný následujícímu:

Výstup funkce velikosti testovací dávky

Určete, která velikost dávky je nejúčinnější, a pak tuto velikost dávky použijte v dalším kroku kurzu. V různých velikostech dávek se může zobrazit plateau v MB/s.

5. Indexování dat

Teď, když jsme identifikovali velikost dávky, kterou máme v úmyslu použít, je dalším krokem začít indexovat data. Pokud chcete efektivně indexovat data, tato ukázka:

  • Používá více vláken nebo pracovních procesů.
  • Implementuje exponenciální strategii opakování opakování.

Odkomentujte řádky 41 až 49 a spusťte znovu a program. Při tomto spuštění ukázka vygeneruje a odesílá dávky dokumentů, až 100 000, pokud kód spustíte beze změny parametrů.

Použití více vláken nebo pracovních procesů

Pokud chcete využít plné výhody rychlosti indexování služby Azure AI Search, použijte více vláken k souběžnému odesílání žádostí o dávkové indexování do služby.

Několik klíčových aspektů, které jsme zmínili dříve, může ovlivnit optimální počet vláken. Tuto ukázku a testování můžete upravit s různými počty vláken, abyste zjistili optimální počet vláken pro váš scénář. Pokud ale máte spuštěných několik vláken současně, měli byste být schopni využít výhod většiny zvýšení efektivity.

Při zprovoznění požadavků do vyhledávací služby můžete narazit na stavové kódy HTTP, které značí, že požadavek nebyl plně úspěšný. Během indexování jsou dva běžné stavové kódy HTTP:

  • 503 Služba není k dispozici – Tato chyba znamená, že systém je zatížený velkým zatížením a v tuto chvíli nelze vaši žádost zpracovat.
  • 207 Multi-Status – Tato chyba znamená, že některé dokumenty byly úspěšné, ale alespoň jeden selhal.

Implementace strategie opakování exponenciálního zpožování

Pokud dojde k selhání, měly by se žádosti opakovat pomocí strategie exponenciálního opakování opakování.

Sada .NET SDK služby Azure AI Search automaticky opakuje pokusy o 503 a dalších neúspěšných požadavků, ale měli byste implementovat vlastní logiku pro opakování 207s. Opensourcové nástroje, jako je Polly , můžou být užitečné ve strategii opakování.

V této ukázce implementujeme vlastní strategii opakování exponenciálního opakování. Začneme definováním některých proměnných, včetně maxRetryAttempts a iniciály delay neúspěšného požadavku:

// 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;

Výsledky operace indexování jsou uloženy v proměnné IndexDocumentResult result. Tato proměnná je důležitá, protože umožňuje zkontrolovat, jestli některé dokumenty v dávce selhaly, jak je znázorněno níže. Pokud dojde k částečnému selhání, vytvoří se nová dávka na základě ID neúspěšných dokumentů.

RequestFailedException Výjimky by také měly být zachyceny, protože indikují, že žádost selhala úplně a měla by se také opakovat.

// 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);

Odsud zabalíme exponenciální backoff kód do funkce, aby se dala snadno volat.

Pak se vytvoří další funkce pro správu aktivních vláken. Pro zjednodušení není tato funkce zahrnutá, ale najdete ji v ExponentialBackoff.cs. Funkci lze volat pomocí následujícího příkazu, kde jsou data, která hotels chcete nahrát, 1000 je velikost dávky a 8 je počet souběžných vláken:

await ExponentialBackoff.IndexData(indexClient, hotels, 1000, 8);

Při spuštění funkce by se měl zobrazit výstup podobný následujícímu:

Výstup funkce dat indexu

Pokud dávka dokumentů selže, vytiskne se chyba, která značí selhání a že se dávka opakuje:

[Batch starting at doc 6000 had partial failure]
[Retrying 560 failed documents]

Po dokončení funkce můžete ověřit, že všechny dokumenty byly přidány do indexu.

6. Prozkoumání indexu

Vyplněný index vyhledávání můžete prozkoumat po programovém spuštění programu nebo pomocí Průzkumníka služby Search na portálu.

Programově

Existují dvě hlavní možnosti pro kontrolu počtu dokumentů v indexu: rozhraní API Count Documents a rozhraní API pro získání statistik indexu. Obě cesty vyžadují čas na zpracování, takže pokud je vrácený počet vrácených dokumentů zpočátku nižší, než očekáváte.

Počet dokumentů

Operace Count Documents načte počet dokumentů v indexu vyhledávání:

long indexDocCount = await searchClient.GetDocumentCountAsync();

Získání statistik indexu

Operace Získat statistiku indexu vrátí počet dokumentů pro aktuální index a využití úložiště. Aktualizace statistik indexu bude trvat déle než počet dokumentů.

var indexStats = await indexClient.GetIndexStatisticsAsync(indexName);

portál Azure

Na webu Azure Portal v levém navigačním podokně vyhledejte indexování optimalizace v seznamu Indexy .

Seznam indexů Azure AI Search

Počet dokumentů a velikost úložiště jsou založené na rozhraní API pro získání statistik indexu a aktualizace může trvat několik minut.

Resetování a opětovné spuštění

V počátečních experimentálních fázích vývoje je nejproktičtějším přístupem k iteraci návrhu odstranit objekty z Azure AI Search a umožnit kódu jejich opětovné sestavení. Názvy prostředků jsou jedinečné. Když se objekt odstraní, je možné ho znovu vytvořit se stejným názvem.

Vzorový kód pro tento kurz zkontroluje existující indexy a odstraní je, abyste mohli znovu spustit kód.

Indexy můžete odstranit také pomocí portálu.

Vyčištění prostředků

Když pracujete ve vlastním předplatném, je na konci projektu vhodné odebrat prostředky, které už nepotřebujete. Prostředky, které necháte spuštěné, vás stojí peníze. Prostředky můžete odstraňovat jednotlivě nebo můžete odstranit skupinu prostředků, a odstranit tak celou sadu prostředků najednou.

Prostředky můžete najít a spravovat na portálu pomocí odkazu Všechny prostředky nebo skupiny prostředků v levém navigačním podokně.

Další kroky

Další informace o indexování velkých objemů dat najdete v následujícím kurzu.