Vai al contenuto principale
BlogBasi di datiDjango e SQL: Il vostro duo dinamico per scalare i database

Django e SQL: Il vostro duo dinamico per scalare i database

Scalare i database con Django e SQL - Intestazione del blog

Scalare e ottimizzare i database per soddisfare le esigenze delle applicazioni può essere una sfida significativa. Se non avete letto il mio recente blog su come Django può fare il lavoro pesante per le applicazioni con database Python e SQL, vi consiglio vivamente di dargli un'occhiata. Ma la versione più breve è che SQL è ottimizzato per i database SQL, mentre Python non lo è, e Django è un ottimo intermediario per aiutarvi a costruire applicazioni più efficaci, con meno attriti, complessità e codice quando si utilizzano questi due linguaggi insieme.

Quindi, mentre Django fa il lavoro pesante di creare l'applicazione di database, voi siete ancora responsabili della gestione e del monitoraggio quotidiani dei vostri database. Alcune di queste attività di gestione possono essere rinviate al vostro provider cloud, utilizzando servizi come Linode Managed Databases, ma potreste scoprire nuovi ostacoli man mano che scalate, come ad esempio:

  • Migrazioni di database. Conversione di un database esistente in un nuovo stato desiderato con modifiche controllate allo schema del database.
  • Distribuzioni multi-database. Per ottimizzare le prestazioni, gli sviluppatori possono progettare le loro applicazioni in modo da utilizzare database separati per funzioni segmentate. Ad esempio, un database primario di lettura/scrittura e un database di replica di lettura per le query comuni.

Se uno dei vostri database utilizza SQL, potete usare Django per ridurre l'attrito e semplificarvi la vita mentre gestite una quantità significativa di dati.

Questa introduzione a due concetti chiave della gestione dei database si abbina alle istruzioni passo-passo per la costruzione di un'applicazione Django pronta per la produzione che si trovano nell'ebook Understanding Databases e nella mia nuova serie di video didattici. Entrambi i percorsi di apprendimento vi aiuteranno a far fare a Django il lavoro pesante in SQL.

Migrazioni del database
Quando si è agli inizi, trovare i tipi di dati giusti per ogni colonna può essere un po' complicato, soprattutto perché le esigenze dei dati cambieranno inevitabilmente nel tempo. E se si volesse che il campo titolo fosse lungo solo 80 caratteri? E se aveste bisogno di aggiungere un campo timestamp per sapere esattamente quando gli articoli sono stati aggiunti al database?

La modifica di una tabella dopo la sua creazione può diventare piuttosto complicata per alcuni motivi:

  • Cosa fare con i valori preesistenti?
  • Cosa succede se nelle righe preesistenti mancano i dati per le nuove colonne/campi?
  • Cosa succede se si rimuove una colonna/campo? Cosa succede ai dati?
  • Cosa succede se si aggiunge una relazione che non esisteva prima (ad esempio le chiavi esterne)?

Fortunatamente per gli sviluppatori di Django, esiste una cosa chiamata makemigrations e migrate.

Vediamo come funziona in azione.

Ecco il nostro modello di dati Django di esempio:

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

Aggiungiamo il campo:

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

Questo campo ci permetterà di tenere traccia dell'ultimo utente che ha apportato una modifica al nostro modello. 

Aggiorniamo il nostro modello:

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
    )

Ora, dopo aver salvato il file, questo BlogArticle è dichiarata in (models.py), come possiamo comunicare al nostro database l'avvenuta modifica?

Ci sono due modi:

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

Vediamo cosa fanno questi due comandi:

python manage.py makemigrations

python manage.py makemigrations cerca i cambiamenti in tutti models.py in tutto il progetto Django e cerca le modifiche. Se vengono trovate delle modifiche, viene creato un nuovo file python con l'opzione modifiche proposte che il nostro database SQL esigenze da apportare. Le modifiche proposte sono più o meno le seguenti:

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

Questo, ovviamente, è solo un altro file Python . Questo file ci fa sapere (gli sviluppatori) cosa deve accadere nel nostro database. È scritto in Python e non in SQL per mantenere la coesione e per sfruttare le funzionalità integrate dell'ORM di Django.

Ma perché questo è un file per ciò che dovrebbe accadere? Le ragioni sono molteplici:

  • Se abbiamo bisogno di rivedere ciò che dovrebbe accadere prima che accada, possiamo prenderlo qui.
  • Questo makemigrations il comando non controllare con il database per vedere se questa modifica può anche solo di accadere.
  • Il database potrebbe essere già stato modificato per adattarsi a questi requisiti (a seconda di una serie di fattori legati a chi/cosa gestisce il database).
  • Se dobbiamo eseguire dei test prima di modificare un database di produzione, questo è il momento ideale per farlo.

Assumendo che questa modifica sia valida (per quanto ne sappiamo), possiamo fare il commit delle modifiche:

python manage.py migrare

python manage.py migrate tenterà di modificare il nostro database al posto nostro - tutti i campi, le colonne, le tabelle, le chiavi esterne, qualsiasi cosa - Django farà il lavoro per noi per assicurarci che il database sia aggiornato nel modo in cui intendiamo.

È importante notare che Django potrebbe non riuscire ad apportare queste modifiche per una serie di motivi. Per i nuovi sviluppatori di Django, ciò è quasi sempre dovuto all'aggiunta e alla rimozione di campi e colonne e alla mancata esecuzione corretta delle migrazioni.

Se fatto correttamente, python manage.py migrate garantisce un sistema stabile che fa coincidere il nostro codice Python con le nostre tabelle SQL, permettendoci così di usufruire di tutte le meraviglie che Django e i database SQL offrono.

In che modo questo ci dà maggiore flessibilità?

Python ha ampie applicazioni, mentre SQL non ne ha. Il linguaggio di interrogazione strutturato ha i suoi limiti scritti nel nome. Chi crea animazioni Pixar solo con SQL?

Ok, tutto questo per dire che la semplicità di Python aiuta gli sviluppatori ad adottare la potenza di SQL, magari senza nemmeno saperlo.

Perché i database gestiti e Django hanno senso

Quando si tratta di creare un'applicazione web, compreso Django, è necessario decidere alcune cose:

  • Quale soluzione di archiviazione dei dati vogliamo? MySQL, Postgres, MongoDB, Redis, Object Storage, ecc.
  • Come funzionerà/si integrerà con la soluzione di archiviazione dei dati?
  • Come ci riprenderemo da un'interruzione o da un fermo macchina?
  • Come si manterrà la soluzione di stoccaggio?
  • Come possiamo proteggere la nostra soluzione di archiviazione?
  • Come faremo il backup della nostra soluzione di archiviazione?

Le risposte a queste domande possono cambiare con l'aumentare della complessità del progetto, ma partono tutte dallo stesso punto: decidere tra autogestione e gestione da parte di terzi.

Autogestito:

  • Pro: Controllo e costi.
  • (Significativo) Contro: siete responsabili di tutto.

I servizi gestiti spesso costano di più fin dall'inizio, mentre l'autogestione significa che potete usare la vostra distro Linux preferita, in qualche modo (o in parte) ottimizzata per le vostre esigenze. Questo può includere l'esecuzione di una versione forked di MySQL modificata dal vostro team. Potreste risparmiare sui costi di gestione del servizio, ma questo richiederà sempre più tempo per la manutenzione.

Database gestiti da terzi: 

Sì, potrebbe essere leggermente più costoso in dollari e centesimi, ma richiederà molto meno tempo per la manutenzione. Questa opzione e le soluzioni di archiviazione gestita dei dati sono la mia scelta di fatto per le mie applicazioni web. In questo esempio, stiamo già utilizzando Django per gestire le transazioni del database. Anche SQLAlchemy condivide questo punto di forza, poiché viene utilizzato con framework come FastAPI, Flask e molti altri. Se state già esternalizzando la scrittura di SQL a un pacchetto Python , perché non esternalizzare la gestione dei server SQL?

Ora, vista l'efficacia degli ORM di Python (come Django ORM e SQLAlchemy), vi consiglio di usare database gestiti e/o servizi di archiviazione dati gestiti ogni volta che è possibile:

  • Riduzione dei tempi di sviluppo
  • Riduzione dei tempi di gestione
  • Riduzione dei tempi di recupero
  • Riduzione delle interruzioni del servizio
  • Riduzione della complessità di distribuzione e sviluppo
  • Riduzione della complessità delle migrazioni di database (da altri servizi)
  • Riduzione delle attività ripetitive/inefficaci/inefficienti per gli sviluppatori SQL
  • Riduzione della complessità DevOps/Ops
  • Maggiore efficacia degli sviluppatori non SQL
  • Maggiore velocità di distribuzione e sviluppo
  • Maggiore affidabilità (spesso supportata da un accordo sul livello di servizio)
  • Maggiore sicurezza
  • Maggiore manutenibilità
  • Aumento dei backup e della ridondanza
  • Aumento marginale dei costi

Ho stilato l'elenco di cui sopra pensando di utilizzare un cluster di database MySQL gestito su Linode, nonché Linode Object Storage (per archiviare file come CSS, JavaScript, immagini, video, ecc.). In pratica, l'uso di questi servizi ci aiuta a mantenere l'attenzione sulla costruzione di un'eccellente applicazione web con Django, FastAPI, Flask, Node.js o altro. In altre parole, spostiamo l'attenzione sulla costruzione degli strumenti e del software che gli utenti desiderano. Insomma, dove c'è il vero valore per loro.

MySQL, PostgreSQL, Redis e Django

Per molto tempo, il database principale di Django è stato PostgreSQL. Questo è dovuto, in gran parte, al fatto che si poteva usare un JSONField solo all'interno di Postgres. Con Django 3.2+ e MySQL 5.7.8+, il JSONField è ora disponibile anche per MySQL.

Perché è importante?

L'archiviazione di dati non strutturati, come JSON, è spesso richiesta quando si gestiscono contenuti generati dagli utenti o si archiviano dati da altri servizi. API servizi. Vediamo come:

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

Ecco i dati che voglio memorizzare in relazione a questo 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"
}

Questi dati ci indicano quando potremmo aver bisogno di un JSONField . Possiamo memorizzare tutti i nomi degli animali domestici (utilizzando l'opzione name ) e mantenere il resto da memorizzare nel campo JSON. La cosa bella di JSONFields è che possono essere interrogati come qualsiasi altro campo standard di Django, anche con schemi diversi.

C'è un dibattito in corso tra gli sviluppatori di Django su quale database utilizzare: MySQL o PostgreSQL. Per molto tempo ho sempre optato per PostgreSQL, perché JSONField era disponibile solo su PostgreSQL, ma ora non è più così. Io dico di sceglierne uno e di mantenerlo finché non serve più alle vostre esigenze.

Ma a cosa serve Redis?

Redis è un datastore in-memory incredibilmente veloce e spesso usato come database temporaneo (di cui parleremo tra poco), servizio di caching e/o coda di messaggistica. Il motivo per cui lo chiamo database temporaneo è che è in-memory. La memoria è spesso più costosa dell'archiviazione su disco e quindi la memorizzazione a lungo termine dei dati in memoria spesso non è fattibile.

I miei casi d'uso principali per Redis e Django sono la cache e l'accodamento.

Caching: supponiamo che abbiate diverse pagine web che gli utenti visitano spesso. Vogliamo che i dati di queste pagine vengano mostrati agli utenti il più rapidamente possibile. Redis, come sistema di caching per Django, rende questo compito incredibilmente facile. I dati di queste pagine potrebbero essere resi da un database SQL, ma Redis può memorizzare i dati resi dalla cache. In altre parole, l'uso di Redis con SQL può spesso accelerare le risposte, riducendo al contempo la quantità di query ai database SQL.

Accodamento: Un altro caso d'uso molto diffuso di Redis è quello di scaricare le attività di lunga durata su un altro processo (spesso tramite un pacchetto Python chiamato Celery). Quando è necessario farlo, si può usare Redis come una coda di compiti che devono essere completati in un altro momento.

Ad esempio, se un utente ha bisogno di un report di tutte le transazioni degli ultimi cinque anni, il software potrebbe impiegare ore per generare il report. Ovviamente, nessuno ha intenzione di fissare una macchina per ore. Per questo motivo, la richiesta dell'utente viene scaricata su una coda Redis. Una volta in Redis, possiamo avere un processo worker in esecuzione (come usare Celery con Django) per generare effettivamente il report. Una volta terminato il report, indipendentemente dal tempo impiegato, l'utente riceverà una notifica. Questa notifica, come altre notifiche, potrebbe essere fatta anche attraverso una coda Redis accoppiata a un processo worker Celery/Django.

Tutto questo per dire che Redis e MySQL si completano molto bene. È possibile distribuire un server di database Redis autogestito tramite Linode Marketplace.

Object Storage

L'ultimo servizio gestito relativo ai dati che consiglio di utilizzare è Linode Object Storage. Object Storage è responsabile di tutti gli altri tipi di dati che è necessario memorizzare. Per esempio, non memorizzeremmo tutti i byte di un video in MySQL. Invece, memorizzeremmo i metadati relativi a quel video e memorizzeremmo il video in Object Storage.

Ecco alcuni esempi di utilizzo dell'archiviazione a oggetti:

  • Fogli di stile a cascata (CSS)
  • JavaScript (come React.js, Vue.js, Vanilla.js, ecc.)
  • Video
  • Images (grezzo e compresso)
  • CSV, XLSX
  • Database Backups
  • Livelli di immagine del contenitore Docker (se autogestito)
  • Iterazioni degli algoritmi di apprendimento automatico addestrati
  • Terraform File di Stato
  • PDF (grandi e piccoli)
  • Qualsiasi file persistente che deve essere scaricato spesso (o caricato)

Sintesi

Dopo aver letto questo articolo, spero che vi sentiate motivati a sfruttare la potenza dei servizi gestiti per i vostri progetti di applicazioni web. Django è un'ottima soluzione per costruire applicazioni web su database SQL, ma non è certo l'unica. Se volete approfondire gli aspetti interni di SQL e dei server SQL, credo che valga la pena di vedere quante applicazioni di successo sfruttano Django per gestire la maggior parte di ciò che Django può fare.

Ecco alcuni (o molti) punti salienti che rendono Django con Managed MySQL su Linode fantastico:

  • Django fa il lavoro pesante con l'SQL (così come strumenti come SQLAlchemy per Flask/FastAPI).
  • Django consente anche comandi SQL grezzi (anche strumenti come SQLAlchemy).
  • Django aiuta i principianti a imparare i comandi SQL
  • Django ha un supporto integrato per MySQL e PostgreSQL (oltre a un client python specifico per il db).
  • Aumenta la velocità delle implementazioni di produzione
  • Maggiore affidabilità e ripristinabilità
  • Consente agli ambienti di sviluppo e di produzione di corrispondere quasi esattamente alla tecnologia del database.
  • Rende Django basato su container più semplice e affidabile
  • Sblocca la scalabilità da un'implementazione a singolo nodo a una multi-nodo o addirittura alla transizione completa a Kubernetes.
  • Per i nuovi sviluppatori di Django/Python è più facile utilizzare sistemi di livello produttivo
  • La condivisione di database tra più applicazioni basate su python è più facile e sicura (ad esempio, un'applicazione FastAPI che legge/scrive da/su un database MySQL basato su Django).
  • JSONField di Django ora supportato da MySQL (in precedenza solo da PostgreSQL)
  • Facile da testare (durante il CI/CD o in ambienti di sviluppo locali)
  • Scala per soddisfare le richieste di Django
  • Supporto per più database in un singolo progetto Django, come ad esempio: utilizzo di MySQL come database primario in lettura/scrittura e di un database di replica in lettura di MySQL per le query più comuni.
  • Controlli di accesso rigorosi (IP privati di Linode, sviluppo locale)
  • Richiede un certificato SSL per la connessione (aggiunge complessità alle implementazioni ma aumenta anche la sicurezza)
  • Consente la connessione privata (nella stessa regione; riduce i costi di connessione)

Se siete interessati a un approccio moderno alla distribuzione di applicazioni Django su Linode insieme a un database MySQL gestito, azioni GitHub per CI/CD, Terraform e Ansible, fate il salto per accedere a tonnellate di contenuti didattici gratuiti passo-passo:

Per aiutarvi a iniziare, su GitHub Coding for Entrepreneurs c'è un repository di codice che accompagna ogni passo della serie. Buona fortuna e fatemi sapere come vanno le cose via Twitter @JustinMitchel.


Commenti

Lascia una risposta

Il vostro indirizzo e-mail non sarà pubblicato. I campi obbligatori sono contrassegnati da *