Skip to main content
BlogBases de donnéesDjango et SQL : Votre duo dynamique pour la mise à l'échelle des bases de données

Django et SQL : Votre duo dynamique pour la mise à l'échelle des bases de données

ScaleDatabaseswithDjangoandSQL-BlogHeader  (en anglais)

La mise à l'échelle et l'optimisation des bases de données pour répondre aux besoins de vos applications peuvent constituer un défi de taille. Si vous n'avez pas lu mon récent blog sur la façon dont Django peut faire le gros du travail pour les applications de bases de données Python et SQL, je vous recommande vivement de le consulter. Mais la version TL;DR est que SQL est optimisé pour les bases de données SQL, Python ne l'est pas, et Django est un excellent intermédiaire pour vous aider à construire des applications plus efficaces, avec moins de friction, de complexité et de code lorsque vous utilisez ces deux langages ensemble.

Ainsi, si Django fait le gros du travail en créant l'application de base de données, vous êtes toujours responsable de la gestion et de la surveillance quotidiennes de vos bases de données. Certaines de ces tâches de gestion peuvent être confiées à votre fournisseur de services en nuage, en utilisant des services tels que Linode Managed Databases, mais vous pourriez découvrir de nouveaux obstacles à mesure que vous évoluez, par exemple :

  • Migrations de bases de données. Conversion d'une base de données existante en un nouvel état souhaité, avec des modifications contrôlées du schéma de la base de données.
  • Déploiements multi-bases de données. Pour optimiser les performances, les développeurs peuvent concevoir leurs applications de manière à utiliser des bases de données distinctes pour des fonctions segmentées. Par exemple, une base de données primaire en lecture/écriture et une base de données réplique en lecture pour les requêtes courantes.

Si l'une de vos bases de données utilise SQL, vous pouvez utiliser Django pour réduire les frictions et vous faciliter la vie tout en manipulant une quantité importante de données.

Cette introduction à deux concepts clés de la gestion des bases de données va de pair avec les instructions étape par étape pour la création d'une application Django prête pour la production que vous trouverez dans l'ebook Understanding Databases et dans ma nouvelle série de vidéos éducatives. L'un ou l'autre de ces parcours d'apprentissage vous aidera à faire en sorte que Django fasse le gros du travail SQL pour vous.

Migrations de bases de données
Lorsque vous débutez, il peut être difficile de déterminer les types de données pour une colonne donnée, d'autant plus que vos besoins en la matière évoluent inévitablement au fil du temps. Et si vous vouliez que votre champ titre ne comporte que 80 caractères ? Et si vous aviez besoin d'ajouter un champ d'horodatage pour savoir exactement quand les éléments ont été ajoutés à la base de données ?

La modification d'un tableau après sa création peut être assez compliquée pour plusieurs raisons :

  • Que faites-vous des valeurs préexistantes ?
  • Que faire si des lignes préexistantes manquent de données pour les nouvelles colonnes/champs ?
  • Que se passe-t-il si vous supprimez une colonne/un champ ? Que se passe-t-il avec les données ?
  • Que se passe-t-il si vous ajoutez une relation qui n'existait pas auparavant (c'est-à-dire des clés étrangères) ?

Heureusement pour les développeurs Django, nous avons quelque chose appelé makemigrations et migrate.

Voyons comment cela fonctionne en action.

Voici notre exemple de modèle de données Django :

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,
    )

Ajoutons le champ :

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

Ce champ nous permettra de suivre le dernier utilisateur à avoir apporté une modification à notre modèle. 

Mettons à jour notre modèle :

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
    )

Maintenant, après avoir enregistré ce fichier, ceci BlogArticle est déclarée dans (models.py), comment faire savoir à notre base de données que ce changement a eu lieu ?

Il y a deux façons :

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

Voyons ce que font ces deux commandes :

python manage.py makemigrations

python manage.py makemigrations recherche les changements dans tous models.py dans votre projet Django et recherche les changements. Si des changements sont trouvés, un nouveau fichier python sera créé avec l'attribut modifications proposées que notre base de données SQL besoins à faire. Les changements proposés ressemblent à quelque chose comme :

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),
        ),
    ]

Il s'agit, bien sûr, d'un autre fichier Python . Ce fichier nous permet (à nous, les développeurs) de savoir ce qui doit se passer dans notre base de données. Il est écrit en Python et non en SQL pour maintenir la cohésion et tirer parti des fonctionnalités intégrées de l'ORM de Django.

Mais pourquoi est-ce un dossier pour ce qui devrait arriver ? Eh bien, il y a plusieurs raisons à cela :

  • Si nous devons revoir ce qui devrait se passer avant que cela ne se produise, nous pouvons le faire ici.
  • Ce site makemigrations La commande ne vérifier auprès de la base de données pour voir si ce changement peut même se produire.
  • La base de données peut avoir déjà été modifiée pour répondre à ces exigences (en fonction d'un certain nombre de facteurs liés à la personne ou à l'organisme qui gère la base de données).
  • Si nous devons effectuer des tests avant de modifier une base de données de production, c'est le moment idéal pour le faire.

En supposant que ce changement est valide (pour autant que l'on puisse dire), nous pouvons valider les changements :

python manage.py migrer

python manage.py migrate tentera de modifier notre base de données à notre place - tous les champs, colonnes, tables, clés étrangères, etc. - Django fera le travail à notre place pour s'assurer que la base de données est mise à jour comme nous l'avions prévu.

Il est important de noter que Django peut ne pas effectuer ces modifications pour un certain nombre de raisons. Pour les nouveaux développeurs Django, cela est presque toujours dû à l'ajout et à la suppression de champs et de colonnes et à l'échec de l'exécution correcte des migrations.

Quand c'est fait correctement, python manage.py migrate assure un système stable qui fait correspondre notre code Python avec nos tables SQL, nous permettant ainsi de profiter de toutes les merveilles que les bases de données Django et SQL offrent.

En quoi cela nous donne-t-il plus de flexibilité ?

Python a de larges applications là où SQL n'en a pas. Le langage de requête structuré a ses limites écrites dans son nom. Qui crée des animations Pixar uniquement avec SQL ?

Tout cela pour dire que la simplicité de Python aide en fait les développeurs à adopter la puissance de SQL, peut-être même sans le savoir.

Pourquoi les bases de données gérées et Django ont du sens

Lorsqu'il s'agit de créer une application web, notamment Django, vous devez prendre quelques décisions :

  • Quelle(s) solution(s) de stockage de données voulons-nous ? MySQL, Postgres, MongoDB, Redis, Object Storage, etc.
  • Comment allons-nous fonctionner/intégrer la solution de stockage des données ?
  • Comment allons-nous nous remettre d'une interruption ou d'un temps d'arrêt ?
  • Comment allons-nous maintenir la solution de stockage ?
  • Comment allons-nous sécuriser notre solution de stockage ?
  • Comment allons-nous sauvegarder notre solution de stockage ?

Les réponses à ces questions peuvent changer au fur et à mesure que votre projet devient plus complexe, mais elles commencent toutes au même endroit : décider entre une gestion autonome et une gestion par un tiers.

Autonome:

  • Les avantages : Contrôle et coût.
  • (Significatif) Con : Vous êtes responsable de tout.

Les services gérés sont souvent plus coûteux dès le départ, alors que l'autogestion vous permet d'utiliser votre distribution Linux préférée, optimisée d'une manière ou d'une autre pour vos besoins. Cela peut inclure l'exécution d'une version bifurquée de MySQL que votre équipe a modifiée. Vous pouvez économiser des dollars sur l'exécution de votre service, mais cela prendra toujours plus de temps à maintenir.

Bases de données gérées par des tiers : 

Oui, c'est peut-être un peu plus cher en dollars et en cents, mais la maintenance sera beaucoup moins longue. Cette option et les solutions de stockage de données gérées sont mon choix de facto pour mes applications Web. Dans cet exemple, nous utilisons déjà Django pour gérer les transactions de la base de données. SQLAlchemy partage également cette force puisqu'il est utilisé avec des frameworks tels que FastAPI, Flask, et bien d'autres. Si vous confiez déjà l'écriture SQL à un progiciel Python , pourquoi ne pas confier l'exécution de vos serveurs SQL ?

Maintenant, étant donné l'efficacité de Python ORM (comme Django ORM et SQLAlchemy), je vous recommande d'utiliser des services gérés de base de données et/ou de stockage de données chaque fois que possible, voici ce que vous avez à gagner si vous le faites :

  • Réduction du temps de développement
  • Réduction du temps de gestion
  • Réduction du temps de récupération
  • Réduction des interruptions de service
  • Réduction de la complexité du déploiement et du développement
  • Réduction de la complexité des migrations de bases de données (à partir d'autres services)
  • Réduction des activités répétitives/inefficaces/inefficientes pour les développeurs SQL
  • Réduction de la complexité DevOps/Ops
  • Augmentation de l'efficacité des développeurs non-SQL
  • Accélération du déploiement et du développement
  • Fiabilité accrue (souvent soutenue par un accord de niveau de service)
  • Sécurité accrue
  • Amélioration de la maintenabilité
  • Augmentation des sauvegardes et de la redondance
  • Augmentation marginale du coût

J'ai fait la liste ci-dessus avec l'esprit d'utiliser un cluster de base de données MySQL géré sur Linode ainsi que le stockage d'objets Linode (pour stocker des fichiers comme CSS, JavaScript, images, vidéos, etc). En pratique, l'utilisation de ces services nous aide à rester concentrés sur la construction d'une excellente application web avec Django, FastAPI, Flask, Node.js, ou autre. En d'autres termes, nous nous concentrons sur la création des outils et des logiciels que vos utilisateurs souhaitent réellement. Vous savez, là où se trouve la vraie valeur pour eux.

MySQL, PostgreSQL, Redis et Django

Pendant longtemps, la principale base de données de Django était PostgreSQL. Je dirais que c'est, en grande partie, dû au fait que vous pouviez utiliser un JSONField uniquement dans Postgres. Avec Django 3.2+ et MySQL 5.7.8+, le JSONField est désormais disponible pour MySQL également.

Pourquoi est-ce important ?

Le stockage de données non structurées, comme le JSON, est souvent nécessaire pour gérer le contenu généré par les utilisateurs ou stocker des données provenant d'autres services API . Voyons comment :

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

Voici les données que je veux stocker en relation avec ce Pet :

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"
}

Ces données nous montrent quand nous pourrions avoir besoin d'un JSONField . Nous pouvons stocker tous les noms d'animaux de compagnie (en utilisant la fonction name ) et conserve le reste pour le stocker dans le JSONField. Ce qui est cool avec JSONFields est qu'ils peuvent être interrogés comme n'importe quel autre champ standard de Django, même avec ces différents schémas.

Il y a un débat en cours parmi les développeurs de Django pour savoir quelle base de données utiliser : MySQL ou PostgreSQL. Pendant longtemps, j'ai toujours opté pour PostgreSQL parce que le JSONField n'était disponible que sur PostgreSQL, mais ce n'est plus le cas. Je vous conseille d'en choisir un et de vous y tenir jusqu'à ce qu'il ne réponde plus à vos besoins.

Mais à quoi sert Redis ?

Redis est une base de données en mémoire incroyablement rapide et souvent utilisée comme base de données temporaire (nous y reviendrons dans un instant), comme service de mise en cache et/ou comme file d'attente de messagerie. La raison pour laquelle je l'appelle une base de données temporaire est due au fait qu'elle est en mémoire. La mémoire est souvent plus chère que le stockage sur disque, ce qui rend souvent impossible le stockage à long terme des données en mémoire.

Mon principal cas d'utilisation de Redis et Django est la mise en cache et la mise en file d'attente.

Mise en cache : disons que vous avez plusieurs pages Web que les utilisateurs visitent souvent. Vous voulez que les données de ces pages soient affichées aux utilisateurs aussi rapidement que possible. Redis, en tant que système de mise en cache pour Django, rend cela incroyablement facile. Les données contenues dans ces pages peuvent être rendues à partir d'une base de données SQL, mais Redis peut stocker ces données rendues à partir du cache. En d'autres termes, l'utilisation de Redis avec SQL peut souvent accélérer vos réponses tout en réduisant le nombre de requêtes à vos bases de données SQL.

Mise en file d'attente : Un autre cas d'utilisation populaire de Redis consiste à décharger les tâches à long terme vers un autre processus (souvent par le biais d'un paquetage Python appelé Celery). Lorsque vous avez besoin de faire cela, vous pouvez utiliser Redis comme une file d'attente des tâches qui devraient être complétées à un autre moment.

Par exemple, si vous avez un utilisateur qui a besoin d'un rapport sur toutes ses transactions des cinq dernières années, le logiciel peut prendre des heures pour générer ce rapport. Évidemment, personne ne va fixer une machine pendant des heures. Nous allons donc décharger cette requête de notre utilisateur vers une file d'attente Redis. Une fois dans Redis, nous pouvons faire tourner un processus de travail (comme l'utilisation de Celery avec Django) pour générer le rapport. Une fois le rapport terminé, quel que soit le temps qu'il a pris, l'utilisateur en est informé. Cette notification, comme d'autres notifications, pourrait également être effectuée par le biais d'une file d'attente Redis couplée à un processus ouvrier Celery/Django.

Tout cela pour dire que Redis et MySQL se complètent très bien. Vous pouvez déployer un serveur de base de données Redis autogéré via la place de marché Linode.

Stockage en mode objet

Le dernier service géré lié aux données que je recommande d'utiliser est Linode Object Storage. Object Storage est responsable de tous les autres types de données que vous pouvez avoir besoin de stocker. Par exemple, nous ne stockerions pas tous les octets d'une vidéo dans MySQL. Au lieu de cela, nous stockons les métadonnées liées à cette vidéo et nous stockons la vidéo dans Object Storage.

Voici quelques exemples d'utilisation du stockage d'objets :

  • Feuilles de style en cascade (CSS)
  • JavaScript (comme React.js, Vue.js, Vanilla.js, etc.)
  • Vidéos
  • Images (brutes et compressées)
  • CSVs, XLSX
  • Sauvegarde des bases de données
  • Couches d'image de conteneur Docker (si auto-géré)
  • Itérations d'algorithmes d'apprentissage automatique entraînés
  • Terraform Dossiers d'État
  • PDFs (grands et petits)
  • Tout fichier persistant qui doit être téléchargé souvent (ou téléchargé vers l'amont).

Résumé

Après avoir lu cet article, j'espère que vous vous sentirez motivé pour tirer parti de la puissance des services gérés dans vos projets d'applications Web. Django est une excellente solution pour créer des applications Web à partir de bases de données SQL, mais ce n'est certainement pas la seule. Si vous voulez vous plonger dans les rouages de SQL et des serveurs SQL, je pense que c'est un exercice intéressant de voir combien d'applications réussies s'appuient sur Django pour gérer l'essentiel de ce que Django peut faire.

Voici quelques points forts (ou beaucoup) qui font que Django avec Managed MySQL sur Linode est génial :

  • Django se charge des tâches SQL les plus lourdes (tout comme des outils tels que SQLAlchemy pour Flask/FastAPI).
  • Django permet également d'utiliser des commandes SQL brutes (tout comme des outils tels que SQLAlchemy).
  • Django aide les débutants à apprendre les commandes SQL
  • Django dispose d'une prise en charge intégrée de MySQL et PostgreSQL (en plus d'un client python spécifique à la base de données).
  • Accélère les déploiements en production
  • Fiabilité et capacité de récupération accrues
  • Permet aux environnements de développement et de production de correspondre presque exactement à la technologie des bases de données.
  • Facilite et rend plus fiable le fonctionnement de Django en conteneur.
  • Permet de passer d'un déploiement à un seul nœud à un déploiement à plusieurs nœuds, voire à une transition complète vers Kubernetes.
  • Il est plus facile pour les nouveaux développeurs Django/Python d'utiliser des systèmes de production.
  • Le partage de bases de données entre plusieurs applications basées sur python est plus facile et plus sûr (par exemple, une application FastAPI lisant/écrivant depuis/vers une base de données MySQL basée sur Django).
  • Le champ JSONField de Django est désormais pris en charge par MySQL (auparavant uniquement par PostgreSQL).
  • Facilité de test (pendant le CI/CD ou dans des environnements de développement locaux)
  • Évolue pour répondre aux exigences de Django
  • Prise en charge de plusieurs bases de données dans un seul projet Django, par exemple : utilisation de MySQL comme base de données principale en lecture/écriture et d'une base de données MySQL répliquée en lecture pour les requêtes courantes.
  • Contrôles d'accès stricts (IPs privées Linode, développement local)
  • Requiert un certificat SSL pour la connexion (ajoute de la complexité aux déploiements mais augmente également la sécurité)
  • Permet une connexion privée (dans la même région ; réduit les coûts de connexion)

Si vous êtes intéressé par une approche moderne du déploiement d'applications Django sur Linode, avec une base de données MySQL gérée, des actions GitHub pour CI/CD, Terraform et Ansible, plongez dans des tonnes de contenu éducatif gratuit, étape par étape :

Pour vous aider à démarrer, le site GitHub Coding for Entrepreneurs propose un référentiel de code correspondant à chaque étape de la série. Bonne chance, et n'oubliez pas de me faire savoir comment les choses se passent via Twitter @JustinMitchel.


Commentaires

Laissez un commentaire

Votre adresse électronique ne sera pas publiée. Les champs obligatoires sont marqués d'un *.