Sugerencias de rendimiento para la versión 4 del SDK de Java de Azure Cosmos DB

SE APLICA A: NoSQL

Importante

Las sugerencias de rendimiento de este artículo son solo para la versión 4 del SDK de Java de Azure Cosmos DB. Consulte las notas de la versión de la versión 4 del SDK de Java de Azure Cosmos DB, el repositorio de Maven y la guía de solución de problemas de la versión 4 del SDK de Java de Azure Cosmos DB para obtener más información. Si en la actualidad usa una versión anterior a la 4, vea la guía Migración a la versión 4 del SDK de Java de Azure Cosmos DB a fin de obtener ayuda para actualizar a la versión 4.

Azure Cosmos DB es una base de datos distribuida rápida y flexible que se escala sin problemas con una latencia y un rendimiento garantizados. No es necesario realizar cambios de arquitectura importantes ni escribir código complejo para escalar la base de datos con Azure Cosmos DB. Escalar y reducir verticalmente es tan sencillo como realizar una única llamada API o una llamada al método SDK. Sin embargo, como el acceso a Azure Cosmos DB se realiza mediante llamadas de red, puede realizar optimizaciones en el lado cliente para conseguir un rendimiento máximo al usar la versión 4 del SDK de Java de Azure Cosmos DB.

Por lo tanto, si se pregunta "¿Cómo puedo mejorar el rendimiento de mi base de datos?", considere las siguientes opciones:

Redes

  • Colocación de los clientes en la misma región de Azure para aumentar el rendimiento

Cuando sea posible, coloque las aplicaciones que llaman a Azure Cosmos DB en la misma región que la base de datos de Azure Cosmos DB. Para obtener una comparación aproximada, las llamadas a Azure Cosmos DB en la misma región se realizan en menos de 1 o 2 ms, pero la latencia entre las costas este y oeste de Estados Unidos es >50 ms. Esta latencia podría variar de una solicitud a otra, según la ruta tomada por la solicitud cuando pasa del cliente al límite del centro de datos de Azure. Para conseguir la menor latencia posible, asegúrese de que la aplicación que llama se encuentra en la misma región de Azure que el punto de conexión de Azure Cosmos DB aprovisionado. Para obtener una lista de regiones disponibles, consulte Regiones de Azure.

Ilustración de la directiva de conexión de Azure Cosmos DB

Una aplicación que interactúa con una cuenta de Azure Cosmos DB de varias regiones debe configurar ubicaciones preferidas para asegurarse de que las solicitudes vayan a una región colocalizada.

Habilitación de redes aceleradas para reducir la latencia y la vibración de la CPU

Se recomienda seguir las instrucciones para habilitar Redes Aceleradas en su máquina virtual Windows (seleccione para obtener instrucciones) o Linux (seleccione para obtener instrucciones) de Azure, con el fin de maximizar el rendimiento (reducir la latencia y la fluctuación de la CPU).

Sin las redes aceleradas, la E/S que pasa entre su máquina virtual de Azure y otros recursos de Azure puede enrutarse a través de un host y un conmutador virtual situado entre la máquina virtual y su tarjeta de red. Si el host y el conmutador virtual están alineados en la ruta de datos no solo se aumenta la latencia y la vibración en el canal de comunicación, sino que también se roban ciclos de la CPU de la máquina virtual. Con las redes aceleradas, la máquina virtual interactúa directamente con la NIC sin intermediarios. Todos los detalles de la directiva de red se controlan en el hardware de la NIC, pasando el host y el conmutador virtual. Por lo general, al habilitar las redes aceleradas puede esperar una menor latencia y un mayor rendimiento, así como una latencia más uniforme y una disminución del uso de la CPU.

Limitaciones: las redes aceleradas deben ser compatibles con el sistema operativo de la máquina virtual y solo se pueden habilitar cuando la máquina virtual se ha detenido y se ha desasignado. No se puede implementar la máquina virtual con Azure Resource Manager. App Service no tiene habilitada la red acelerada.

Para más información, consulte las instrucciones de Windows y Linux.

Ajuste de la configuración de conexión directa y de puerta de enlace

Para optimizar las configuraciones de conexión de modo directo y de puerta de enlace, consulte cómo optimizar las configuraciones de conexión para el SDK de Java v4.

Uso del SDK

  • Instalación del SDK más reciente

Los SDK de Azure Cosmos DB se mejoran constantemente para proporcionar el mejor rendimiento. Para determinar las mejoras más recientes del SDK, visite el SDK de Azure Cosmos DB.

  • Uso de un cliente de Azure Cosmos DB singleton para aumentar la duración de la aplicación

Cada instancia de Azure Cosmos DB está protegida frente a amenazas y realiza de manera eficiente la administración de las conexiones y el almacenamiento en caché de las direcciones. Para permitir una administración de conexiones eficaz y un mejor rendimiento por parte del cliente de Azure Cosmos DB, se recomienda usar una única instancia del cliente de Azure Cosmos DB durante la vigencia de la aplicación.

  • Uso del nivel de coherencia más bajo necesario para la aplicación

Cuando se crea una instancia de CosmosClient, la coherencia predeterminada utilizada si no se establece explícitamente es Sesión. Si la lógica de la aplicación no requiere coherencia en el nivel Sesión, establezca el valor de Coherencia en Ocasional. Nota: Se recomienda usar al menos la coherencia en el nivel Sesión en las aplicaciones que emplean el procesador de fuente de cambios de Azure Cosmos DB.

  • Uso de la API asincrónica para maximizar el rendimiento aprovisionado

La versión 4 del SDK de Java de Azure Cosmos DB agrupa dos API: sincrónica y asincrónica. En términos generales, la API asincrónica implementa la funcionalidad del SDK, mientras que la API sincrónica es un contenedor fino que realiza llamadas de bloqueo a la API asincrónica. Esto se opone a la anterior versión 2 del SDK de Java asincrónico de Azure Cosmos DB y a la anterior versión 2 del SDK de Java sincrónico de Azure Cosmos DB, que era solo de sincronización y tenía una implementación completamente independiente.

La elección de la API se determina durante la inicialización del cliente; CosmosAsyncClient admite la API asincrónica, mientras que CosmosClient admite la API sincrónica.

La API asincrónica implementa E/S sin bloqueo y es la opción óptima si el objetivo es maximizar el rendimiento al emitir solicitudes para Azure Cosmos DB.

El uso de la API sincrónica puede ser la opción correcta si quiere o necesita una API que bloquee la respuesta a cada solicitud, o si la operación sincrónica es el paradigma dominante en la aplicación. Por ejemplo, es posible que desee la API sincrónica al guardar los datos en Azure Cosmos DB en una aplicación de microservicios, siempre que el rendimiento no sea crítico.

Tenga en cuenta que el rendimiento de la API sincrónica se degrada con un tiempo de respuesta de solicitud creciente, mientras que la API asincrónica puede saturar todas las capacidades de ancho de banda del hardware.

La colocación geográfica puede brindarle un rendimiento más alto y más uniforme cuando se usa la API sincrónica (consulte Colocación de los clientes en la misma región de Azure para aumentar el rendimiento) pero aún no se espera que exceda el rendimiento que puede alcanzar la API asincrónica.

Algunos usuarios también pueden no estar familiarizados con Project Reactor, el marco de Reactive Streams utilizado para implementar la API asincrónica de la versión 4 del SDK de Java de Azure Cosmos DB. Si esto supone un problema, le recomendamos que lea nuestra guía de Reactor Pattern introductoria y, después, eche un vistazo a esta introducción a la programación reactiva con el fin de familiarizarse con estos conceptos. Si ya ha usado Azure Cosmos DB con una interfaz asincrónica y el SDK que usó era la versión 2 del SDK de Java asincrónico de Azure Cosmos DB, es posible que conozca ReactiveX/RxJava pero no esté al corriente de los cambios en Project Reactor. En ese caso, eche un vistazo a nuestra guía comparativa de Reactor y RxJava para familiarizarse con ello.

En los fragmentos de código siguientes se muestra cómo inicializar el cliente de Azure Cosmos DB para la operación de API asincrónica o API sincrónica, respectivamente:

SDK para Java V4 (Maven com.azure::azure-cosmos) API asincrónica


CosmosAsyncClient client = new CosmosClientBuilder()
        .endpoint(HOSTNAME)
        .key(MASTERKEY)
        .consistencyLevel(CONSISTENCY)
        .buildAsyncClient();

  • Escalado horizontal de la carga de trabajo de cliente

Si va a realizar pruebas en niveles de alto rendimiento, la aplicación cliente puede crear cuellos de botella, debido a que la máquina limita el uso de CPU o de la red. Si llega a este punto, puede seguir insertando la cuenta de Azure Cosmos DB mediante la escala horizontal de las aplicaciones cliente en varios servidores.

Una buena regla general es no superar un uso de la CPU >50 % en cualquier servidor para mantener baja la latencia.

  • Use el programador adecuado (evite el robo de subprocesos de E/S Eventloop de Netty)

La funcionalidad asincrónica del SDK de Java de Azure Cosmos DB se basa en la E/S sin bloqueo de netty. El SDK usa un número fijo de subprocesos de E/S Eventloop de Netty (tantos como núcleos de CPU tenga su máquina) para ejecutar operaciones de E/S. El elemento Flux que devuelve la API emite el resultado en uno de los subprocesos de E/S Eventloop de Netty compartidos. Así que es importante no bloquear dichos subprocesos. Realizar trabajos que hacen un uso elevado de CPU o bloquear operaciones en el subproceso de E/S Eventloop de Netty puede provocar un interbloqueo o reducir el rendimiento del SDK de manera considerable.

Por ejemplo, el código siguiente ejecuta un trabajo que hace un uso elevado de CPU en el subproceso de E/S Eventloop de Netty:


Mono<CosmosItemResponse<CustomPOJO>> createItemPub = asyncContainer.createItem(item);
createItemPub.subscribe(
        itemResponse -> {
            //this is executed on eventloop IO netty thread.
            //the eventloop thread is shared and is meant to return back quickly.
            //
            // DON'T do this on eventloop IO netty thread.
            veryCpuIntensiveWork();
        });


Tras recibir el resultado, debe evitar realizar cualquier trabajo que haga un uso intensivo de CPU sobre un subproceso de Netty de E/S del bucle de eventos. En su lugar, proporcione su propio programador a fin de suministrar su propio subproceso para ejecutar su trabajo, como se muestra a continuación (requiere import reactor.core.scheduler.Schedulers).


Mono<CosmosItemResponse<CustomPOJO>> createItemPub = asyncContainer.createItem(item);
createItemPub
        .publishOn(Schedulers.parallel())
        .subscribe(
                itemResponse -> {
                    //this is now executed on reactor scheduler's parallel thread.
                    //reactor scheduler's parallel thread is meant for CPU intensive work.
                    veryCpuIntensiveWork();
                });

Según el tipo del trabajo, debe usar el programador de Reactor existente adecuado para su trabajo. Obtenga más información aquí Schedulers.

Para comprender aún más el modelo de subprocesos y programación de Project Reactor, consulte esta entrada de blog de Project Reactor.

Para más información sobre la versión 4 del SDK de Java de Azure Cosmos DB, consulte el repositorio único del directorio Azure Cosmos DB del SDK de Azure para Java en GitHub.

  • Optimización de la configuración de registro en la aplicación

Por varias razones, debe agregar el registro en un subproceso que genera un alto rendimiento de solicitudes, Si su objetivo es saturar completamente el rendimiento aprovisionado de un contenedor con las solicitudes generadas por este subproceso, las optimizaciones de registro pueden mejorar considerablemente el rendimiento.

  • Configuración de un registrador asincrónico

La latencia de un registrador sincrónico necesariamente tiene en cuenta el cálculo de la latencia general de su subproceso generador de solicitudes. Se recomienda un registrador asincrónico como log4j2 para desacoplar la sobrecarga de registro de los subprocesos de la aplicación de alto rendimiento.

  • Deshabilitación del registro de Netty

El registro de la biblioteca Netty es muy activo y debe desactivarse (puede que no baste con suprimir el inicio de sesión en la configuración) para evitar costos adicionales de CPU. Si no está en modo de depuración, deshabilite por completo el registro de Netty. De modo que, si va a usar Log4j para eliminar los costos de CPU adicionales que se producen debido al uso de org.apache.log4j.Category.callAppenders() desde Netty, agregue la siguiente línea al código base:

org.apache.log4j.Logger.getLogger("io.netty").setLevel(org.apache.log4j.Level.OFF);
  • Límite de recursos de archivos abiertos del sistema operativo

Algunos sistemas Linux (como Red Hat) tienen un límite superior sobre el número de archivos abiertos y, por tanto, sobre el número total de conexiones. Ejecute el siguiente código para ver los límites actuales:

ulimit -a

El número de archivos abiertos (nofile) debe ofrecer el espacio suficiente para el tamaño configurado del grupo de conexiones y otros archivos abiertos por el sistema operativo. Se puede modificar para permitir un tamaño mayor del grupo de conexiones.

Abra el archivo limits.conf:

vim /etc/security/limits.conf

Agregue o modifique las siguientes líneas:

* - nofile 100000
  • Especificación de la clave de partición en escrituras puntuales

Para mejorar el rendimiento de las escrituras puntuales, especifique la clave de partición del elemento en la llamada API de escritura puntual, como se muestra a continuación:

SDK para Java V4 (Maven com.azure::azure-cosmos) API asincrónica

asyncContainer.createItem(item,new PartitionKey(pk),new CosmosItemRequestOptions()).block();

en lugar de proporcionar solo la instancia del elemento, como se muestra a continuación:

SDK para Java V4 (Maven com.azure::azure-cosmos) API asincrónica

asyncContainer.createItem(item).block();

Esta última es compatible, pero agregará latencia a la aplicación; el SDK debe analizar el elemento y extraer la clave de partición.

Operaciones de consulta

Para las operaciones de consulta, consulte las sugerencias de rendimiento para las consultas.

Directiva de indexación

  • Exclusión de rutas de acceso sin utilizar de la indexación para acelerar las escrituras

La directiva de indexación de Azure Cosmos DB le permite especificar las rutas de acceso de documentos que se incluirán en la indexación o se excluirán de esta mediante el aprovechamiento de las rutas de acceso de indexación (setIncludedPaths y setExcludedPaths). El uso de rutas de acceso de indexación puede ofrecer un rendimiento de escritura mejorado y un almacenamiento de índices reducido en escenarios en los que los patrones de consulta se conocen de antemano, dado que los costos de indexación están directamente correlacionados con el número de rutas de acceso únicas indexadas. Por ejemplo, en el código siguiente se muestra cómo incluir y excluir secciones completas de los documentos (que también se conocen como subárbol) de la indexación mediante el comodín "*".


CosmosContainerProperties containerProperties = new CosmosContainerProperties(containerName, "/lastName");

// Custom indexing policy
IndexingPolicy indexingPolicy = new IndexingPolicy();
indexingPolicy.setIndexingMode(IndexingMode.CONSISTENT);

// Included paths
List<IncludedPath> includedPaths = new ArrayList<>();
includedPaths.add(new IncludedPath("/*"));
indexingPolicy.setIncludedPaths(includedPaths);

// Excluded paths
List<ExcludedPath> excludedPaths = new ArrayList<>();
excludedPaths.add(new ExcludedPath("/name/*"));
indexingPolicy.setExcludedPaths(excludedPaths);

containerProperties.setIndexingPolicy(indexingPolicy);

ThroughputProperties throughputProperties = ThroughputProperties.createManualThroughput(400);

database.createContainerIfNotExists(containerProperties, throughputProperties);
CosmosAsyncContainer containerIfNotExists = database.getContainer(containerName);

Para más información, consulte Directivas de indexación de Azure Cosmos DB.

Throughput

  • Medición y optimización del uso menor de unidades de solicitud por segundo

Azure Cosmos DB ofrece un amplio conjunto de operaciones de base de datos, incluidas consultas relacionales y jerárquicas con funciones definidas por el usuario, procedimientos almacenados y desencadenadores. Todo funciona con los documentos dentro de una colección de base de datos. El costo asociado a cada una de estas operaciones variará en función de la CPU, la E/S y la memoria necesarias para completar la operación. En lugar de administrar y pensar sobre los recursos de hardware, puede pensar en una unidad de solicitud (RU) como una medida única para los recursos necesarios para realizar varias operaciones de la base de datos y dar servicio a una solicitud de la aplicación.

El rendimiento se aprovisiona en función del número de unidades de solicitud establecido para cada contenedor. El consumo de la unidad de solicitud se evalúa como frecuencia por segundo. Las aplicaciones que superan la frecuencia de unidad de solicitud aprovisionada para su contenedor están limitadas hasta que la frecuencia cae por debajo del nivel aprovisionado del contenedor. Si la aplicación requiere un mayor nivel de rendimiento, puede aumentar el rendimiento mediante el aprovisionamiento de unidades de solicitud adicionales.

La complejidad de una consulta afecta a la cantidad de unidades de solicitud consumidas para una operación. El número de predicados, la naturaleza de los predicados, el número de UDF y el tamaño del conjunto de datos de origen influyen en el costo de operaciones de consulta.

Para medir la sobrecarga de cualquier operación (crear, actualizar o eliminar), inspeccione el encabezado x-ms-request-charge para medir el número de unidades de solicitud usadas por estas operaciones. También puede examinar la propiedad RequestCharge equivalente en ResourceResponse<T> or FeedResponse<T>.

SDK para Java V4 (Maven com.azure::azure-cosmos) API asincrónica

CosmosItemResponse<CustomPOJO> response = asyncContainer.createItem(item).block();

response.getRequestCharge();

El cargo de solicitud devuelto en este encabezado es una fracción de la capacidad de proceso aprovisionada. Por ejemplo, si tiene 2000 RU/segundo aprovisionadas, y si la consulta anterior devuelve 1000 documentos de 1 KB, el costo de la operación será 1000. Por lo tanto, al cabo de un segundo, el servidor atenderá solo dos de estas solicitudes antes de limitar la velocidad de las solicitudes posteriores. Para más información, consulte Unidades de solicitud y la calculadora de unidades de solicitud.

  • Administración de la limitación de velocidad y la tasa de solicitudes demasiado grande

Cuando un cliente intenta superar la capacidad de proceso reservada para una cuenta, no habrá ninguna degradación del rendimiento en el servidor y no se utilizará ninguna capacidad de proceso más allá del nivel reservado. El servidor finalizará de forma preventiva la solicitud con RequestRateTooLarge (código de estado HTTP 429) y devolverá el encabezado x-ms-retry-after-ms para indicar la cantidad de tiempo, en milisegundos, que el usuario debe esperar antes de volver a intentar realizar la solicitud.

HTTP Status 429,
Status Line: RequestRateTooLarge
x-ms-retry-after-ms :100

Los SDK capturan implícitamente esta respuesta, respetan el encabezado retry-after especificado por el servidor y reintentan la solicitud. A menos que varios clientes obtengan acceso a la cuenta al mismo tiempo, el siguiente reintento se realizará correctamente.

Si tiene más de un cliente que opera de forma acumulativa y de manera frecuente por encima de la tasa de solicitudes, puede que no baste con el número de reintentos predeterminado, establecido actualmente en 9 de manera interna por el cliente. En este caso, el cliente producirá CosmosClientException con el código de estado 429 para la aplicación. El número predeterminado de reintentos puede cambiarse con setMaxRetryAttemptsOnThrottledRequests() en la instancia ThrottlingRetryOptions. De forma predeterminada, la excepción CosmosClientException con el código de estado 429 se devuelve tras un tiempo de espera acumulativo de 30 segundos si la solicitud sigue funcionando por encima de la tasa de solicitudes. Esto sucede incluso cuando el número de reintentos actual es inferior al número de reintentos máximo de 9, el valor predeterminado, o un valor definido por el usuario.

Aunque el comportamiento de reintento automático ayuda a mejorar la resistencia y la usabilidad en la mayoría de las aplicaciones, podría no resultar ventajoso al realizar comparativas de rendimiento, en especial al medir la latencia. La latencia observada del cliente aumentará si el experimento llega a la limitación del servidor y hace que el SDK del cliente realice reintentos de forma silenciosa. Para evitar aumentos de latencia durante los experimentos de rendimiento, mida el gasto devuelto por cada operación y asegúrese de que las solicitudes funcionan por debajo de la tasa de solicitudes observada. Para más información, consulte Unidades de solicitud.

  • Diseño de documentos más pequeños para un mayor rendimiento

El gasto de solicitud (es decir, el costo de procesamiento de solicitudes) de una operación dada está directamente correlacionado con el tamaño del documento. Las operaciones con documentos grandes cuestan más que las operaciones con documentos pequeños. Idealmente, diseñe la aplicación y los flujos de trabajo para que el tamaño del elemento sea ~1 KB, o un orden o una magnitud similares. En el caso de las aplicaciones dependientes de la latencia, deben evitarse los elementos de gran tamaño, ya que los documentos de varios MB ralentizarán la aplicación.

Pasos siguientes

Para más información sobre cómo diseñar la aplicación para escalarla y obtener un alto rendimiento, consulte Partición y escalado en Azure Cosmos DB.

¿Intenta planear la capacidad de una migración a Azure Cosmos DB? Para ello, puede usar información sobre el clúster de bases de datos existente.