Ir al contenido principal
BlogBases de datosDjango y SQL: Tu dúo dinámico para escalar bases de datos

Django y SQL: Tu dúo dinámico para escalar bases de datos

ScaleDatabaseswithDjangoandSQL-BlogHeader

Escalar y optimizar las bases de datos para satisfacer las necesidades de tus aplicaciones puede ser un reto importante. Si no has leído mi reciente blog sobre cómo Django puede hacer el trabajo pesado para Python y las aplicaciones de bases de datos SQL, te recomiendo encarecidamente que lo veas. Pero la versión TL;DR es que SQL está optimizado para bases de datos SQL, Python no lo está, y Django es un gran intermediario para ayudarte a construir aplicaciones más efectivas, con menos fricción, complejidad y código cuando se usan estos dos lenguajes juntos.

Así que, mientras Django hace el trabajo pesado de crear la aplicación de base de datos, tú sigues siendo responsable de la gestión y el seguimiento diario de tus bases de datos. Algunas de estas tareas de gestión pueden ser diferidas a tu proveedor de la nube, utilizando servicios como Linode Managed Databases, pero podrías descubrir nuevos obstáculos a medida que escalas, como por ejemplo:

  • Migraciones de bases de datos. Conversión de una base de datos existente a un nuevo estado deseado con cambios controlados en el esquema de la base de datos.
  • Despliegue de múltiples bases de datos. Para optimizar el rendimiento, los desarrolladores pueden diseñar sus aplicaciones para utilizar bases de datos separadas para funciones segmentadas. Por ejemplo, una base de datos primaria de lectura/escritura y una base de datos de réplica de lectura para las consultas comunes.

Si una de tus bases de datos utiliza SQL, puedes utilizar Django para reducir la fricción y hacer tu vida mucho más fácil mientras manejas una cantidad importante de datos.

Esta introducción a dos conceptos clave de gestión de bases de datos se combina con las instrucciones paso a paso para construir una aplicación Django lista para la producción que se encuentran en el ebook Entendiendo las Bases de Datos y en mi nueva serie de videos educativos. Cualquiera de los dos caminos de aprendizaje te ayudará a que Django haga el trabajo pesado de SQL por ti.

Migraciones de bases de datos
Cuando se está empezando, acertar con los tipos de datos de una columna puede ser un poco complicado, sobre todo porque las necesidades de datos cambiarán inevitablemente con el tiempo. ¿Qué pasa si quieres que tu campo de título tenga sólo 80 caracteres? ¿Y si necesita añadir un campo de fecha y hora para saber exactamente cuándo se añadieron los elementos a la base de datos?

Modificar una tabla después de haberla creado puede ser bastante complicado por varias razones:

  • ¿Qué se hace con los valores preexistentes?
  • ¿Qué pasa si a las filas preexistentes les faltan datos para las nuevas columnas/campos?
  • ¿Qué pasa si se elimina una columna/campo? ¿Qué ocurre con los datos?
  • ¿Qué ocurre si se añade una relación que no existía antes (es decir, claves externas)?

Por suerte para los desarrolladores de Django, tenemos algo llamado makemigrations y migrate.

Veamos cómo funciona en acción.

Aquí está nuestro modelo de datos Django de ejemplo:

class BlogArticle(models.Model):
    user = models.ForeignKey(User, default=1, on_delete=models.SET_DEFAULT)
    title = models.CharField(max_length=120)
    slug = models.SlugField(blank=True, null=True)
    content = models.TextField(blank=True, null=True)
    publish_timestamp = models.DateTimeField(
        auto_now_add=False,
        auto_now=False,
        blank=True,
        null=True,
    )

Añadamos el campo:

updated_by = models.ForeignKey(
        User, related_name="editor", null=True, blank=True, on_delete=models.SET_NULL
)

Este campo nos permitirá rastrear el último usuario que realizó un cambio en nuestro modelo. 

Actualicemos nuestro modelo:

class BlogArticle(models.Model):
    user = models.ForeignKey(User, default=1, on_delete=models.SET_DEFAULT)
    title = models.CharField(max_length=120)
    slug = models.SlugField(blank=True, null=True)
    content = models.TextField(blank=True, null=True)
    publish_timestamp = models.DateTimeField(
        auto_now_add=False,
        auto_now=False,
        blank=True,
        null=True,
    )
    # our new field
    updated_by = models.ForeignKey(
        User, related_name="editor", null=True, blank=True, on_delete=models.SET_NULL
    )

Ahora, después de guardar este archivo este BlogArticle se declara en (models.py), ¿cómo hacemos saber a nuestra base de datos que se ha producido este cambio?

Hay dos maneras:

  1. python manage.py makemigrations
  2. python manage.py migrate

Vamos a discutir lo que hacen estos dos comandos:

python manage.py makemigrations

python manage.py makemigrations busca cambios en todo models.py en todo el proyecto Django y busca los cambios. Si se encuentran cambios, se creará un nuevo archivo python con la etiqueta cambios propuestos que nuestra base de datos SQL necesita para hacer. Los cambios propuestos son algo así:

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
        ('articles', '0001_initial'),
    ]

    operations = [
        migrations.AddField(
            model_name='article',
            name='updated_by',
            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='editor', to=settings.AUTH_USER_MODEL),
        ),
    ]

Esto, por supuesto, es sólo otro archivo Python . Este archivo nos permite (a los desarrolladores) saber lo que debe ocurrir en nuestra base de datos. Está escrito en Python y no en SQL para mantener la cohesión y aprovechar las características incorporadas del ORM de Django.

Pero, ¿por qué es este un archivo para lo que debería ocurrir? Bueno, hay varias razones para esto:

  • Si necesitamos revisar lo que debería ocurrir antes de que ocurra, podemos cogerlo aquí.
  • Este makemigrations El comando no consultar con la base de datos para ver si este cambio puede incluso suceder.
  • Es posible que la base de datos ya haya sido modificada para ajustarse a estos requisitos (dependiendo de una serie de factores relacionados con quién o qué gestiona la base de datos).
  • Si necesitamos realizar pruebas antes de cambiar una base de datos de producción, ahora mismo sería un momento increíble para hacerlo.

Asumiendo que este cambio es válido (por lo que podemos decir), podemos confirmar los cambios:

python manage.py migrar

python manage.py migrate intentará cambiar nuestra base de datos por nosotros - todos los campos, columnas, tablas, claves foráneas, lo que sea - Django hará el trabajo por nosotros para ayudar a asegurar que la base de datos se actualice de la manera que pretendíamos.

Es importante tener en cuenta que Django puede fallar al realizar estos cambios por varias razones. Para los nuevos desarrolladores de Django, esto se debe casi siempre a la adición y eliminación de campos y columnas y a la no ejecución correcta de las migraciones.

Cuando se hace correctamente, python manage.py migrate asegura un sistema estable que hace coincidir nuestro código Python con nuestras tablas SQL, permitiéndonos así toda la maravilla que proporcionan tanto Django como las bases de datos SQL.

¿Cómo nos da esto más flexibilidad?

Python tiene amplias aplicaciones donde SQL no las tiene. El lenguaje de consulta estructurado tiene sus limitaciones escritas en el nombre. ¿Quién crea animaciones de Pixar sólo con SQL?

OK, todo esto es para decir que la simplicidad de Python realmente ayuda a los desarrolladores a adoptar el poder de SQL y posiblemente sin siquiera saberlo.

Por qué tienen sentido las bases de datos gestionadas y Django

Cuando se trata de crear una aplicación web, incluyendo Django, tendrás que decidir algunas cosas:

  • ¿Qué solución(es) de almacenamiento de datos queremos? MySQL, Postgres, MongoDB, Redis, Object Storage, etc.
  • ¿Cómo vamos a funcionar/integrarnos con la solución de almacenamiento de datos?
  • ¿Cómo nos recuperaremos de la interrupción o el tiempo de inactividad?
  • ¿Cómo vamos a mantener la solución de almacenamiento?
  • ¿Cómo aseguramos nuestra solución de almacenamiento?
  • ¿Cómo haremos una copia de seguridad de nuestra solución de almacenamiento?

Las respuestas a estas preguntas pueden cambiar a medida que su proyecto crece en complejidad, pero todas empiezan en el mismo lugar: decidir entre la autogestión y la gestión por terceros.

Autogestionado:

  • Ventajas: Control y coste.
  • (Significativo) Contra: Eres responsable de todo.

Los servicios gestionados suelen costar más dinero desde el principio, mientras que la autogestión significa que puedes utilizar tu distribución de Linux preferida que esté de alguna manera (o algo) optimizada para lo que necesitas. Esto puede incluir la ejecución de una versión bifurcada de MySQL que su equipo haya modificado. Puede que ahorres dólares en la ejecución de tu servicio, pero esto siempre llevará más tiempo de mantenimiento.

Bases de datos gestionadas por terceros: 

Sí, puede ser un poco más caro en dólares y centavos, pero llevará mucho menos tiempo mantenerlo. Esta opción y las soluciones de almacenamiento de datos gestionados son mi elección de facto para mis aplicaciones web. En este ejemplo, ya estamos utilizando Django para gestionar las transacciones de la base de datos. SQLAlchemy también comparte esta fortaleza ya que se utiliza con frameworks como FastAPI, Flask y muchos otros. Si ya estás externalizando la escritura de SQL a un paquete de Python , ¿por qué no externalizar la ejecución de tus servidores SQL?

Ahora bien, dada la eficacia de los ORM de Python (como Django ORM y SQLAlchemy), te recomiendo que utilices bases de datos gestionadas y/o servicios de almacenamiento de datos gestionados siempre que sea posible, esto es lo que puedes ganar si lo haces:

  • Reducción del tiempo de desarrollo
  • Reducción del tiempo de gestión
  • Reducción del tiempo de recuperación
  • Reducción de las interrupciones del servicio
  • Reducción de la complejidad de la implantación y el desarrollo
  • Menor complejidad en las migraciones de bases de datos (desde otros servicios)
  • Reducción de las actividades repetitivas/ineficaces/ineficientes para los desarrolladores de SQL
  • Reducción de la complejidad de DevOps/Ops
  • Aumento de la eficacia de los desarrolladores que no son de SQL
  • Mayor velocidad de implantación y desarrollo
  • Mayor fiabilidad (a menudo respaldada por un acuerdo de nivel de servicio)
  • Mayor seguridad
  • Mayor capacidad de mantenimiento
  • Aumento de las copias de seguridad y la redundancia
  • Aumento marginal del coste

Hice la lista anterior con la mentalidad de utilizar un clúster de base de datos MySQL administrado en Linode así como Linode Object Storage (para almacenar archivos como CSS, JavaScript, imágenes, videos, etc). En la práctica, el uso de estos servicios nos ayuda a mantener el enfoque en la construcción de una excelente aplicación web con Django, FastAPI, Flask, Node.js, o lo que sea. Para decirlo de otra manera, cambiamos el enfoque en la construcción de las herramientas y el software que sus usuarios realmente quieren. Ya sabes, donde está el valor real para ellos.

MySQL, PostgreSQL, Redis y Django

Durante mucho tiempo, la base de datos líder de Django fue PostgreSQL. Yo diría que esto se debe, en gran parte, al hecho de que sólo se podía utilizar un JSONField dentro de Postgres. Con Django 3.2+ y MySQL 5.7.8+, el JSONField está ahora disponible también para MySQL.

¿Por qué es importante?

El almacenamiento de datos no estructurados, como JSON, suele ser necesario cuando se manejan contenidos generados por el usuario o se almacenan datos de otros servicios de API . Veamos cómo:

from django.db import models

class Pet(models.Model):
    name = models.CharField(max_length=200)
    data = models.JSONField(null=True)

    def __str__(self):
        return self.name

Estos son los datos que quiero almacenar en relación con esta mascota:

pet1 = {
    "name": "Bruno",
    "type": "Rat",
    "nickname": "We don't talk about it",
    "age": 2,
    "age_interval": "months"
}

pet2 = {
    "name": "Tom",
    "type": "Cat",
    "breed": "Mixed"
    "age": 4,
    "age_interval: "years",
    "favorite_food": [{"brand": "Acme", "flavor": "Tuna" }]
}

pet3 = {
    "name": "Stewey",
    "type": "Dog",
    "breed": "unknown"
    "age": 34,
    "age_interval: "dog years",
    "nickname": "Football"
}

Estos datos nos indican cuándo podemos necesitar un JSONField . Podemos almacenar todos los nombres de las mascotas (utilizando el name ) y guardar el resto para almacenarlo en el JSONField. Lo bueno de JSONFields es que pueden ser consultados como cualquier otro campo estándar de Django, incluso con estos esquemas variables.

Hay un debate continuo entre los desarrolladores de Django sobre qué base de datos utilizar: MySQL o PostgreSQL. Durante mucho tiempo, siempre opté por PostgreSQL debido al hecho de que el JSONField sólo estaba disponible en PostgreSQL, y ese ya no es el caso. Yo digo que elijas uno y te quedes con él hasta que ya no te sirva para tus necesidades.

Pero, ¿para qué usamos Redis?

Redis es un almacén de datos en memoria que es increíblemente rápido y a menudo se utiliza como una base de datos temporal (más sobre esto en un segundo), un servicio de almacenamiento en caché, y / o una cola de mensajería. La razón por la que la llamo base de datos temporal es el hecho de que está en memoria. La memoria es a menudo más cara que el almacenamiento en disco y, por lo tanto, hacer que el almacenamiento de datos a largo plazo en memoria sea a menudo inviable.

Mi principal caso de uso para Redis y Django son el almacenamiento en caché y las colas

Caché: Supongamos que tienes varias páginas web que los usuarios visitan mucho. Quieres que los datos de esas páginas se muestren a los usuarios lo más rápido posible. Redis, como sistema de caché para Django, hace que esto sea increíblemente fácil. Los datos dentro de estas páginas pueden ser renderizados desde una base de datos SQL pero Redis puede almacenar esos datos renderizados desde la caché. En otras palabras, el uso de Redis con SQL a menudo puede acelerar sus respuestas al tiempo que reduce la cantidad de consultas a sus bases de datos SQL.

Colas: Otro caso de uso popular de Redis es la descarga de tareas de larga duración a otro proceso (a menudo a través de un paquete Python llamado Celery). Cuando necesites hacer esto, puedes usar Redis como una cola de las tareas que deben completarse en otro momento.

Por ejemplo, si tienes un usuario que necesita un informe de todas sus transacciones de los últimos cinco años, el software puede tardar horas en generar ese informe. Obviamente, nadie va a mirar una máquina durante horas. Así que descargaríamos esta petición de nuestro usuario a una cola de Redis. Una vez en Redis, podemos tener un proceso de trabajo en ejecución (como el uso de Celery con Django) para generar el informe. Una vez que el informe está hecho, no importa el tiempo que haya tomado, el usuario sería notificado. Esta notificación, al igual que otras notificaciones, también podría hacerse a través de una cola de Redis acoplada a un proceso worker de Celery/Django.

Todo esto para decir que Redis y MySQL se complementan muy bien. Puedes desplegar un servidor de base de datos Redis autogestionado a través de Linode Marketplace.

Almacenamiento de objetos

El último servicio gestionado relacionado con los datos que recomiendo utilizar es Linode Object Storage. El Almacenamiento de Objetos es responsable de todos los otros tipos de datos que puedas necesitar almacenar. Por ejemplo, no almacenaríamos todos los bytes de un vídeo en MySQL. En su lugar, almacenaríamos los metadatos relacionados con ese vídeo y almacenaríamos el vídeo en Object Storage.

Estas son algunas de las cosas para las que se puede utilizar el almacenamiento de objetos:

  • Hojas de estilo en cascada (CSS)
  • JavaScript (como React.js, Vue.js, Vanilla.js, etc)
  • Vídeos
  • Imágenes (en bruto y comprimidas)
  • CSV, XLSX
  • Copias de seguridad de la base de datos
  • Capas de imagen del contenedor Docker (si es autogestionado)
  • Iteraciones de algoritmos de aprendizaje automático entrenados
  • Terraform Archivos del Estado
  • PDFs (grandes y pequeños)
  • Cualquier archivo persistente que deba descargarse con frecuencia (o cargarse)

Resumen

Después de leer esto, espero que te sientas motivado para aprovechar el poder de los servicios gestionados con tus proyectos de aplicaciones web. Django es una excelente solución para construir aplicaciones web sobre bases de datos SQL, pero ciertamente no es la única. Si quieres sumergirte en las interioridades de SQL y los servidores SQL, creo que es un ejercicio que vale la pena para ver cómo muchas aplicaciones exitosas aprovechan Django para manejar la mayor parte de lo que Django puede hacer.

Aquí hay algunos (o muchos) puntos destacados que hacen que Django con MySQL Gestionado en Linode sea impresionante:

  • Django hace el trabajo pesado de SQL por ti (también lo hacen herramientas como SQLAlchemy para Flask/FastAPI)
  • Django permite también comandos SQL en bruto (de nuevo, también lo hacen herramientas como SQLAlchemy)
  • Django ayuda a los principiantes a aprender los comandos SQL
  • Django tiene soporte incorporado para MySQL y PostgreSQL (además de un cliente específico para db python )
  • Aumenta la velocidad de los despliegues de producción
  • Mayor fiabilidad y capacidad de recuperación
  • Permite que los entornos de desarrollo y producción coincidan casi exactamente con la tecnología de las bases de datos
  • Hace que Django basado en contenedores sea más fácil y fiable
  • Permite pasar de una implantación de un solo nodo a otra de varios nodos o incluso una transición completa a Kubernetes
  • Facilita a los nuevos desarrolladores de Django/Python el uso de sistemas de producción
  • Compartir bases de datos a través de múltiples aplicaciones basadas en python es más fácil y seguro (como una aplicación FastAPI leyendo/escribiendo desde/a una base de datos MySQL basada en Django).
  • El JSONField de Django ahora es compatible con MySQL (antes sólo con PostgreSQL)
  • Fácil de probar (durante CI/CD o en entornos de desarrollo locales)
  • Se adapta a las exigencias de Django
  • Soporte para múltiples bases de datos en un solo proyecto Django como: usar MySQL como base de datos primaria de lectura/escritura y una base de datos de réplica de lectura MySQL para consultas comunes.
  • Controles de acceso estrictos (Linode IPs privadas, desarrollo local)
  • Requiere un certificado SSL para la conexión (añade complejidad a las implantaciones, pero también aumenta la seguridad)
  • Permite la conexión privada (en la misma región; reduce los costes de conexión)

Si estás interesado en un enfoque moderno para desplegar aplicaciones Django en Linode junto con una base de datos MySQL gestionada, acciones de GitHub para CI/CD, Terraform, y Ansible, entra en toneladas de contenido educativo gratuito paso a paso:

Para ayudarte a empezar, el GitHub de Coding for Entrepreneurs tiene un repositorio de código que va con cada paso de la serie. Buena suerte, y asegúrese de hacerme saber cómo van las cosas a través de Twitter @JustinMitchel.


Comentarios

Dejar una respuesta

Su dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *.