¡Acelera! Recomendaciones para acelerar su aplicación Django

django-faster

Admitámoslo, Django es lo mejor. Es simple, confiable, rápido y está escrito en Python. ¿Que más necesita?

Sin embargo, tendemos a abusar de su facilidad y rápidamente olvidamos que una aplicación crece; y con ella la cantidad de código procesado, así como la cantidad de datos guardados en la base de datos. Esto afecta directamente el tiempo de respuesta de la aplicación. Es por eso que nuestra aplicación es rápida al principio y luego, un día, la verifica y es muy lenta para los estándares de Django.

Pero no es culpa de Django, es culpa nuestra. Tendemos a pensar «Django ORM es genial, usémoslo en todas partes todo el tiempo sin consideraciones» o «Django views es lo máximo, puedo calcular todo para mi vista y enviar la información sobre el contexto y la aplicación aún es rápida» o simplemente «No te preocupes, mejoraré ese código en la próxima versión» y nunca nos comprometemos con ese refactorización.

Por estas razones, y considerando que somos la razón principal por la que falla el software, revisemos algunas consideraciones para hacer que nuestra aplicación Django sea más rápida.

Reduzca la cantidad de queries

Una cosa que generalmente olvidamos es que cada query a la base de datos es costosa; especialmente si está utilizando filtros y si su base de datos tiene mucha información. Por esta razón, son el principal factor de lentitud en una aplicación Django. El problema es que los usamos descuidadamente porque generalmente son solo un par de líneas y porque no estamos tratando con la base de datos directamente sino a través del ORM de Django; incluso si escribimos Python, está ejecutando SQL, y SQL puede ser muy lento.

Por eso es importante depurar y realizar un seguimiento de sus queries. Una forma increíble de hacerlo es usar la librería Django Debug Toolbar. Tiene un panel SQL que no solo muestra la cantidad de queries realizadas a la base de datos y su tiempo de ejecución; además le brinda un informe detallado de lo que hace cada query, cuántas veces se repite una query, cuáles son las queries más caras, etc. Si está utilizando Cookie Cutter de Pydanny, esta librería ya está instalada para instancias locales.

Detalles del panel SQL de Django Debug ToolBar

Por otro lado, si su aplicación ya está en producción, puede usar un sistema de monitoreo como The Elastic Stack. Este tipo de herramientas puede brindarle información más detallada, en función de las visitas de su sitio, como la vista más lenta de la aplicación, su vista más visitada, su tiempo de carga promedio, etc. Esto puede permitirle optimizar las vistas que realmente importan primero y priorizar el trabajo para garantizar que sus visitantes tengan una buena experiencia en su sitio.
Utilizando estas herramientas, pude reducir las queries en una vista muy pesada de 1500 a 31 en menos de una hora trabajando en ello sin perder información; reduciendo el tiempo de carga de varios segundos a menos de un segundo. ¡Fue increíble!

Aquí hay algunos trucos para reducir las queries en su aplicación.:

  • Use select_related y prefetch_related cuando trabaje con relaciones de modelo (uno a uno, muchos a muchos, foreign keys, etc.).
  • Verifique sus filtros, a veces es más rápido seleccionar todos los elementos y filtrarlos directamente con Python.
  • Asigne sus conjuntos de queries a variables y siga usando esas variables en lugar de llamar a la query cada vez que la necesite.
  • Si solo necesita verificar si el conjunto de queries tiene elementos o la cantidad de elementos en la query, use la función exists o count para eso. Es mucho más rápido que verificar la longitud del resultado de la query.
  • Si usted necesita el último o el más reciente ítem en el query set, use estos métodos directamente en lugar de usar order_by y luego llamar al último resultado de la query.
  • Es obvio, pero lea la documentación de querySets de Django. Contiene muchos buenos consejos y recomendaciones para mejorar los tiempos de sus queries.

Vaya asincrónico donde pueda con Celery

Hay ciertos procesos que no requieren ser ejecutados sincrónicamente que pueden tomar tiempo y ralentizar nuestra aplicación. Por ejemplo, enviar un correo electrónico al usuario después de que él/ella realiza una acción, calculando valores para un informe o actualizando alguna información dentro de la base de datos. Además, hay algunos procesos que pueden tardar demasiado en finalizar y que afectarán directamente a nuestra aplicación.

Para todo este tipo de acciones, usted puede usar Celery. De acuerdo a su documentación:

Celery es una cola de tasks con enfoque en el procesamiento en tiempo real, al tiempo que admite la programación de tasks.

http://docs.celeryproject.org/en/latest/index.html

Con celery, usted puede crear tasks. Una task es simplemente un método que se puede ejecutar de forma asincrónica. Lo que hace celery es crear varios trabajadores (4 por defecto) que escuchen la query de la task. Cada vez que se agrega una task a la query de las tasks, el primer trabajador disponible la obtendrá y la ejecutará. Si se agregan varias tasks al mismo tiempo, cada trabajador obtendrá una hasta que estén ocupadas. Una vez que uno de ellos termine, obtendrá la siguiente task en la query hasta que esté limpia. Los trabajadores se ejecutan simultáneamente, por lo que se pueden ejecutar varias tasks a la vez. Además, al ser un proceso asincrónico, no hay control sobre el orden en que finalizan las tasks. La primera query enviada a la task puede ser la última en finalizar su ejecución.

Los trabajadores se ejecutan en un servicio separado de su aplicación, por lo que su aplicación no se ve afectada por su tiempo de ejecución. Incluso si una task genera un error, su aplicación no se verá afectada (siendo una buena herramienta para aislar procesos propensos a errores). Por esta razón, puede ejecutar como tasks todos aquellos procesos que no le importa dónde se ejecutarán.

Además, para procesos muy lentos como el análisis de datos, puede usar celery junto con una REST API o websockets, consumiéndolos con un framework JS como Vue. Es mejor para un usuario ver un mensaje de carga en la pantalla que nada. Además, teniendo en cuenta que celery es asíncrono, puede comenzar a mostrar los resultados que ya están calculados en la página sin esperar a que finalicen todos los procesos.

Casi todas nuestras aplicaciones de Django usan celery, ¡debería probarlo!

No se repita usted mismo

Una de las primeras reglas de codificación es el principio DRY, Do Not Repeat Yourself, no se repita usted mismo. Siempre debe evitar codificar funcionalidades u objetos que ya existen; especialmente cuando trabaja con un framework como Django. Menciono esto porque generalmente el código que repetimos es más lento que el que viene con el núcleo de la librería. Además, el uso de las herramientas proporcionadas por la librería le permite verificar la consideración del rendimiento dentro de la documentación, las mejores prácticas, etc.

Sé que esta recomendación es bastante obvia, pero es impresionante cuántas veces he encontrado código hecho a mano que ya se implementó en el núcleo de Django. Verifique la documentación de Django o navegue por la web antes de codificar; usted puede quedar gratamente sorprendido.

Cachée los datos predecibles

Si tiene un método con un resultado que no cambiará en mucho tiempo, puede considerar el almacenamiento en caché. Django proporciona una caché de bajo nivel que le permite almacenar datos específicos en una capa de caché sin almacenar en caché toda la página.

Aquí hay un pequeño ejemplo:

from django.core.cache import cache
cache.set('my_key', 'hello, world!', 30)
cache.get('my_key')
cache.clear()

Con esta caché, puede calcular un gran valor una vez en un periodo específico de tiempo, reduciendo el tiempo de respuesta en todas las demás solicitudes. Es muy útil si no necesita devolver información actualizada, si está utilizando una API externa cuyo resultado se actualiza una vez al día o una vez a la semana, etc. Incluso puede mezclarlo con celery y actualizar la caché una vez acabe la task.

Tenga en cuenta que si va a guardar mucha información en la memoria caché, puede considerar crear un modelo para guardar la información en la base de datos. El uso de procesos timestamp y celery pueden ser una mejor manera si necesita usar los datos para otros análisis y procesos. Depende de sus requerimientos.

Mantenga su código limpio

Mantener su código limpio y organizado no solo es una buena manera de facilitar el proceso de desarrollo, sino que puede afectar directamente el tiempo de su aplicación. Aquí hay algunas consideraciones que he encontrado que están directamente relacionadas con el rendimiento de la aplicación:

  • Verifique el código repetido. Si una sección de código aparece dos veces o más en su código, considere crear una función o un método con ella.
  • En general, un método o una función solo deben llamarse una vez por datos por proceso. Si necesita usar su resultado, almacénelo en una variable local y use esta variable en su lugar.
  • Evite procesos innecesarios en el método __init__ de sus objetos. Si hay algo pesado, como una solicitud externa, manténgalo en un método diferente para que pueda controlar cuándo llamarlo.
  • Verifique las variables no utilizadas y las llamadas a métodos. Puede estar pasando tiempo ejecutando un método cuyo resultado no se está utilizando. Varios editores de código pueden ayudarlo a verificar esto.
  • Una vez más, lea la documentación de Django y encuentre la forma de Django de hacer lo que necesita hacer; Por lo general, es más limpio y rápido que otros enfoques.

¡Mantenga la calma y disfrute de Django!

Sé que puede estar muy preocupado después de leer este artículo. ¡Pero tómelo con calma! vaya y compruebe su aplicación y comience a mejorar su rendimiento. Una de las mejores cosas de Python y Django es que son muy versátiles, por lo que refactorizar su código para mejorar su aplicación será mucho más fácil de lo que piensa. Entonces, ¡feliz codificación!