Arquitectura orientada al rendimiento

"¿Cómo debo acceder a mis datos?" se preguntan a menudo los desarrolladores cuando contemplan una solución de almacenamiento. Para responder a esta pregunta, primero hay que entender la aplicación en cuestión. ¿Quiénes son los usuarios más importantes y qué casos de uso deben ser rápidos, es decir, qué acciones realiza el usuario con frecuencia? ¿Cuál es la "ruta caliente"?

Una vez que comprenda la ruta caliente, estará listo para analizar el almacenamiento.

Algunas acciones, como cargar un archivo grande, no son tan sensibles a la latencia como otras. Por ejemplo, hacer clic en "comprar ahora" en un sitio de comercio electrónico, o ver tu cronología en las redes sociales, se benefician mucho más de las capacidades de almacenamiento y recuperación de alto rendimiento.

Así que para cada aplicación, si adoptas una perspectiva de rendimiento en la arquitectura, verás que hay lugares en los que el usuario pasa mucho tiempo, donde la capacidad de respuesta es clave. No se trata sólo de ser rápido, se trata de ser fiable rápido bajo carga. Los nueves importan, sobre todo para la latencia.

Quieres que tus casos de uso más cruciales sean rápidos. Dado que la memoria es mucho más rápida que el disco, estos casos deberían servirse desde la memoria. No es casualidad que las bases de datos basadas en memoria estén en auge. Especialmente en entornos de nube, servir desde la memoria aísla la aplicación contra el rendimiento inconsistente del disco.

Priorice su esquema en torno a la ruta caliente

Incluso con una base de datos sin esquema, tu aplicación impondrá un esquema. Depende de ti hacer que ese esquema favorezca los casos de uso de alto rendimiento. Así que identificar los tipos de objetos calientes y mantenerlos en un cubo totalmente residente es un paso. Así, si tiene 4 GB de datos de la cesta de la compra, reserve más de 4 GB de RAM sólo para eso. Si tiene 20 GB de datos del historial de compras, puede estar bien dedicar sólo 5 GB de RAM a esos tipos de documentos. De este modo, los datos menos sensibles a la latencia se beneficiarán de los efectos de mayor capacidad de disco que de memoria, al tiempo que se mantiene el rendimiento de los datos más importantes en cualquier circunstancia.

Ahora que estás pensando en términos de hot path, hablemos de las vistas. Son muy flexibles (la clave aquí es la capacidad de proyectar los datos en tiempo real en formas más ventajosas para la consulta). Dado que vas a querer que tu hot path se ejecute sobre un modelo clave-valor, acabarás haciendo un montón de punteros y multi get. Es decir, una aplicación de alto rendimiento vinculará estrechamente su modelo de datos a la ruta caliente. Esto puede hacer que soportar los casos de uso menos calientes sea un poco más difícil. Aquí es donde intervienen las vistas. Las vistas son lo suficientemente potentes como para dar a esas estructuras de datos en tiempo de ejecución una nueva vida como índices consultables. El resultado es un modelo de consulta flexible además del rendimiento de los valores clave.

Para poner un ejemplo concreto, miremos el conjunto de datos de muestra de cervezas. Tal vez en su aplicación, la pantalla principal que los usuarios quieren ver es una lista de cervezas con sus puntuaciones y, para cada cerveza, la posibilidad de cargar rápidamente reseñas de la cerveza escritas por los usuarios finales. En este caso, querrá realizar búsquedas masivas de valores clave (en lugar de consultas a la vista) para rellenar esas pantallas con el mayor rendimiento posible. Si un caso de uso secundario es navegar por las cervecerías por ciudad, este es un buen momento para utilizar vistas, ya que pueden proporcionar una ventana flexible a los datos subyacentes.

Empecemos por los propios documentos. En este caso, almacenamos las valoraciones directamente en los documentos de cerveza. Al incrustar las valoraciones, podemos mostrarlas de forma trivial en la interfaz de usuario, sin consultas ni búsquedas adicionales. También enlazamos directamente con las opiniones ("comentarios") del documento de la cerveza, de modo que una cerveza y todos sus comentarios pueden obtenerse sin necesidad de realizar una consulta basada en disco. Esta técnica garantiza que los tiempos de respuesta de la base de datos sean rápidos, incluso con una gran carga de tráfico.

He aquí un ejemplo:

El documento azul es un documento de perfil de usuario, se hace referencia en algunos otros lugares en el esquema. Siempre que tengamos el user_id podemos buscar el perfil de usuario rápidamente. Así que busca ese id en los otros documentos JSON.

El documento amarillo es un comentario sobre una cerveza. Lleva el identificador de la cerveza, pero las búsquedas desde un comentario a la cerveza de la que se trata son poco frecuentes. Lo más habitual es tener la cerveza en la mano y querer buscar las reseñas. Hemos mencionado anteriormente que en nuestro ejemplo hipotético, consideramos que esta es una sección crítica para el rendimiento, por lo que si bien sería posible utilizar vistas para buscar comentarios por beer_id, en este caso queremos hacer nuestras búsquedas a través de la interfaz clave-valor. Trabajando en esta interfaz obtenemos el beneficio de las velocidades en memoria, así como una mayor escalabilidad ya que estas peticiones utilizan menos recursos del servidor.

El documento verde es un documento real de cerveza. Tenemos las valoraciones en línea, almacenadas bajo el user_id para imponer la restricción de que cada usuario sólo puede valorar cada cerveza una vez. Para las reseñas / comentarios, los enlazamos desde la matriz de comentarios. Así que el código para obtener todos los datos necesarios para mostrar una página para una cerveza se ve algo como esto (Ejemplo es en ningún lenguaje de programación en particular).

cerveza = couchbase.get("mi-id-cerveza");
comentarios = couchbase.multiget(cerveza.comentarios);
profiles = couchbase.multiget(reviews.map{|review|review.user_id});

Entonces la página tendrá suficientes datos para mostrar información sobre todas las reseñas de la cerveza, así como información sobre el usuario que dejó la reseña. Todo esto sólo requiere 3 peticiones a la base de datos, por lo que el tiempo total transcurrido debería ser sólo un puñado de milisegundos, que será más rápido que el estilo alternativo de enviar una consulta compleja a la base de datos, para que pueda construir un conjunto de resultados y devolverlo.

Estratificación de vistas sobre el esquema de alto rendimiento

La estructura del documento que hemos diseñado para facilitar las interacciones clave-valor es muy rica. Aunque no mantengamos una ruta de búsqueda clave-valor para descubrir las cervezas mejor valoradas, como las valoraciones están incrustadas directamente en el documento, es fácil escribir una vista que ordene las cervezas por valoración. La consulta de esta vista no será tan rápida como una búsqueda directa de clave-valor, pero algo como las cervezas mejor valoradas puede almacenarse fácilmente en caché, de modo que los usuarios disfruten de una experiencia de alto rendimiento aunque el índice subyacente esté basado en disco y no en memoria.

He aquí una vista de mapa que ordenaría las cervezas por puntuación media.

function(doc, meta) {
  if (doc.ratings) {
    var total = 0, count = 0;
    for (var user_id in doc.valoraciones) {
      total = total + doc.valoraciones[id_usuario]; count++
    }
    emit(total/cuenta, null);
  }
}

Se consultaría con Descending=true para conseguir primero las cervezas mejor valoradas.

A partir de ese mismo conjunto de datos subyacente, también podemos proporcionar una forma de que un usuario determinado encuentre todas las reseñas que ha dejado. Esta no es una operación común (en nuestra aplicación de ejemplo inventada), por lo que no es importante que sea increíblemente rápida. Así que habría sido un gran dolor de cabeza mantener una ruta de búsqueda clave-valor para esto. Por ejemplo, si tus usuarios rara vez intentan encontrar todas las opiniones que han escrito, entonces es un dolor mantener una lista de opiniones-id adjunta a cada perfil de usuario. En su lugar, basta con etiquetar las opiniones con el ID de usuario y utilizar un índice para realizar la consulta. La vista para encontrar todas las opiniones del usuario X es sencilla:

function(doc, meta) {
  if (doc.type == "comment" && doc.user_id) {
    emit(doc.user_id, null);
  }
}

Se consulta con ?key=525 para encontrar todas las opiniones escritas por el usuario número 525.

Conclusión: Diseñe para el camino caliente, deje que la flexibilidad de NoSQL le ayude con el resto

Esperemos que este artículo haya pintado una imagen lo suficientemente clara sobre cómo diseñar tu aplicación para las secciones críticas de rendimiento. Lo más importante que puedes hacer es personalizar tu esquema para que las páginas que necesitan ser más rápidas sean las más fáciles de cargar desde la base de datos. Por supuesto, esto significa que no será tan rápido cargar páginas no críticas, ya que el esquema no está diseñado específicamente para hacerlas rápidas.

Sin embargo, las vistas de Couchbase facilitan la reutilización de los datos para otros patrones de acceso. Esperamos que los ejemplos anteriores muestren cómo puedes hacer esencialmente el mismo tipo de consulta de dos maneras muy diferentes, dependiendo de tus limitaciones de rendimiento.

Bonus: Sobre las vistas en caché

Si tu hot path incluye una consulta a la vista (por ejemplo, en una línea de tiempo de inicio de una red social) deberías almacenarla en caché. Esto significa que mientras que la primera petición puede tardar unos milisegundos, las siguientes tendrán un alto rendimiento constante. Esto es más típico del uso de memcached con mysql. En ese patrón, memcached se utiliza tanto para acelerar el rendimiento percibido, como para mantener la carga fuera de la base de datos. Esencialmente estamos hablando de lo mismo aquí, excepto que en lugar de poner los resultados de una consulta mysql lenta en memcached, ponemos los resultados de una consulta de vista Couchbase en un cubo de memoria.

Couchbase TTL puede manejar la expiración de la caché para usted. O es posible usar alguna validación de caché más avanzada

estrategias (una historia para otro día...)

Comparte este artículo
Recibe actualizaciones del blog de Couchbase en tu bandeja de entrada
Este campo es obligatorio.

Autor

Publicado por J. Chris Anderson, cofundador y arquitecto móvil, Couchbase

1 Comentarios

  1. [...] estrategias de modelado, pero eso queda fuera del ámbito de este artículo. Para más información al respecto, lea "Performance Oriented Architecture", de Chris [...]

Deja un comentario

¿Listo para empezar con Couchbase Capella?

Empezar a construir

Consulte nuestro portal para desarrolladores para explorar NoSQL, buscar recursos y empezar con tutoriales.

Utilizar Capella gratis

Ponte manos a la obra con Couchbase en unos pocos clics. Capella DBaaS es la forma más fácil y rápida de empezar.

Póngase en contacto

¿Quieres saber más sobre las ofertas de Couchbase? Permítanos ayudarle.