Optimización del Rendimiento en Azure Cosmos DB: Mejores Prácticas y Consejos

Cuando trabajamos con una base de datos, la optimización es crucial y clave en términos de rendimiento y eficiencia de la aplicación. Del mismo modo, en Azure Cosmos DB, la optimización es crucial para maximizar la eficiencia, minimizar costos y garantizar que tu aplicación escale de manera efectiva. A continuación se presentan algunas de las mejores prácticas con ejemplos de codificación para optimizar el rendimiento en Azure Cosmos DB.

1. Selección de la Clave de Partición Correcta

Elegir una clave de partición apropiada es vital para bases de datos distribuidas como Cosmos DB. Una buena clave de partición asegura que los datos estén distribuidos uniformemente entre las particiones, reduciendo los puntos calientes y mejorando el rendimiento.

La selección de una clave de partición es simple pero muy importante en la fase de diseño en Azure Cosmos DB. Una vez que hemos seleccionado la clave de partición, no es posible cambiarla en su lugar.

Mejor Práctica

  • Selecciona una clave de partición con alta cardinalidad (muchos valores únicos).
  • Asegúrate de que distribuye lecturas y escrituras de manera uniforme.
  • Mantén los datos relacionados juntos para minimizar las consultas entre particiones.

Ejemplo: Creando un Contenedor con una Clave de Partición Óptima

C#

 

var database = await cosmosClient.CreateDatabaseIfNotExistsAsync("YourDatabase");
var containerProperties = new ContainerProperties
{
    Id = "myContainer",
    PartitionKeyPath = "/customerId"  // Partition key selected to ensure balanced distribution
}; 

// Crear el contenedor con un rendimiento provisionado de 400 RU/s
var container = await database.CreateContainerIfNotExistsAsync(containerProperties, throughput: 400);

2. La indexación adecuadaenAzure Cosmos DB

se aplica a todas las propiedades de forma predeterminada, lo que puede ser beneficioso pero puede resultar en un aumento de los costos de almacenamiento y RU/s. Para mejorar el rendimiento de las consultas y minimizar los gastos, considere personalizar la política de indexación. Cosmos DB admite tres tipos de índices: Índices de rango, Índices espaciales e Índices compuestos. Utilice el tipo adecuado sabiamente.

Mejor práctica

  • Excluya campos innecesarios de la indexación.
  • Utilice índices compuestos para consultas de varios campos.

Ejemplo: Política de indexación personalizada

C#

 

{
    "indexingPolicy": {
        "automatic": true,
        "indexingMode": "consistent",  // Can use 'none' or 'lazy' to reduce write costs
        "includedPaths": [
            {
                "path": "/orderDate/?",  // Only index specific fields like orderDate
                "indexes": [
                    {
                        "kind": "Range",
                        "dataType": "Number"
                    }
                ]
            }
        ],
        "excludedPaths": [
            {
                "path": "/largeDataField/*"  // Exclude large fields not used in queries
            }
        ]
    }
}

Ejemplo: Agregar un índice compuesto para consultas optimizadas

C#

 

{
    "indexingPolicy": {
        "compositeIndexes": [
            [
                { "path": "/lastName", "order": "ascending" },
                { "path": "/firstName", "order": "ascending" }
            ]
        ]
    }
}

Puede leer más sobre los tipos de indexación aquí.

3. Optimizar consultas

La consulta eficiente es crucial para minimizar las unidades de solicitud (RU/s) y mejorar el rendimiento en Azure Cosmos DB. El costo de RU/s depende de la complejidad y tamaño de la consulta.

Utilizar los ejecutores por lotes puede reducir aún más los costos al disminuir las RUs consumidas por operación. Esta optimización ayuda a gestionar eficazmente el uso de RUs y reduce sus gastos totales de Cosmos DB.

Mejores Prácticas

  • Utilice consultas SELECT en cantidades limitadas, recuperando solo las propiedades necesarias.
  • Avoid queries entre particiones proporcionando la clave de partición en su consulta.
  • Utilice filtros en campos indexados para reducir los costos de la consulta.

Ejemplo: Obtener Registro de Cliente

C#

 

var query = new QueryDefinition("SELECT c.firstName, c.lastName FROM Customers c WHERE c.customerId = @customerId")
    .WithParameter("@customerId", "12345");

var iterator = container.GetItemQueryIterator<Customer>(query, requestOptions: new QueryRequestOptions
{
    PartitionKey = new PartitionKey("12345")  // Provide partition key to avoid cross-partition query
});

while (iterator.HasMoreResults)
{
    var response = await iterator.ReadNextAsync();
    foreach (var customer in response)
    {
        Console.WriteLine($"{customer.firstName} {customer.lastName}");
    }
}

4. Ajuste de Niveles de Consistencia

Los niveles de consistencia definen modos operativos específicos diseñados para cumplir garantías relacionadas con la velocidad. Existen cinco niveles de consistencia (Fuerte, Acotado, Sesión, Prefijo Consistente y Eventual) disponibles en Cosmos DB. Cada nivel de consistencia afecta la latencia, disponibilidad y rendimiento.

Mejores Prácticas

  • Utilice consistencia de sesión para la mayoría de los escenarios para equilibrar rendimiento y consistencia de datos.
  • La consistencia fuerte garantiza la consistencia de datos pero aumenta RU/s y latencia.

Ejemplo: Establecer Nivel de Consistencia

C#

 

var cosmosClient = new CosmosClient(
    "",
    "",
    new CosmosClientOptions
    {
        // Establecer consistencia en "Sesión" para un rendimiento equilibrado
		ConsistencyLevel = ConsistencyLevel.Session      
});

Lea más sobre el nivel de consistencia aquí.

5. Usa el rendimiento provisionado (RU/s) y la escala automática de manera inteligente

La provisión de rendimiento es un factor clave para lograr eficiencia de costos y un rendimiento óptimo en Azure Cosmos DB. El servicio te permite configurar el rendimiento de dos formas:

  • RU/s fijo: Un nivel predefinido y constante de Unidades de solicitud por segundo (RU/s), adecuado para cargas de trabajo con demandas de rendimiento consistentes.
  • Escala automática: Una opción dinámica que ajusta automáticamente el rendimiento en función de las fluctuaciones de la carga de trabajo, brindando escalabilidad y evitando la sobredimensionamiento durante períodos de baja actividad.

Elegir el modelo de rendimiento adecuado ayuda a equilibrar las necesidades de rendimiento con la gestión de costos de manera efectiva.

Mejor práctica

  • Para cargas de trabajo predecibles, prové el rendimiento manualmente.
  • Usa la escala automática para cargas de trabajo impredecibles o con picos de actividad.

Ejemplo: Provisión de rendimiento con Escala automática

C#

 

var throughputProperties = ThroughputProperties.CreateAutoscaleThroughput(maxThroughput: 4000);  // Autoscale up to 4000 RU/s 
var container = await database.CreateContainerIfNotExistsAsync(new ContainerProperties
{
	Id = "autoscaleContainer",
	PartitionKeyPath = "/userId"
}, throughputProperties);

Ejemplo: Configuración manual de RU/s fijo para cargas de trabajo estables 

C#

 

var container = await database.CreateContainerIfNotExistsAsync(new ContainerProperties
{
    Id = "manualThroughputContainer",
    PartitionKeyPath = "/departmentId"
}, throughput: 1000);  // Fixed 1000 RU/s

6. Aprovecha el cambio en el feed para un procesamiento eficiente en tiempo real

El feed de cambios permite el procesamiento en tiempo real y basado en eventos al capturar automáticamente los cambios en la base de datos, eliminando la necesidad de sondear. Esto reduce la sobrecarga de consultas y mejora la eficiencia.

Mejores Prácticas

  • Utilice el feed de cambios para escenarios donde los cambios de datos en tiempo real necesitan ser procesados (por ejemplo, análisis en tiempo real, notificaciones, alertas).

Ejemplo: Lectura desde el Feed de Cambios

C#

 

var iterator = container.GetChangeFeedIterator(
ChangeFeedStartFrom.Beginning(),
ChangeFeedMode.Incremental);
while (iterator.HasMoreResults)
{
    var changes = await iterator.ReadNextAsync();
    foreach (var change in changes)
    {
        Console.WriteLine($"Detected change: {change.Id}");
        // Procesar el cambio (por ejemplo, desencadenar un evento, actualizar caché)
    }
}

7. Utilización de Tiempo de Vida (TTL) para la Expiración Automática de Datos

Si tiene datos que solo son relevantes por un tiempo limitado, como registros o datos de sesión, habilitar el Tiempo de Vida (TTL) en Azure Cosmos DB puede ayudar a gestionar los costos de almacenamiento. El TTL elimina automáticamente los datos caducados después del período de retención especificado, eliminando la necesidad de limpieza manual de datos. Este enfoque no solo reduce la cantidad de datos almacenados, sino que también asegura que su base de datos esté optimizada para ser rentable al eliminar información obsoleta o innecesaria.

Mejores Prácticas

  • Establezca TTL para contenedores donde los datos deben expirar automáticamente para reducir los costos de almacenamiento.

Ejemplo: Configuración de Tiempo de Vida (TTL) para Datos Expirados

C#

 

{
    "id": "sessionDataContainer",
    "partitionKey": { "paths": ["/sessionId"] },
    "defaultTtl": 3600  // 1 hour (3600 seconds)
}

En Cosmos DB, el valor máximo de Tiempo de Vida (TTL) que se puede establecer es de 365 días (1 año). Esto significa que los datos pueden ser eliminados automáticamente después de que expire dentro de un año desde su creación o última modificación, dependiendo de cómo se configure el TTL.

8. Avoid Cross-Partition Queries

Las consultas entre particiones pueden aumentar significativamente los RU/s y la latencia. Para evitar esto:

Mejor Práctica

  • Siempre incluya la clave de partición en sus consultas.
  • Diseñe su estrategia de particionado para minimizar el acceso entre particiones.

Ejemplo: Consulta Con Clave de Partición para Evitar Consultas Entre Particiones

C#

 

var query = new QueryDefinition("SELECT * FROM Orders o WHERE o.customerId = @customerId")
    .WithParameter("@customerId", "12345"); 

var resultSetIterator = container.GetItemQueryIterator<Order>(query, requestOptions: new QueryRequestOptions
{
    PartitionKey = new PartitionKey("12345")
});

while (resultSetIterator.HasMoreResults)
{
    var response = await resultSetIterator.ReadNextAsync();
    foreach (var order in response)
    {
        Console.WriteLine($"Order ID: {order.Id}");
    }
}

Conclusión

Estos consejos son muy efectivos durante el desarrollo. Al implementar una estrategia de particionado efectiva, personalizar políticas de indexación, optimizar consultas, ajustar niveles de consistencia y seleccionar los modelos adecuados de aprovisionamiento de rendimiento, puede mejorar en gran medida el rendimiento y la eficiencia de su implementación de Azure Cosmos DB. Estas optimizaciones no solo mejoran la escalabilidad, sino que también ayudan a gestionar costos al tiempo que proporcionan una experiencia de base de datos de alto rendimiento.

Source:
https://dzone.com/articles/optimizing-performance-in-azure-cosmos-db