Ottimizzare l'applicazione Azure Cosmos DB con la limitazione della frequenza

Questo articolo fornisce agli sviluppatori una metodologia per imporre limiti di velocità sulle richieste ad Azure Cosmos DB. L'implementazione di questo modello può ridurre gli errori e migliorare le prestazioni generali per i carichi di lavoro che superano la velocità effettiva con provisioning del contenitore o del database di destinazione.

Le richieste che superano la velocità effettiva con provisioning in Azure Cosmos DB possono causare errori temporanei come TooManyRequests, Timeout e ServiceUnavailable. In genere si ritentano queste richieste quando la capacità è disponibile e in questo caso vanno a buon fine. Tuttavia, questo approccio può comportare un numero elevato di richieste che seguono il percorso di errore nel codice e, di conseguenza, una velocità effettiva ridotta.

Le prestazioni di sistema ottimali, misurate in base al costo e al tempo, possono essere raggiunte associando il traffico del carico di lavoro lato client alla velocità effettiva con provisioning lato server.

Prendiamo in considerazione lo scenario seguente:

  • Si crea un account Azure Cosmos DB con 20.000 UR al secondo.
  • L'applicazione elabora un processo di inserimento contenente 10.000 record, ognuno dei quali costa 10 unità richieste (UR). La capacità totale necessaria per completare questo processo è pari a 100.000 UR.
  • Si invia un intero processo ad Azure Cosmos DB e si prevede un numero elevato di errori temporanei e un grande buffer di richieste che è necessario ripetere. Questa condizione è dovuta al fatto che il numero totale di unità richieste necessarie per il processo (100.000) è maggiore del valore massimo con provisioning (20.000). Circa 2.000 record vengono accettati nel database, ma circa 8.000 vengono rifiutati. Si inviano circa 8.000 record ad Azure Cosmos DB durante i tentativi, di cui circa 2.000 vengono accettati e così via. Si prevede che questo modello invii circa 30.000 record anziché 10.000 record.
  • Se invece si sceglie di inviare queste richieste in modo uniforme nell'arco di cinque secondi, non si dovrebbero riscontrare errori e si otterrà una velocità effettiva complessivamente superiore, poiché ogni batch rimarrebbe pari o inferiore ai 20.000 record con provisioning.

La distribuzione delle richieste in un periodo di tempo può essere eseguita introducendo un meccanismo di limitazione della velocità nel codice.

Le UR di cui è stato effettuato il provisioning per un contenitore verranno condivise uniformemente tra il numero di partizioni fisiche. Nell'esempio precedente, se Azure Cosmos DB ha effettuato il provisioning di due partizioni fisiche, ognuna avrà 10.000 UR.

Per altre informazioni sulle unità richiesta, vedere Unità richiesta in Azure Cosmos DB. Per altre informazioni sulla stima del numero di UR utilizzate dal carico di lavoro, vedere Considerazioni sulle unità richiesta. Per altre informazioni sul partizionamento di Azure Cosmos DB, vedere Partizionamento e scalabilità orizzontale in Azure Cosmos DB.

Metodologia

Un approccio all'implementazione della limitazione della velocità potrebbe essere simile al seguente:

  1. Profilare l'applicazione in modo che siano presenti dati sulle scritture e le richieste di lettura usate.
  2. Definire tutti gli indici.
  3. Popolare la raccolta con una quantità ragionevole di dati (potrebbe trattarsi di dati di esempio). Se si prevede che l'applicazione abbia in genere milioni di record, popolarla con milioni di record.
  4. Scrivere i documenti rappresentativi e registrare il costo delle UR.
  5. Eseguire le query rappresentative e registrare il costo delle UR.
  6. Implementare una funzione nell'applicazione per determinare il costo di una determinata richiesta in base ai risultati.
  7. Implementare un meccanismo di limitazione della velocità nel codice per garantire che la somma di tutte le operazioni inviate ad Azure Cosmos DB in un secondo non superi la velocità effettiva con provisioning.
  8. Testare il carico dell'applicazione e assicurarsi di non superare la velocità effettiva con provisioning.
  9. Ritestare periodicamente i costi delle UR e aggiornare la funzione di costo in base alle esigenze.

Indicizzazione

A differenza di altri database SQL e NoSQL con cui si ha familiarità, i criteri di indicizzazione predefiniti di Azure Cosmos DB per i contenitori appena creati indicizzano ogni proprietà. Ogni proprietà indicizzata aumenta il costo delle UR delle scritture.

I criteri di indicizzazione predefiniti possono ridurre la latenza nei sistemi con un'attività intensiva di lettura in cui le condizioni di filtro delle query sono ben distribuite in tutti i campi archiviati. Ad esempio, i sistemi in cui Azure Cosmos DB impiega la maggior parte del tempo a gestire le ricerche ad hoc create dall'utente finale possono trarne vantaggio.

È possibile escludere dall'indicizzazione le proprietà che non vengono mai cercate. La rimozione delle proprietà dall'indice potrebbe migliorare le prestazioni complessive del sistema (costi e tempi) per i sistemi che registrano un'attività di lettura intensiva e con modelli di recupero dei record più vincolati.

Prima di misurare i costi, è consigliabile configurare intenzionalmente criteri di indice appropriati per i casi d'uso. Se in seguito si modificano gli indici, è necessario rieseguire tutti i calcoli dei costi.

Quando possibile, testare un sistema in fase di sviluppo con un carico che rifletta le query tipiche, sia in condizioni normali sia di picco, permette di individuare quali criteri di indicizzazione usare.

Per altre informazioni sugli indici, vedere Criteri di indicizzazione in Azure Cosmos DB.

Misurazione dei costi

Esistono alcuni concetti chiave per misurare i costi:

  • Prendere in considerazione tutti i fattori che influiscono sull'utilizzo delle UR, come descritto in considerazioni sulle unità richiesta.
  • Tutte le letture e le scritture nel database o nel contenitore condivideranno la stessa velocità effettiva con provisioning.
  • Il consumo di UR viene addebitato indipendentemente dalle API di Azure Cosmos DB in uso.
  • La strategia di partizione per una raccolta può avere un impatto significativo sul costo di un sistema. Per altre informazioni, vedere Partizionamento e scalabilità orizzontale in Azure Cosmos DB.
  • Usare documenti rappresentativi e query rappresentative.
    • Si tratta di documenti e query ritenuti vicini a ciò che il sistema operativo incontrerà.
    • Il modo migliore per ottenere questi documenti e query rappresentativi consiste nell'instrumentare l'utilizzo dell'applicazione. È sempre meglio prendere una decisione basata sui dati.
  • Misurare periodicamente i costi.
    • Modifiche all'indice, le dimensioni degli indici possono influire sul costo.
    • È utile creare un test ripetibile (forse anche automatizzato) dei documenti e delle query rappresentativi.
    • Assicurarsi che i documenti e le query rappresentativi siano ancora rappresentativi.

Il metodo per determinare il costo di una richiesta è diverso per ogni API:

Richieste di scrittura

Il costo delle operazioni di scrittura tende a essere facile da prevedere. Si inseriscono record e si documentano i costi segnalati da Azure Cosmos DB.

Se si dispone di documenti di dimensioni diverse e/o documenti che usano indici diversi, è importante misurarli tutti. È possibile che i documenti rappresentativi siano abbastanza vicini in termini di costi da poter assegnare un singolo valore in tutte le scritture. Ad esempio, se si sono trovati costi pari a 13,14 UR, 16,01 UR e 12,63 UR, è possibile ottenere una media di 14 UR.

Richieste di lettura

Il costo delle operazioni di query può essere più difficile da prevedere per i motivi seguenti:

  • Se il sistema supporta query definite dall'utente, è necessario eseguire il mapping delle query in ingresso alle query rappresentative per determinare il costo. Questo processo può assumere diverse forme:
    • Potrebbe essere possibile abbinare esattamente le query. Se non esiste una corrispondenza diretta, potrebbe essere necessario trovare la query rappresentativa più vicina.
    • È possibile che sia possibile calcolare un costo in base alle caratteristiche della query. Ad esempio, è possibile che ogni clausola della query abbia un certo costo o che una proprietà indicizzata costi "x", mentre una non indicizzata costi "y" e così via.
  • Il numero di risultati può variare e, a meno che non si disponga di statistiche, non è possibile stimare l'impatto delle UR dal payload di ritorno.

È probabile che non esista un singolo costo delle operazioni di query, ma piuttosto una funzione che valuti la query e ne calcoli un costo. Se si usa l'API per NoSQL, è possibile valutare il costo effettivo dell'operazione e determinare l'accuratezza della stima (l'ottimizzazione di questa stima potrebbe verificarsi automaticamente all'interno del codice).

Gestione degli errori temporanei

L'applicazione dovrà comunque gestire gli errori temporanei anche se si implementa un meccanismo di limitazione della velocità per i motivi seguenti:

  • Il costo effettivo di una richiesta potrebbe essere diverso dal costo previsto.
  • Gli errori temporanei possono verificarsi per motivi diversi da TooManyRequests.

Tuttavia, l'implementazione corretta di un meccanismo di limitazione della velocità nell'applicazione ridurrà notevolmente il numero di errori temporanei.

Anche se questo articolo descrive il coordinamento lato client e l'invio in batch dei carichi di lavoro, esistono altre tecniche da usare per gestire la velocità effettiva complessiva del sistema.

Autoscaling

La velocità effettiva ottenuta con scalabilità automatica in Azure Cosmos DB consente di dimensionare la velocità effettiva (UR/sec) del database o del contenitore automaticamente e immediatamente. La velocità effettiva viene dimensionata in base all'utilizzo, senza influire su disponibilità, latenza, velocità effettiva o prestazioni del carico di lavoro.

La velocità effettiva ottenuta con scalabilità automatica è particolarmente adatta per i carichi di lavoro critici con modelli di traffico variabili o imprevedibili e che richiedono contratti di servizio per prestazioni elevate e scalabilità.

Per altre informazioni sulla scalabilità automatica, vedere Creare contenitori e database di Azure Cosmos DB con velocità effettiva basata su scalabilità automatica.

Queue-Based modello di livellamento del carico

È possibile usare una coda che funga da buffer tra un client e Azure Cosmos DB per alleggerire i sovraccarichi intermittenti che possono causare un errore del servizio o il timeout dell'attività.

Questo modello è utile per qualsiasi applicazione che usi i servizi soggetti a sovraccarico. Tuttavia, questo modello non è utile se l'applicazione attende una risposta dal servizio con una latenza minima.

Questo modello è spesso adatto alle operazioni di inserimento.

Per altre informazioni su questo modello, vedere Modello di livellamento del carico basato su coda.

modelloCache-Aside

È possibile caricare i dati su richiesta in una cache anziché eseguire query in Azure Cosmos DB ogni volta. Usando una cache è possibile migliorare le prestazioni, nonché garantire coerenza tra i dati memorizzati nella cache e quelli presenti nell'archivio dati sottostante.

Per altre informazioni, vedere: Modello cache-aside.

Modello di vista materializzata

È possibile prepopolare le viste in altre raccolte dopo aver archiviato i dati in Azure Cosmos DB quando i dati non sono idealmente formattati per le operazioni di query necessarie. In questo modo è possibile garantire l'efficienza delle attività di query ed estrazione dei dati e migliorare le prestazioni dell'applicazione.

Per altre informazioni, vedere il Modello di vista materializzata.