Django生产上的数据库迁移

2024-06-16 10:08:37 发布

您现在位置:Python中文网/ 问答频道 /正文

对于在非平凡的生产环境中拥有django应用程序的人,您如何处理数据库迁移?我知道有south,但如果涉及到任何实质性的内容,这似乎会错过很多。

另外两个选项(我可以想到或已经使用过)是在测试数据库上进行更改,然后(与应用程序脱机)导入该sql导出。或者,可能是一个风险更大的选项,实时对生产数据库进行必要的更改,如果有任何问题,则恢复到备份。

您通常如何处理数据库迁移和模式更改?


Tags: django数据库应用程序内容sql环境选项模式
3条回答

我使用South作为一个生产服务器,代码库约为40K行,到目前为止我们还没有遇到任何问题。对于我们的一些模型,我们也经历了一些主要的重构,我们没有遇到任何问题。

我们还有一件事就是对模型进行版本控制,这有助于我们恢复对软件方面的模型所做的任何更改,而South则更多地用于实际数据。我们用Django Reversion

我有时对这个问题采取了一种非传统的方法(阅读其他答案,也许不是那么非传统)。我从来没有和django一起试过,所以我只是做了一些实验。

简而言之,我让代码捕获旧模式导致的异常并应用适当的模式升级。我不认为这是一个公认的答案——这只在某些情况下是合适的(有些人可能永远不会争辩)。但我觉得它有丑小鸭的优雅。

当然,我有一个测试环境,可以在任何时候重置回生产状态。使用这个测试环境,我更新了我的模式并编写了相应的代码——和往常一样。

然后我恢复模式更改并再次测试新代码。我捕获结果错误,执行架构升级,然后重新尝试错误查询。

必须编写升级功能,使其“无害”,这样,如果多次调用(在投入生产时可能会发生这种情况),它只会执行一次操作。

实际的python代码-我把它放在settings.py的末尾来测试这个概念,但是您可能希望将它保存在一个单独的模块中:

from django.db.models.sql.compiler import SQLCompiler
from MySQLdb import OperationalError

orig_exec = SQLCompiler.execute_sql
def new_exec(self, *args, **kw):
    try:
        return orig_exec(self, *args, **kw)
    except OperationalError, e:
        if e[0] != 1054: # unknown column
            raise
        upgradeSchema(self.connection)
        return orig_exec(self, *args, **kw)
SQLCompiler.execute_sql = new_exec

def upgradeSchema(conn):
    cursor = conn.cursor()
    try:
        cursor.execute("alter table users add phone varchar(255)")
    except OperationalError, e:
        if e[0] != 1060: # duplicate column name
            raise

一旦生产环境是最新的,就可以从代码库中删除此自升级代码。但即使你不这样做,代码也不会做任何重要的不必要的工作。

您需要根据您的数据库引擎和模式更改来调整异常类(MySQLdb.operationaleerror,在我的情况下)和数字(1054“未知列”/1060“重复列”),但这应该很容易。

您可能需要添加一些额外的检查,以确保正在执行的sql实际上是错误的,因为有问题的模式更改而不是其他问题,但即使没有,这也会重新引发不相关的异常。唯一的缺点是,在这种情况下,您将在引发异常之前尝试升级和错误查询两次。

关于python,我最喜欢的事情之一是能够在这样的运行时轻松地重写系统方法。它提供了很大的灵活性。

我认为这个问题有两个部分。

首先是管理数据库模式及其更改。我们使用South实现这一点,将工作模型和迁移文件保存在我们的SCM存储库中。为了安全(或偏执),我们在运行任何迁移之前(如果真的很害怕,在运行任何迁移之后)转储数据库。到目前为止,南方已经满足了我们的所有要求。

第二个是部署模式更改,这不仅仅是运行South生成的迁移文件。根据我的经验,对数据库的更改通常需要更改已部署的代码。如果您甚至有一个小的web服务器场,那么保持部署的代码与数据库架构的当前版本同步可能并不容易——如果考虑不同的缓存层并对已经处于活动状态的站点用户产生影响,情况会变得更糟。不同的网站处理这个问题的方式不同,我不认为有一刀切的答案。


解决这个问题的第二部分不一定是直截了当的。我不相信有一刀切的方法,也没有足够的信息,你的网站和环境,以建议一个解决方案,将最适合你的情况。不过,我认为在大多数情况下,有几个考虑因素可以帮助指导部署。

在某些情况下,使整个站点(web服务器和数据库)脱机是一个选项。这无疑是管理更新的最直接的方法。但是,频繁的停机(即使是在计划的情况下)可能是一种快速开展业务的好方法,使部署即使是很小的代码更改也很烦人,而且如果您有一个大型数据集和/或复杂的迁移,则可能需要花费数小时。也就是说,对于我帮助管理的站点(这些站点都是内部的,通常只在工作日的工作时间使用),这种方法会产生奇迹。

如果对主数据库的副本进行更改,请小心。这里的主要问题是您的站点仍然处于活动状态,并且可能接受对数据库的写操作。当您忙于迁移克隆以供以后使用时,写入主数据库的数据会发生什么情况?你的站点要么一直关闭,要么暂时处于只读状态,否则你会丢失它们。

如果您的更改是向后兼容的,并且您有一个web服务器场,有时您可以通过更新live production数据库服务器(我认为在大多数情况下这是不可避免的),然后通过将服务器场中的节点从负载平衡器中取出一段时间来增量地更新这些节点。这可以正常工作-但是这里的主要问题是,如果已经更新的节点发送了一个旧节点不支持的url请求,您将失败,因为您无法在负载平衡器级别管理该请求。

我听说有几种其他的方法很有效。

第一种方法是将所有代码更改包装到功能锁中,然后在运行时通过一些站点范围的配置选项对其进行配置。这实际上意味着您可以在关闭所有更改的地方发布代码,然后在对服务器进行所有必要的更新之后,更改配置选项以启用该功能。但这会产生很重的代码。。。

第二个是让代码管理迁移。我听说过一些站点,在这些站点中,对代码的更改是以这样一种方式编写的,即在运行时处理迁移。它能够检测正在使用的模式的版本,以及它返回的数据的格式—如果数据来自旧模式,它会就地执行迁移;如果数据已经来自新模式,它不会执行任何操作。从自然的站点使用情况来看,大部分数据将由使用站点的人迁移,其余的则可以随时使用迁移脚本。

但我认为在这一点上谷歌成为你的朋友,因为正如我所说,解决方案是非常有竞争力的我担心这个答案会变得毫无意义。。。搜索“零停机时间部署”之类的内容,您将得到诸如this之类的结果,其中包含很多想法。。。

相关问题 更多 >