Oktatóanyag: Indexelés optimalizálása a Push API-val

Az Azure AI Search két alapvető módszert támogat az adatok keresési indexbe való importálásához: az adatok programozott módon való leküldése az indexbe, vagy egy Azure AI Search-indexelő egy támogatott adatforrásra mutatva beolvasni az adatokat.

Ez az oktatóanyag bemutatja, hogyan indexelheti hatékonyan az adatokat a leküldéses modell használatával kérelmek kötegelésével és exponenciális visszalépési újrapróbálkozási stratégia használatával. Letöltheti és futtathatja a mintaalkalmazást. Ez a cikk az alkalmazás főbb szempontjait és az adatok indexelése során figyelembe veendő tényezőket ismerteti.

Ez az oktatóanyag a .NET-hez készült Azure SDK C# és Azure.Search.Documents könyvtárát használja a következő feladatok végrehajtásához:

  • Index létrehozása
  • Különböző kötegméretek tesztelése a leghatékonyabb méret meghatározásához
  • Index kötegek aszinkron módon
  • Több szál használata az indexelési sebesség növeléséhez
  • Exponenciális backoff újrapróbálkozási stratégia használata a sikertelen dokumentumok újrapróbálkozásához

Ha még nincs Azure-előfizetése, kezdés előtt hozzon létre egy ingyenes fiókot.

Előfeltételek

Az oktatóanyaghoz az alábbi szolgáltatásokra és eszközökre van szükség.

Fájlok letöltése

Az oktatóanyag forráskódja az Azure-Samples/azure-search-dotnet-samples GitHub-adattár optimize-data-indexing/v11 mappájában található.

Fő szempontok

Az indexelési sebességet befolyásoló tényezők a következőkben láthatók. További információt az Index nagyméretű adatkészleteiben találhat.

  • Szolgáltatási szint és partíciók/replikák száma – A partíciók hozzáadása vagy a réteg frissítése növeli az indexelési sebességet.
  • Indexséma összetettsége – A mezők és mezőtulajdonságok hozzáadása csökkenti az indexelési sebességet. A kisebb indexek gyorsabban indexelnek.
  • Kötegméret – Az optimális kötegméret az indexsémától és az adatkészlettől függően változik.
  • Szálak/feldolgozók száma – Egyetlen szál nem használja ki teljes mértékben az indexelési sebesség előnyeit.
  • Újrapróbálkozási stratégia – Az optimális indexelés ajánlott eljárása az exponenciális backoff újrapróbálkozási stratégia.
  • Hálózati adatátvitel sebessége – Az adatátvitel sebessége korlátozó tényező lehet. Indexelje az adatokat az Azure-környezetből az adatátviteli sebesség növelése érdekében.

1 – Azure AI-Search szolgáltatás létrehozása

Az oktatóanyag elvégzéséhez szüksége lesz egy Azure AI-Search szolgáltatás, amelyet a portálon hozhat létre. Javasoljuk, hogy ugyanazt a szintet használja, amelyet éles környezetben szeretne használni, hogy pontosan tesztelhesse és optimalizálhassa az indexelési sebességet.

Ez az oktatóanyag kulcsalapú hitelesítést használ. Másolja a rendszergazdai API-kulcsot a appsettings.json fájlba való beillesztéshez.

  1. Jelentkezzen be az Azure Portalra, és a keresési szolgáltatás áttekintési lapján kérje le az URL-címet. A végpontok például a következőképpen nézhetnek ki: https://mydemo.search.windows.net.

  2. A Gépház> Keysben szerezze be a szolgáltatás teljes jogosultságainak rendszergazdai kulcsát. Két felcserélhető rendszergazdai kulcs áll rendelkezésre az üzletmenet folytonossága érdekében, ha át kell gördítenie egyet. Az elsődleges vagy a másodlagos kulcsot használhatja objektumok hozzáadására, módosítására és törlésére vonatkozó kérelmekhez.

    Get an HTTP endpoint and access key

2 – A környezet beállítása

  1. Indítsa el a Visual Studiót, és nyissa meg a OptimizeDataIndexing.sln.
  2. A Megoldáskezelő nyissa meg a appsettings.json a kapcsolati adatok megadásához.
{
  "SearchServiceUri": "https://{service-name}.search.windows.net",
  "SearchServiceAdminApiKey": "",
  "SearchIndexName": "optimize-indexing"
}

3 – A kód megismerése

A appsettings.json frissítése után az OptimizeDataIndexing.sln mintaprogramjának készen kell állnia a létrehozásra és a futtatásra.

Ez a kód a rövid útmutató C# szakaszából származik: Teljes szöveges keresés az Azure SDK-k használatával. Ebben a cikkben részletesebb információkat talál a .NET SDK használatának alapjairól.

Ez az egyszerű C#/.NET-konzolalkalmazás a következő feladatokat hajtja végre:

  • Létrehoz egy új indexet a C# Hotel osztály adatstruktúrája alapján (amely a Cím osztályra is hivatkozik).
  • Különböző kötegméretek tesztelése a leghatékonyabb méret meghatározásához
  • Az adatokat aszinkron módon indexeli
    • Több szál használata az indexelési sebesség növeléséhez
    • Exponenciális backoff újrapróbálkozási stratégia használata a sikertelen elemek újrapróbálkozásához

A program futtatása előtt szánjon egy percet a minta kódjának és indexdefinícióinak tanulmányozására. A megfelelő kód több fájlban található:

  • Hotel.cs és Address.cs az indexet meghatározó sémát tartalmazza
  • DataGenerator.cs egy egyszerű osztályt tartalmaz, amely megkönnyíti a nagy mennyiségű szállodai adat létrehozását
  • ExponentialBackoff.cs tartalmaz kódot az indexelési folyamat optimalizálásához a jelen cikkben leírtak szerint
  • Program.cs olyan függvényeket tartalmaz, amelyek létrehozzák és törlik az Azure AI Search-indexet, indexelik az adatkötegeket, és különböző kötegméreteket tesztelnek

Az index létrehozása

Ez a mintaprogram az Azure SDK for .NET használatával definiál és hoz létre egy Azure AI Search-indexet. Az osztály előnyeit kihasználva FieldBuilder indexstruktúrát hozhat létre egy C#-adatmodell-osztályból.

Az adatmodellt a Hotel osztály határozza meg, amely a Cím osztályra mutató hivatkozásokat is tartalmaz. A FieldBuilder több osztálydefiníción keresztül részletezi az index összetett adatstruktúráját. A metaadatcímkék az egyes mezők attribútumainak meghatározására szolgálnak, például azt, hogy kereshető vagy rendezhető-e.

A Hotel.cs fájl alábbi kódrészletei bemutatják, hogyan adható meg egyetlen mező és egy másik adatmodell-osztályra mutató hivatkozás.

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

A Program.cs fájlban az index a metódus által létrehozott névvel és mezőgyűjteménysel van definiálva, majd a FieldBuilder.Build(typeof(Hotel)) következőképpen jön létre:

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

Adatok generálása

A DataGenerator.cs fájlban egy egyszerű osztályt implementál a teszteléshez szükséges adatok létrehozásához. Ennek az osztálynak az egyetlen célja, hogy megkönnyítse nagyszámú, egyedi azonosítóval rendelkező dokumentum létrehozásának megkönnyítését az indexeléshez.

100 000 egyedi azonosítóval rendelkező szálloda listájának lekéréséhez futtassa a következő kódsorokat:

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

Ebben a mintában két méretben lehet tesztelni a szállodákat: kicsi és nagy.

Az index sémája hatással van az indexelési sebességre. Ezért érdemes átalakítani ezt az osztályt úgy, hogy az oktatóanyag futtatása után a legjobban megfelelő adatokat hozzon létre a kívánt indexsémának megfelelően.

4 – Kötegméretek tesztelése

Az Azure AI Search a következő API-kat támogatja egy vagy több dokumentum indexbe való betöltéséhez:

A dokumentumok kötegekben való indexelése jelentősen javítja az indexelési teljesítményt. Ezek a kötegek legfeljebb 1000 dokumentumból, kötegenként körülbelül 16 MB-ból lehetnek.

Az indexelési sebesség optimalizálásának kulcsfontosságú összetevője az adatok optimális kötegméretének meghatározása. Az optimális kötegméretet befolyásoló két elsődleges tényező:

  • Az index sémája
  • Az adatok mérete

Mivel az optimális kötegméret az indextől és az adatoktól függ, a legjobb módszer a különböző kötegméretek tesztelése, hogy megállapítsa, mi okozza a forgatókönyv leggyorsabb indexelési sebességét.

Az alábbi függvény egy egyszerű módszert mutat be a kötegméretek tesztelésére.

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

Mivel nem minden dokumentum mérete azonos (bár ebben a mintában szerepelnek), megbecsüljük a keresési szolgáltatásnak küldött adatok méretét. Ezt az alábbi függvény használatával hajtjuk végre, amely először json-ra konvertálja az objektumot, majd bájtokban határozza meg a méretét. Ez a technika lehetővé teszi, hogy megállapítsuk, mely kötegméretek a leghatékonyabbak MB/s indexelési sebesség szempontjából.

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

A függvényhez meg kell adni az SearchClient egyes kötegméretekhez tesztelni kívánt próbálkozások számát. Mivel az egyes kötegek indexelési idejének variabilitása lehet, alapértelmezés szerint háromszor próbálkozunk, hogy az eredmények statisztikailag szignifikánsabbak legyenek.

await TestBatchSizesAsync(searchClient, numTries: 3);

A függvény futtatásakor az alábbihoz hasonló kimenetnek kell megjelennie a konzolon:

Output of test batch size function

Azonosítsa a leghatékonyabb kötegméretet, majd használja ezt a kötegméretet az oktatóanyag következő lépésében. Előfordulhat, hogy a plató MB/s-ban különböző kötegméretekben jelenik meg.

5 – Indexadatok

Most, hogy azonosítottuk a használni kívánt kötegméretet, a következő lépés az adatok indexelésének megkezdése. Az adatok hatékony indexeléséhez ez a minta:

  • Több szálat/feldolgozót használ.
  • Exponenciális backoff újrapróbálkozási stratégiát valósít meg.

A 41–49. sor kibontása, majd az újrafuttatás és a program. Ebben a futtatásban a minta legfeljebb 100 000 dokumentumköteget hoz létre és küld el, ha a paraméterek módosítása nélkül futtatja a kódot.

Több szál/feldolgozó használata

Az Azure AI Search indexelési sebességének teljes kihasználásához használjon több szálat a kötegindexelési kérések egyidejű elküldéséhez a szolgáltatásnak.

A korábban említett főbb szempontok közül több is befolyásolhatja a szálak optimális számát. Ezt a mintát módosíthatja, és különböző szálszámokkal tesztelheti a forgatókönyv optimális szálszámának meghatározásához. Mindaddig azonban, amíg több szál fut egyszerre, képesnek kell lennie arra, hogy kihasználja a hatékonyságnövekedés nagy részét.

A keresési szolgáltatásra irányuló kérések felfuttatása során HTTP-állapotkódok jelenhetnek meg, amelyek azt jelzik, hogy a kérés nem sikerült teljes mértékben. Az indexelés során két gyakori HTTP-állapotkód:

  • 503 Szolgáltatás nem érhető el – Ez a hiba azt jelenti, hogy a rendszer nagy terhelés alatt áll, és a kérés jelenleg nem dolgozható fel.
  • 207 Többállapotú – Ez a hiba azt jelenti, hogy egyes dokumentumok sikeresek voltak, de legalább egy sikertelen volt.

Exponenciális backoff újrapróbálkozási stratégia implementálása

Ha hiba történik, a kéréseket exponenciális visszalépési újrapróbálkozási stratégiával kell újrapróbálkoznia.

Az Azure AI Search .NET SDK-jában automatikusan újrapróbálkoznak az 503s és más sikertelen kérések, de a 207-ek újrapróbálkozásához saját logikát kell implementálnia. A nyílt forráskódú eszközök, például a Polly hasznosak lehetnek az újrapróbálkozás stratégiájában.

Ebben a mintában saját exponenciális backoff újrapróbálkozási stratégiát valósítunk meg. Először definiálunk néhány változót, köztük egy maxRetryAttempts sikertelen kérés kezdeti delay értékét:

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

Az indexelési művelet eredményeit a változó IndexDocumentResult resulttárolja. Ez a változó azért fontos, mert lehetővé teszi annak ellenőrzését, hogy a kötegben lévő dokumentumok sikertelenek-e az alább látható módon. Részleges hiba esetén egy új köteg jön létre a sikertelen dokumentumok azonosítója alapján.

RequestFailedException kivételeket is ki kell fogni, mivel azt jelzik, hogy a kérés teljesen meghiúsult, és újra kell próbálkozni.

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

Innen az exponenciális visszalépési kódot egy függvénybe csomagoljuk, hogy könnyen meghívható legyen.

Ezután létrejön egy másik függvény az aktív szálak kezeléséhez. Az egyszerűség kedvéért ez a függvény nem szerepel itt, de megtalálható a ExponentialBackoff.cs. A függvény a következő paranccsal hívható meg, ahol hotels a feltölteni kívánt adatok, 1000 a köteg mérete és 8 az egyidejű szálak száma:

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

A függvény futtatásakor az alábbihoz hasonló kimenetnek kell megjelennie:

Output of index data function

Ha egy dokumentumköteg meghibásodik, a rendszer a hibát jelző hibát nyomtatja ki, és a köteg újrapróbálkozás alatt áll:

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

A függvény futtatása után ellenőrizheti, hogy az összes dokumentum hozzá lett-e adva az indexhez.

6 – Az index felfedezése

A kitöltött keresési indexet a program programozott futtatása után, vagy a Portál Keresőböngészőjének használatával ismerheti meg.

Programozott módon

Az indexben lévő dokumentumok számának ellenőrzésére két fő lehetőség áll rendelkezésre: a Dokumentumok száma API és az Indexstatisztikák lekérése API. Mindkét elérési úthoz időre van szükség a feldolgozáshoz, ezért ne aggódjon, ha a visszaadott dokumentumok száma kezdetben alacsonyabb a vártnál.

Dokumentumok száma

A Dokumentumok száma művelet lekéri a keresési indexben lévő dokumentumok számát:

long indexDocCount = await searchClient.GetDocumentCountAsync();

Indexstatisztikák lekérése

Az Indexstatisztikák lekérése művelet az aktuális index dokumentumszámát és a tárterület használatát adja vissza. Az indexstatisztikák frissítése hosszabb időt vesz igénybe, mint a dokumentumok száma.

var indexStats = await indexClient.GetIndexStatisticsAsync(indexName);

Azure Portalra

Az Azure Portal bal oldali navigációs paneljén keresse meg az optimalizálási indexet az Indexek listában.

List of Azure AI Search indexes

A dokumentumszám és a tárterület mérete az Indexstatisztikai API lekérésén alapul, és a frissítés eltarthat néhány percig.

Alaphelyzetbe állítás és ismételt futtatás

A fejlesztés korai kísérleti fázisaiban a tervezési iteráció legpraktikusabb módszere az objektumok törlése az Azure AI Searchből, és a kód újraépítésének engedélyezése. Az erőforrásnevek egyediek. Egy objektum törlése révén újból létrehozhatja azt ugyanazzal a névvel.

Az oktatóanyag mintakódja ellenőrzi a meglévő indexeket, és törli őket, hogy újrafuttassa a kódot.

A portálon indexeket is törölhet.

Az erőforrások eltávolítása

Ha a saját előfizetésében dolgozik, a projekt végén célszerű eltávolítania a már nem szükséges erőforrásokat. A továbbra is futó erőforrások költségekkel járhatnak. Az erőforrásokat törölheti egyesével, vagy az erőforráscsoport törlésével eltávolíthatja a benne lévő összes erőforrást is.

A portálon a bal oldali navigációs panel Minden erőforrás vagy Erőforráscsoport hivatkozásával kereshet és kezelhet erőforrásokat.

Következő lépések

A nagy mennyiségű adatok indexelésével kapcsolatos további információkért próbálja ki az alábbi oktatóanyagot.