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

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

用Django和SQL扩展数据库-BlogHeader

扩展和优化数据库以满足你的应用程序的需求可能是一个重大挑战。如果你还没有读过我最近关于Django如何为Python 和SQL数据库应用程序完成重任的博客,我强烈建议你去看看。但是TL;DR版本是,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 migrate

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、对象存储,等等。
  • 我们将如何运行/与数据存储解决方案整合?
  • 我们将如何从中断或停机中恢复?
  • 我们将如何维护存储解决方案?
  • 我们将如何保障我们的存储解决方案?
  • 我们将如何备份我们的存储解决方案?

这些问题的答案可能会随着你的项目复杂性的增加而改变,但它们都是从同一个地方开始的:在自我管理和第三方管理之间做出决定。

自我管理

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

管理服务往往从一开始就花费更多的钱,而自我管理意味着你可以使用你喜欢的Linux发行版,并以某种方式(或某种程度)为你的需要进行优化。这可以包括运行你的团队修改过的MySQL的分叉版本。你可能在运行你的服务上节省美元,但这总是需要更多的时间来维护。

第三方管理的数据库: 

是的,以美元和美分计算,它可能略微贵一些,但维护的时间会大大减少。这个选项和管理数据存储解决方案是我的网络应用的事实选择。在这个例子中,我们已经在利用Django来管理数据库事务。SQLAlchemy也有这个优势,因为它与FastAPI、Flask等框架一起使用。如果你已经把你的SQL编写工作外包给了一个Python 包,为什么不把你的SQL服务器运行工作外包出去呢?

现在,鉴于Python ORM(如Django ORM和SQLAlchemy)的有效性,我建议你尽可能地使用托管数据库和/或托管数据存储服务,以下是你这样做的好处:

  • 缩短了开发时间
  • 减少了管理时间
  • 缩短了恢复时间
  • 减少了服务中断
  • 减少了部署和开发的复杂性
  • 降低数据库迁移的复杂性(从其他服务)。
  • 减少了SQL开发人员的重复/无效/低效的活动
  • 降低DevOps/Ops的复杂性
  • 提高非SQL开发人员的效率
  • 提高部署和开发速度
  • 增加可靠性(通常由服务水平协议支持)。
  • 提高安全性
  • 提高可维护性
  • 在备份和冗余方面有所增加
  • 费用的边际增加

我在制定上述清单时,考虑到了在Linode上使用管理型MySQL数据库集群以及Linode对象存储(用于存储CSS、JavaScript、图片、视频等文件)的思路。实际上,使用这些服务可以帮助我们保持专注于用Django、FastAPI、Flask、Node.js 或其他什么东西构建一个优秀的网络应用。换句话说,我们将重点转移到构建你的用户真正需要的工具和软件上。你知道,对他们来说,真正的价值在哪里。

MySQL、PostgreSQL、Redis和Django

在很长一段时间里,Django的主要数据库是PostgreSQL。我认为,这在很大程度上是由于你只能在Postgres中使用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的主要使用情况是缓存和排队。

缓存:比方说,你有几个用户经常访问的网页。你希望这些页面中的数据能尽快地显示给用户。Redis作为Django的一个缓存系统,让这一切变得异常简单。这些页面中的数据可能是从一个SQL数据库中渲染出来的,但Redis可以从缓存中存储这些渲染出来的数据。换句话说,将Redis与SQL一起使用往往可以加快你的响应速度,同时减少对SQL数据库的查询量。

排队:Redis的另一个流行的用例是将长期运行的任务卸载到另一个进程(通常通过一个叫做Celery的Python 包)。当你需要这样做时,你可以使用Redis作为一个队列,将应该在另一个时间完成的任务放在队列中。

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

这都是说,RedisMySQL实际上是非常互补的。你可以通过Linode Marketplace部署一个自我管理的Redis数据库服务器。

对象存储

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

下面是一些你会使用对象存储的事情:

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

摘要

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

这里有几个(或许多)亮点,使Linode上的Django与管理的MySQL变得非常棒:

  • Django为你做繁重的SQL工作(Flask/FastAPI的SQLAlchemy等工具也是如此)。
  • Django也能实现原始的SQL命令(同样,像SQLAlchemy这样的工具也是如此)。
  • Django帮助初学者学习SQL命令
  • Django内置了对MySQL和PostgreSQL的支持(除了一个针对db的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让我知道事情进展如何。


注释

留下回复

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