Todos queremos construir aplicaciones con buen rendimiento y que puedan escalar facilmente para satisfacer nuevos requerimientos. Y claro, construir este tipo de aplicaciones no es simple. Pero hay algunos consejos básicos que podemos seguir y que nos ayudarán a evitar errores comunes, aplicando prácticas probadas que mejoran el rendimiento de prácticamente cualquier tipo de aplicación, sea un sistema de servicios o una aplicación web.
Repasemos entonces 8 consejos para desarrollar sistemas escalables de alto rendimiento:
Bajar la carga de la base de datos
¿Cuál es el cuello de botella para la mayoría de las aplicaciones? ¡La base de datos, sin dudas! ¿La solución? Evitar acceder a la base de datos, y evitar abrir transacciones o conexiones a menos que sea absolutamente necesario.
Esto significa que el anti-patrón "Abrir la sesión en la vista" , aunque conveniente, suele ser un desperdicio terrible de recursos, y limita fuertemente el rendimiento y la escalabilidad del sistema.
Usar todo el caché posible
Entonces, ¿cómo aliviamos a la base de datos cuando necesitamos acceder a mucha información? Para la mayoría de las aplicaciones de sólo lectura la solución es simple: utilizar muchos caché. Esta solución también puede funcionar para aplicaciones de lectura-escritura si se utilizan eventos para expirar los caché cuando sea necesario.
Cuando se habla de caché, hay una clara diferencia jerárquica en la eficiencia: los caché en-memoria son mucho más rápidos que los caché en-disco, y los caché en-disco son más rápidos que un acceso remoto a la red o a una base de datos.
Usar un caché con objetos de grano grueso
Mientras sea posible, utilizar objetos/datos en el caché en el nivel de grano grueso más alto - incluso aunque se haga caché de objetos de grano fino, un enfoque de grano grueso ahorra CPU y el tiempo para interrogar n áreas de caché, en vez de una sola. Más aún, recuperar todo un grafo de objetos ahorra el tiempo de ensamblar dicho grafo.
No almacenar estados volátiles permanentemente
Los proyectos tienen una tendencia a poner absolutamente todos sus datos en la base de datos. Pero, ¿es absolutamente necesario? ¿Realmente necesitamos guardar la información de sesión del usuario en la base de datos? ¿Estamos almacenando estados volátiles, o datos de negocio necesarios?
Como regla general, sólo hay que almacenar en medios permanentes (disco, base de datos) datos de negocio críticos, necesarios y accionables, y nada más.
Ubicación, ubicación - ubicar las cosas en donde se van a usar
Si sabemos que mañana vamos a subir un armario pesado a un camión de mudanza, va a ser mejor que lo dejemos ubicado cerca de la puerta, en vez de bajarlo al sótano en el fondo de la casa.
Si para recuperar datos necesitamos pasar por un balanceador de carga, servidor web, servidor de aplicaciones y base de datos (en vez de por un balanceador de carga y un servidor web), la escalabilidad y el rendimiento se van a ver afectados.
Restringir el acceso concurrente a los recursos limitados
Imaginemos que a pesar del caché tenemos un miss, y por lo tanto tenemos que hacer varios cálculos costosos buscando muchos datos hacia la base de datos. Más aún, imaginemos que otros 30 clientes quieren exactamente la misma información antes de que se llene el caché. Vamos a tener a 30 clientes accediendo a los mismos datos, realizando la misma operación costosa. ¡Un desperdicio!
Una forma simple de resolver esto es hacer que el primer cliente haga los cáculos, mientras que el resto simplemente espera al resultado del primer cliente.
Restringir el acceso a recursos limitados no sólo aplica a operaciones de lectura, sino también a operaciones transaccionales (aplicando el proceso de "escribir después", como ser usando un mencanismo asincrónico de mensajería).
El 99% de las veces es más rápido dejar que un único hilo termine el trabajo que matar al recurso finito con 200 clientes.
Procesamiento asincrónico
Esto es una extensión del punto anterior: separar un proceso con asincronismo en pasos discretos y separados, usando colas consumidas por una cantidad limitada de trabajadores en cada paso. Esto hace maravillas para la escalabilidad y el rendimiento, y limita el riesgo de que el sistema se va sobrecargado: la aplicación puede estar lenta por veces mientras termina de procesar tareas de la cola, pero casi seguro que seguirá funcionando.
Minimizar el tráfico de red
Salir del área de ejecución de nuestra aplicación es lento, comunicarse con una aplicación remota es más lento todavía que comunicarse con objetos en memoria en el mismo entorno de ejecución. Las redes son menos confiables que la memoria RAM y definitivamente tienen más latencia. Tenemos que evitar las comunicaciones, y hacer todo lo posible para que nuestra aplicación no "converse" demasiado por la red. Obviamente que a veces hay enormes beneficios, y si, incluso el rendimiento y la escalabilidad se benefician de tener estados distribuidos en una red.
Pero en general, el costo-beneficio de comunicarse a través de la red tiene que ser examinado con mucho detalle; hay que evitar el tráfico de red a menos que resulte absolutamente necesario.