Compartir a través de


Sugerencias de rendimiento para el SDK de Java asincrónico de Azure Cosmos DB v2

Importante

No se trata de la versión de SDK de Azure Cosmos DB para Java más reciente. Debe actualizar el proyecto al SDK de Java v4 de Azure Cosmos DB y, a continuación, leer la guía de sugerencias de rendimiento del SDK de Java v4 de Azure Cosmos DB. Siga las instrucciones de la guía Migración al SDK de Java de Azure Cosmos DB v4 y la guía de Reactor frente a RxJava para actualizar.

Las sugerencias de rendimiento de este artículo son solo para el SDK de Java asincrónico de Azure Cosmos DB v2. Consulte las notas de la versión del SDK de Java asincrónico de Azure Cosmos DB v2, el repositorio de Maven y la guía de solución de problemas del SDK de Java asincrónico de Azure Cosmos DB v2 para más información.

Importante

El 31 de agosto de 2024 se retirará el SDK de Java asincrónico de Azure Cosmos DB v2.x; el SDK y todas las aplicaciones que usan el SDK seguirán funcionando; Azure Cosmos DB simplemente dejará de proporcionar más mantenimiento y soporte técnico para este SDK. Se recomienda seguir las instrucciones anteriores para migrar al SDK de Java v4 de Azure Cosmos DB.

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, dado que se accede a Azure Cosmos DB a través de llamadas de red, hay optimizaciones del lado cliente que puede realizar para lograr un rendimiento máximo al usar el SDK de Java asincrónico de Azure Cosmos DB v2.

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

Networking

  • Modo de conexión: Uso del modo directo

    La forma en que un cliente se conecta a Azure Cosmos DB tiene implicaciones importantes en el rendimiento, especialmente en términos de latencia del lado cliente. ConnectionMode es una configuración clave disponible para configurar la Política de Conexión del cliente. En el caso del SDK de Java asincrónico de Azure Cosmos DB v2, los dos ConnectionModes disponibles son:

    El modo de puerta de enlace es compatible con todas las plataformas del SDK y es la opción configurada de forma predeterminada. Si las aplicaciones se ejecutan dentro de una red corporativa con restricciones estrictas de firewall, el modo de puerta de enlace es la mejor opción, ya que usa el puerto HTTPS estándar y un único punto de conexión. Sin embargo, el equilibrio de rendimiento es que el modo de puerta de enlace implica un paso adicional en la red cada vez que los datos se leen o escriben en Azure Cosmos DB. Por este motivo, el modo Directo ofrece un mejor rendimiento debido a menos saltos de red.

    ConnectionMode se configura durante la construcción de la instancia de DocumentClient con el parámetro ConnectionPolicy.

SDK de Java asincrónico V2 (Maven com.microsoft.azure::azure-cosmosdb)

    public ConnectionPolicy getConnectionPolicy() {
        ConnectionPolicy policy = new ConnectionPolicy();
        policy.setConnectionMode(ConnectionMode.Direct);
        policy.setMaxPoolSize(1000);
        return policy;
    }

    ConnectionPolicy connectionPolicy = new ConnectionPolicy();
    DocumentClient client = new DocumentClient(HOST, MASTER_KEY, connectionPolicy, null);
  • 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

Uso del SDK

  • Instalación del SDK más reciente

    Los SDK de Azure Cosmos DB se mejoran constantemente para proporcionar el mejor rendimiento. Consulte las páginas de Notas de la versión v2 del SDK de Java asincrónico de Azure Cosmos DB para determinar el SDK más reciente y revisar las mejoras.

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

    Cada instancia de AsyncDocumentClient es segura para subprocesos y realiza una administración eficaz de la conexión y el almacenamiento en caché de direcciones. Para permitir una administración de conexiones eficaz y un mejor rendimiento por parte de AsyncDocumentClient, se recomienda usar una única instancia de AsyncDocumentClient por AppDomain durante la vigencia de la aplicación.

  • Ajuste de la Política de Conexión

    De forma predeterminada, las solicitudes del modo directo de Azure Cosmos DB se realizan a través de TCP cuando se usa el SDK de Java asincrónico de Azure Cosmos DB v2. Internamente, el SDK usa una arquitectura de modo directo especial para administrar dinámicamente los recursos de red y obtener el mejor rendimiento.

    En el SDK de Java asincrónico de Azure Cosmos DB v2, el modo directo es la mejor opción para mejorar el rendimiento de la base de datos con la mayoría de las cargas de trabajo.

    • Información general sobre el modo directo

    Ilustración de la arquitectura del modo directo

    La arquitectura del lado cliente empleada en modo directo permite el uso predecible de la red y el acceso multiplexado a las réplicas de Azure Cosmos DB. En el diagrama anterior se muestra cómo el modo directo enruta las solicitudes de cliente a las réplicas en el back-end de Azure Cosmos DB. La arquitectura del modo directo asigna hasta 10 canales en el lado cliente por réplica de base de datos. Un canal es una conexión TCP que está precedida por un búfer de solicitudes con una profundidad de 30 solicitudes. Los canales que pertenecen a una réplica se asignan dinámicamente a medida que lo necesite el Punto de Servicio de la réplica. Cuando el usuario emite una solicitud en modo directo, TransportClient enruta la solicitud al punto de conexión de servicio adecuado en función de la clave de partición. La cola de solicitudes almacena en búfer las solicitudes antes del punto de conexión de servicio.

    • Opciones de configuración de ConnectionPolicy para el modo directo

      Como primer paso, use las siguientes opciones de configuración recomendadas a continuación. Póngase en contacto con el equipo de Azure Cosmos DB si tiene problemas en este tema en particular.

      Si usa Azure Cosmos DB como base de datos de referencia (es decir, la base de datos se usa para muchas operaciones de lectura de punto y pocas operaciones de escritura), puede ser aceptable establecer idleEndpointTimeout en 0 (es decir, sin tiempo de espera).

      Opción de configuración Predeterminado
      bufferPageSize 8192
      connectionTimeout "PT1M"
      idleChannelTimeout (tiempo de espera del canal inactivo) "PT0S"
      idleEndpointTimeout "PT1M10S"
      maxBufferCapacity 8388608
      máxCanalesPorPuntoFinal 10
      solicitudesMáximasPorCanal 30
      recibirDetecciónDeColgadoTiempo "PT1M5S"
      requestExpiryInterval "PT5S"
      requestTimeout "PT1M"
      requestTimerResolution "PT0.5S"
      sendHangDetectionTime "PT10S"
      shutdownTimeout "PT15S"
  • Sugerencias de programación para el modo directo

    Revise el artículo Solución de problemas del SDK de Java asincrónico de Azure Cosmos DB v2 como línea base para resolver los problemas del SDK.

    Algunas sugerencias de programación importantes al usar el modo directo:

    • Usa multithreading en tu aplicación para una transferencia de datos TCP eficaz - Después de realizar una solicitud, tu aplicación debe suscribirse para recibir los datos en otro subproceso. Si no se hace, se provoca una operación "half-duplex" no deseada, y las solicitudes posteriores se bloquean a la espera de la respuesta de la solicitud anterior.

    • Llevar a cabo cargas de trabajo de proceso intensivo en un subproceso dedicado : por motivos similares a la sugerencia anterior, las operaciones como el procesamiento de datos complejos se colocan mejor en un subproceso independiente. Una solicitud que extrae datos de otro almacén de datos (por ejemplo, si el subproceso utiliza almacenes de datos de Azure Cosmos DB y Spark simultáneamente) puede experimentar una mayor latencia y se recomienda generar un subproceso adicional que espera una respuesta del otro almacén de datos.

    • Modelado de datos : el Acuerdo de Nivel de Servicio de Azure Cosmos DB supone que el tamaño del documento es inferior a 1 KB. La optimización del modelo de datos y la programación para favorecer el tamaño de documento más pequeño generalmente provocará una disminución de la latencia. Si va a necesitar almacenamiento y recuperación de documentos de más de 1 KB, el enfoque recomendado es que los documentos se vincule a los datos de Azure Blob Storage.

  • Optimización de consultas paralelas para colecciones con particiones

    El SDK de Java asincrónico de Azure Cosmos DB v2 admite consultas paralelas, lo que le permite consultar una colección con particiones en paralelo. Para obtener más información, consulte ejemplos de código relacionados con el trabajo con los SDK. Las consultas paralelas están diseñadas para mejorar la latencia y el rendimiento de las consultas respecto a sus homólogos en serie.

    • Ajuste de setMaxDegreeOfParallelism:

      Las consultas paralelas funcionan realizando consultas a múltiples particiones en paralelo. Sin embargo, los datos de una colección particionada individualmente se recuperan en serie con respecto a la consulta. Por lo tanto, use setMaxDegreeOfParallelism para establecer el número de particiones que tiene la máxima posibilidad de lograr la consulta más eficaz, siempre que todas las demás condiciones del sistema sigan siendo las mismas. Si no conoce el número de particiones, puede usar setMaxDegreeOfParallelism para establecer un número alto y el sistema elige el mínimo (número de particiones, entrada proporcionada por el usuario) como el grado máximo de paralelismo.

      Es importante tener en cuenta que las consultas paralelas producen las mejores ventajas si los datos se distribuyen uniformemente entre todas las particiones con respecto a la consulta. Si la colección con particiones se particiona de tal manera que todos o la mayoría de los datos devueltos por una consulta se concentran en algunas particiones (una partición en el peor de los casos), el rendimiento de la consulta se vería afectado por esas particiones.

    • Configuración de setMaxBufferedItemCount:

      La consulta paralela está diseñada para capturar previamente los resultados mientras el cliente procesa el lote de resultados actual. La captura previa ayuda a mejorar la latencia general de una consulta. setMaxBufferedItemCount limita el número de resultados precargados. Establecer setMaxBufferedItemCount en el número esperado de resultados devueltos (o un número superior) permite que la consulta reciba el máximo beneficio de la captura previa.

      La captura previa funciona de la misma manera independientemente del maxDegreeOfParallelism y hay un único búfer para los datos de todas las particiones.

  • Implementar el retroceso en los intervalos de getRetryAfterInMilliseconds

    Durante las pruebas de rendimiento, debe aumentar la carga hasta que se limite una pequeña tasa de solicitudes. Si se limita, la aplicación cliente debe retroceder para el intervalo de reintento especificado por el servidor. Respetar el retroceso garantiza que dedique un tiempo mínimo a la espera entre reintentos.

  • Escalado horizontal de la carga de trabajo de cliente

    Si está probando en niveles de alto rendimiento (>50 000 RU/s), la aplicación cliente puede convertirse en el cuello de botella debido al límite de la máquina en el uso de CPU o 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.

  • Uso del direccionamiento basado en nombres

    Use direccionamiento basado en nombres, donde los vínculos tienen el formato dbs/MyDatabaseId/colls/MyCollectionId/docs/MyDocumentId, en lugar de SelfLinks (_self), que tienen el formato dbs/<database_rid>/colls/<collection_rid>/docs/<document_rid> para evitar recuperar ResourceIds de todos los recursos usados para construir el vínculo. Además, a medida que estos recursos se vuelven a crear (posiblemente con el mismo nombre), es posible que el almacenamiento en caché no le ayude.

  • Ajuste del tamaño de página de las consultas o fuentes de lectura para mejorar el rendimiento.

    Al realizar una lectura masiva de documentos mediante la funcionalidad de fuente de lectura (por ejemplo, readDocuments) o al emitir una consulta SQL, los resultados se devuelven de forma segmentada si el conjunto de resultados es demasiado grande. De forma predeterminada, los resultados se devuelven en fragmentos de 100 elementos o 1 MB, el límite que se alcance primero.

    Para reducir el número de recorridos de ida y vuelta de red necesarios para recuperar todos los resultados aplicables, puede aumentar el tamaño de página mediante el encabezado de solicitud x-ms-max-item-count hasta 1000. En los casos en los que necesite mostrar solo unos pocos resultados, por ejemplo, si la interfaz de usuario o la API de aplicación devuelve solo 10 resultados a la vez, también puede reducir el tamaño de página a 10 para reducir el rendimiento consumido para lecturas y consultas.

    También puede establecer el tamaño de página mediante el método setMaxItemCount.

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

    El SDK Java Async v2 de Azure Cosmos DB utiliza netty para la E/S (entrada/salida) sin bloqueo. 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 Observable devuelto por la API emite el resultado en uno de los subprocesos de Netty de bucle de eventos de E/S compartidos. Así que es importante no bloquear dichos subprocesos. Realizar tareas intensivas de procesamiento de CPU o una operación de bloqueo en el subproceso de Netty del bucle de eventos E/S puede causar un interbloqueo o reducir significativamente el rendimiento del SDK.

    Por ejemplo, el siguiente código ejecuta un trabajo intensivo de CPU en el subproceso Netty de E/S del bucle de eventos:

    SDK de Java asincrónico V2 (Maven com.microsoft.azure::azure-cosmosdb)

      Observable<ResourceResponse<Document>> createDocObs = asyncDocumentClient.createDocument(
        collectionLink, document, null, true);
    
      createDocObs.subscribe(
        resourceResponse -> {
          //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();
        });
    

    Una vez recibido el resultado, si desea realizar un trabajo intensivo de CPU en él, debe evitar hacerlo en el hilo de E/S del bucle de eventos de Netty. En su lugar, puede proporcionar su propio planificador para usar su propio hilo al ejecutar su trabajo.

    SDK de Java asincrónico V2 (Maven com.microsoft.azure::azure-cosmosdb)

      import rx.schedulers;
    
      Observable<ResourceResponse<Document>> createDocObs = asyncDocumentClient.createDocument(
        collectionLink, document, null, true);
    
      createDocObs.subscribeOn(Schedulers.computation())
      subscribe(
        resourceResponse -> {
          // this is executed on threads provided by Scheduler.computation()
          // Schedulers.computation() should be used only when:
          //   1. The work is cpu intensive 
          //   2. You are not doing blocking IO, thread sleep, etc. in this thread against other resources.
          veryCpuIntensiveWork();
        });
    

    En función del tipo de trabajo, debe usar el programador RxJava adecuado para el mismo. Obtenga más información aquí Schedulers.

    Para más información, consulte la página de GitHub del SDK de Java asincrónico de Azure Cosmos DB v2.

  • Deshabilitación del registro de Netty

    El registro de la biblioteca Netty es prolijo y debe desactivarse (es posible que suprimir el registro en la configuración no sea suficiente) para evitar costos adicionales de CPU. Si no está en modo de depuración, deshabilite por completo el registro de Netty. Por lo tanto, si usa log4j para quitar los costos adicionales de CPU generados por org.apache.log4j.Category.callAppenders() 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 ser lo suficientemente grande como para tener suficiente espacio para el tamaño del grupo de conexiones configurado 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
    

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, el código siguiente muestra cómo excluir una sección completa de los documentos (también conocidos como subárboles) de la indexación mediante el comodín "*".

    SDK de Java asincrónico V2 (Maven com.microsoft.azure::azure-cosmosdb)

    Index numberIndex = Index.Range(DataType.Number);
    numberIndex.set("precision", -1);
    indexes.add(numberIndex);
    includedPath.setIndexes(indexes);
    includedPaths.add(includedPath);
    indexingPolicy.setIncludedPaths(includedPaths);
    collectionDefinition.setIndexingPolicy(indexingPolicy);
    

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

Capacidad de procesamiento

  • 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> o FeedResponse<T>.

    SDK de Java asincrónico V2 (Maven com.microsoft.azure::azure-cosmosdb)

    ResourceResponse<Document> response = asyncClient.createDocument(collectionLink, documentDefinition, null,
                                                     false).toBlocking.single();
    response.getRequestCharge();
    

    El cargo de solicitud devuelto en este encabezado es una fracción de la capacidad de proceso aprovisionada. Por ejemplo, si tiene aprovisionadas 2000 RU/s y la consulta anterior devuelve 1000 documentos de 1 KB, el costo de la operación es 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 funciona de forma acumulativa de forma coherente por encima de la tasa de solicitudes, es posible que el número de reintentos predeterminado establecido actualmente en 9 internamente por el cliente no sea suficiente; en este caso, el cliente inicia una excepción DocumentClientException con el código de estado 429 a la aplicación. El recuento de reintentos predeterminado se puede cambiar mediante setRetryOptions en la instancia de ConnectionPolicy. De forma predeterminada, documentClientException con el código de estado 429 se devuelve después de un tiempo de espera acumulado 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.

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.