跳到主要内容
博客数据库Django 和 SQL:扩展数据库的动态二重奏

Django和SQL。缩放数据库的动态组合

使用 Django 和 SQL 扩展数据库--博客头

扩展和优化数据库以满足应用程序的需求是一项巨大的挑战。如果您还没有阅读我最近关于Django 如何为Python 和 SQL数据库应用程序完成繁重工作的博客,我强烈建议您去看看。简而言之,SQL 针对 SQL 数据库进行了优化,而Python 并非如此,Django是一个很好的中介,可以帮助您构建更有效的应用程序,同时减少使用这两种语言时的摩擦、复杂性和代码。

因此,虽然 Django 承担了创建数据库应用程序的重任,但您仍需负责数据库的日常管理和监控。其中一些管理任务可以通过使用Linode Managed Databases 等服务委托给云提供商来完成,但您可能会在扩展过程中发现新的障碍,例如:

  • 数据库迁移。通过对数据库方案进行有控制的更改,将现有数据库转换为新的理想状态。
  • 多数据库部署。为了优化性能,开发人员可以在设计应用程序时,将不同的数据库用于不同的功能。例如,一个主读写数据库和一个用于普通查询的读取副本数据库。

如果您的其中一个数据库使用 SQL,您可以使用 Django 减少摩擦,在处理大量数据时让您的生活更轻松。

这两个关键数据库管理概念的介绍与《了解数据库》电子书和我的新教育视频系列中有关构建可投入生产的 Django 应用程序的分步说明相匹配。无论哪种学习途径,都将帮助您让 Django 为您完成 SQL 的繁重工作。

数据库迁移
刚开始工作时,为任何给定的列确定正确的数据类型都可能有点棘手,尤其是您的数据需求不可避免地会随着时间的推移而变化。如果您希望标题字段只有 80 个字符长怎么办?如果您需要添加一个时间戳字段,以便准确跟踪项目何时添加到数据库中?

在创建表格后对其进行更改可能会非常麻烦,原因有以下几点:

  • 如何处理已有的价值观?
  • 如果原有行缺少新列/字段的数据怎么办?
  • 如果删除一列/字段怎么办?数据会发生什么变化?
  • 如果添加一个以前不存在的关系(即外键)怎么办?

对于 Django 开发人员来说,幸运的是我们有一种叫做 makemigrationsmigrate.

让我们来看看它是如何运作的。

下面是我们的 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,
    )

让我们添加字段:

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

通过该字段,我们可以跟踪对模型进行更改的最后一位用户。 

让我们更新模型:

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
    )

现在,我们将文件保存为 BlogArticle 类是在 (models.py),我们如何让数据库知道发生了这一更改?

有两种方法:

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

让我们来讨论一下这两条命令的作用:

python manage.py makemigrations

python manage.py makemigrations 会发现以下变化 一应俱全 models.py 文件,并查找更改。如果发现有更改,就会创建一个新的python 文件,并使用 拟议变动 我们的 SQL 数据库 需要 要做的。拟议的修改如下

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

当然,这只是另一个Python 文件。该文件让我们(开发人员)知道数据库中应该发生什么。它是用Python 而不是 SQL 编写的,以保持一致性并利用 Django ORM 的内置功能。

但为什么这是一份应该发生的文件呢?这有几个原因:

  • 如果我们需要在事情发生之前回顾应该发生的事情,我们可以在这里抓住它。
  • makemigrations 命令不会 查看 数据库,以查看这一更改是否 甚至会发生。
  • 数据库可能已经根据这些要求进行了更改(取决于与管理数据库的人/物有关的许多因素)。
  • 如果我们需要在更改生产数据库之前运行测试,那么现在就是一个绝佳的时机。

假设这一更改有效(就我们所知),我们就可以提交更改:

python manage.py 迁移

python manage.py migrate 将尝试为我们更改数据库,包括所有字段、列、表、外键等,Django 将为我们完成这些工作,以确保数据库按照我们的预期方式更新。

需要注意的是,Django可能会因为多种原因而无法进行这些更改。对于新的 Django 开发人员来说,这几乎总是由于添加和删除字段和列以及未能正确运行迁移造成的。

只要方法得当、 python manage.py migrate 确保了一个稳定的系统,能将我们的Python 代码与我们的 SQL 表相匹配,从而让我们享受到 Django 和 SQL 数据库所带来的所有乐趣。

这如何让我们更加灵活?

Python 结构化查询语言有广泛的应用,而 SQL 则没有。结构化查询语言的名字里就写着它的局限性。谁在SQL 制作皮克斯动画?

好了,说了这么多,我想说的是,Python 的简单性实际上有助于开发人员在不知不觉中采用 SQL 的强大功能。

为什么托管数据库和 Django 有意义?

在创建网络应用程序(包括 Django)时,您需要决定一些事情:

  • 我们需要哪种数据存储解决方案?MySQL、Postgres、MongoDB、Redis、Object Storage 等
  • 我们将如何运行/整合数据存储解决方案?
  • 我们如何从中断或停机中恢复?
  • 我们将如何维护存储解决方案?
  • 我们如何确保存储解决方案的安全?
  • 我们如何备份存储解决方案?

这些问题的答案可能会随着项目复杂程度的增加而改变,但出发点都是一样的:在自我管理和第三方管理之间做出选择。

自我管理

  • 优点控制和成本
  • (康:你要对一切负责。

托管服务通常从一开始就需要花费更多的钱,而自我管理则意味着你可以使用自己喜欢的 Linux 发行版,这些发行版在某种程度上(或某种程度上)已经针对你的需求进行了优化。这可能包括运行你的团队修改过的 MySQL 分支版本。您可能会在运行服务上节省开支,但这总是需要花费更多的时间来维护。

第三方管理的数据库: 

是的,按美元和美分计算,它可能会稍贵一些,但维护所需的时间会大大减少。对于我的网络应用程序来说,这种选择和托管数据存储解决方案是我的实际选择。在这个例子中,我们已经使用 Django 来管理数据库事务。SQLAlchemy 也具有这种优势,因为它可以与 FastAPI、Flask 等框架一起使用。如果您已经将 SQL 编写工作外包给Python 软件包,为什么不外包 SQL 服务器的运行呢?

现在,鉴于Python ORM(如 Django ORM 和 SQLAlchemy)的有效性,我建议您尽可能使用托管数据库和/或托管数据存储服务:

  • 缩短开发时间
  • 减少管理时间
  • 缩短恢复时间
  • 减少服务中断
  • 降低部署和开发的复杂性
  • 降低数据库迁移(从其他服务迁移)的复杂性
  • 减少 SQL 开发人员的重复/无效/低效活动
  • 降低 DevOps/Ops 复杂性
  • 提高非数据库开发人员的效率
  • 提高部署和开发速度
  • 提高可靠性(通常以服务水平协议为后盾)
  • 加强安全
  • 提高可维护性
  • 增加备份和冗余
  • 边际成本增加

我在列出上述清单时,考虑到了在 Linode 上使用托管 MySQL 数据库集群以及 Linode Object Storage(用于存储 CSS、JavaScript、图片、视频等文件)。实际上,使用这些服务可以帮助我们继续专注于使用 Django、FastAPI、Flask、Node.js 或其他工具构建优秀的网络应用程序。换句话说,我们将重点转移到构建用户真正需要的工具和软件上。要知道,这才是对他们真正有价值的地方。

MySQL、PostgreSQL、Redis 和 Django

长期以来,Django 的主要数据库是 PostgreSQL。我认为这在很大程度上是因为您只能在 PostgreSQL 中使用 JSONField。在 Django 3.2+ 和 MySQL 5.7.8+ 版本中,JSONField 现在也适用于 MySQL。

为什么这很重要?

在处理用户生成的内容或存储来自其他 API 服务的数据时,经常需要存储 JSON 等非结构化数据。让我们来看看如何实现:

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

下面是我要存储的与 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"
}

这些数据告诉我们什么时候需要 JSONField .我们可以存储所有宠物的名字(使用 name 键),其余部分存储在 JSONField 中。关于 JSONFields 因为即使有这些不同的模式,它们也可以像 Django 的其他标准字段一样被查询。

Django 开发人员一直在争论使用哪种数据库:MySQL还是PostgreSQL。长期以来,我一直选择PostgreSQL,因为 JSONField 只能在PostgreSQL上使用,但现在情况不再是这样了。我的意见是,选择一个并坚持使用,直到它不再满足你的需求。

但我们用 Redis 来做什么呢?

Redis 是一种内存数据存储,速度快得令人难以置信,通常用作临时数据库(稍后详述)、缓存服务和/或消息队列。我之所以称它为临时数据库,是因为它是内存数据库。内存通常比磁盘存储昂贵,因此在内存中长期存储数据通常是不可行的。

Redis 和 Django 的主要用途是缓存和队列。

缓存:假设您有几个用户经常访问的网页。您希望这些网页中的数据能尽快显示给用户。作为 Django 的缓存系统,Redis 可以轻松实现这一目标。这些页面中的数据可能是通过 SQL 数据库渲染的,但 Redis 可以从缓存中存储渲染的数据。换句话说,将 Redis 与 SQL 结合使用通常可以加快响应速度,同时减少对 SQL 数据库的查询量。

排队:Redis 的另一个流行用例是将长期运行的任务卸载给另一个进程(通常通过名为 Celery 的Python 软件包)。需要这样做时,可以将 Redis 用作应在其他时间完成的任务队列。

例如,如果用户需要一份过去五年所有交易的报告,软件可能需要几个小时才能真正生成该报告。显然,没有人会盯着一台机器看上几个小时。因此,我们会将用户的请求卸载到 Redis 队列中。一旦进入 Redis,我们就可以运行一个工作进程(如在 Django 中使用 Celery)来实际生成报告。一旦报告完成,无论耗时多久,用户都会收到通知。这种通知和其他通知一样,也可以通过 Redis 队列和 Celery/Django 工作进程来完成。

综上所述,RedisMySQL实际上可以很好地互补。你可以通过LinodeMarketplace 部署一个自我管理的 Redis 数据库服务器。

Object Storage

我推荐使用的最后一种与数据相关的托管服务是 LinodeObject Storage 。Object Storage 负责您可能需要存储的所有其他类型的数据。例如,我们不会在 MySQL 中存储视频中的所有字节。相反,我们会存储与该视频相关的元数据,并将视频存储在Object Storage 中。

以下是对象存储的几种用途:

  • 层叠样式表(CSS)
  • JavaScript(如 React.js、Vue.js、Vanilla.js 等)
  • 视频
  • Images (原始数据和压缩数据)
  • CSVs, XLSX
  • 数据库Backups
  • Docker 容器映像层(如果自行管理)
  • 训练机器学习算法的迭代
  • Terraform 州档案
  • PDF 文件(包括大文件和小文件)
  • 任何需要经常下载(或上传)的持久性文件

摘要

读完这篇文章后,我希望您会有动力在您的网络应用项目中利用托管服务的力量。Django 是在 SQL 数据库之上构建网络应用的优秀解决方案,但它肯定不是唯一的解决方案。如果您想深入了解 SQL 和 SQL 服务器的内部结构,我认为这是一项值得一试的工作,看看有多少成功的应用程序利用 Django 来处理 Django 能做的大部分工作。

以下是一些(或许多)亮点,它们使在 Linode 上使用托管 MySQL 的 Django 非常出色:

  • Django 可为您提供繁重的 SQL 工作(Flask/FastAPI 的 SQLAlchemy 等工具也是如此)。
  • Django 也支持原始 SQL 命令(同样,SQLAlchemy 等工具也支持原始 SQL 命令)。
  • Django 帮助初学者学习 SQL 命令
  • Django 内置了对 MySQL 和 PostgreSQL 的支持(此外还有一个特定于数据库的python 客户端)。
  • 提高生产部署速度
  • 提高可靠性和可恢复性
  • 使开发和生产环境几乎完全匹配数据库技术
  • 让基于容器的 Django 更简单、更可靠
  • 实现从单节点部署到多节点的扩展,甚至完全过渡到 Kubernetes
  • 新 Django/Python 开发人员更容易使用生产级系统
  • 在多个基于python 的应用程序之间共享数据库更容易,也更安全(例如,FastAPI 应用程序从/到基于 Django 的 MySQL 数据库读/写)。
  • Django 的 JSONField 现在支持使用 MySQL(以前仅支持 PostgreSQL)
  • 易于测试(在 CI/CD 或本地开发环境中进行测试)
  • 扩展以满足 Django 的需求
  • 支持在单个 Django 项目中使用多个数据库,例如:使用 MySQL 作为主读写数据库,并使用 MySQL 读取副本数据库进行常用查询。
  • 严格的访问控制(Linode 专用 IP、本地开发)
  • 连接需要 SSL 证书(增加了部署的复杂性,但也提高了安全性)
  • 实现专用连接(在同一地区;降低连接成本)

如果您对在 Linode 上部署 Django 应用程序的现代方法以及可管理的 MySQL 数据库、用于 CI/CD 的 GitHub 操作、Terraform 和Ansible 感兴趣,请访问大量免费的分步教学内容:

为了帮助你开始,Coding for Entrepreneurs GitHub有一个代码库,与该系列的每个步骤相配套。祝你好运,一定要通过Twitter@JustinMitchel让我知道事情进展如何。


注释

留下回复

您的电子邮件地址将不会被公布。 必须填写的字段被标记为*