将数据库更改从开发环境迁移到生产环境

15

在将新功能推向生产环境时,最大的风险可能在于新代码所需的数据库修改。在Rails中,我相信他们有“迁移”,可以通过编程方式对开发主机进行更改,然后随着使用修订后模式的代码一起在生产环境中进行相同的更改。如果需要的话,还可以以同步的方式回滚两者。

是否有人遇到过类似的PHP / MySQL工具集?很想听听它,或任何程序化或流程解决方案,以帮助降低这种风险......

10个回答

6

我不信任程序化迁移。如果仅仅是简单的更改,例如添加一个可空列,则直接将其添加到生产服务器上。如果更复杂或需要数据更改,则编写一对SQL迁移文件并针对副本数据库进行测试。

在使用迁移时,一定要测试回滚迁移。这是您的紧急“出问题了”按钮。


5
我从未遇到过一款能够胜任该工作的工具。相反,我使用单独的文件,按顺序编号,以便知道运行它们的顺序:本质上是手动版本的Rails迁移,但没有回滚功能。
以下是我所说的内容示例:
000-clean.sql         # wipe out everything in the DB
001-schema.sql        # create the initial DB objects
002-fk.sql            # apply referential integrity (simple if kept separate)
003-reference-pop.sql # populate reference data
004-release-pop.sql   # populate release data
005-add-new-table.sql # modification
006-rename-table.sql  # another modification...

我从未遇到过在这方面遇到任何问题,但它并不是非常优雅。你需要跟踪哪些脚本需要运行以进行给定的更新(更智能的编号方案可能会有所帮助)。它也可以与源代码控制很好地配合使用。
处理代理键值(来自自动编号列)可能很麻烦,因为生产数据库与开发数据库可能具有不同的值。因此,我尽量避免在我的任何修改脚本中引用字面上的代理键值。

3

我使用的解决方案(最初由我的一个朋友开发)是yukondude的另一个补充。

  1. 在版本控制下创建一个模式目录,然后对于每个您所做的数据库更改,请保留一个.sql文件,其中包含要执行的SQL以及更新db_schema表的sql查询。
  2. 创建一个名为“db_schema”的数据库表,其中包含一个名为version的整数列。
  3. 在模式目录中创建两个shell脚本,“current”和“update”。执行current会告诉您当前连接的数据库所处的db模式版本。运行update按顺序执行大于db_schema表中版本号的每个.sql文件,直到您达到模式目录中编号最大的文件。

模式目录中的文件:

0-init.sql 
1-add-name-to-user.sql
2-add-bio.sql

典型文件长这样,注意每个 .sql 文件结尾处的 db_schema 更新:

BEGIN;
-- comment about what this is doing
ALTER TABLE user ADD COLUMN bio text NULL;

UPDATE db_schema SET version = 2;
COMMIT;

“当前”脚本(针对psql):

#!/bin/sh

VERSION=`psql -q -t <<EOF
\set ON_ERROR_STOP on
SELECT version FROM db_schema;
EOF
`

[ $? -eq 0 ] && {
    echo $VERSION
    exit 0
}

echo 0

更新脚本(也称为psql):
#!/bin/sh

CURRENT=`./current`
LATEST=`ls -vr *.sql |egrep -o "^[0-9]+" |head -n1`

echo current is $CURRENT
echo latest is $LATEST

[[ $CURRENT -gt $LATEST ]] && {
    echo That seems to be a problem.
    exit 1
}

[[ $CURRENT -eq $LATEST ]] && exit 0

#SCRIPT_SET="-q"
SCRIPT_SET=""

for (( I = $CURRENT + 1 ; I <= $LATEST ; I++ )); do
    SCRIPT=`ls $I-*.sql |head -n1`
    echo "Adding '$SCRIPT'"
    SCRIPT_SET="$SCRIPT_SET $SCRIPT"
done

echo "Applying updates..."
echo $SCRIPT_SET
for S in $SCRIPT_SET ; do
    psql -v ON_ERROR_STOP=TRUE -f $S || {
    echo FAIL
    exit 1
    }
done
echo OK

我的0-init.sql文件包含完整的初始模式结构以及初始的“UPDATE db_schema SET version = 0;”语句。修改这些脚本以适用于MySQL应该不难。在我的情况下,我也有。

export PGDATABASE="dbname"
export PGUSER="mike"
在我的 .bashrc 文件中,它会提示每个被执行的文件需要输入密码。

3

我以前用过这个工具,它完美地发挥了作用。

http://www.mysqldiff.org/

它的输入可以是数据库连接或SQL文件,并将其与相同的(另一个数据库连接或另一个SQL文件)进行比较。它可以输出SQL以进行更改,也可以为您进行更改。


3

@[yukondude]

我自己使用Perl,并且以类似Rails风格的迁移为例,半自动地进行了相同的操作。

我的做法是创建一个名为“version”的单个表,其中包含一个名为“version”的列,包含一个数字的单行,该数字是当前模式版本。然后编写一个脚本来读取该数字,在某个目录中查找并应用所有编号的迁移,以从这里到达那里(然后更新该数字),这样做相当容易。

在我的开发/阶段环境中,我经常(通过另一个脚本)将生产数据导入到暂存数据库中,并运行迁移脚本。如果您在上线之前这样做,您就可以相当确定迁移将起作用。显然,在暂存环境中进行广泛测试。

我在一个版本控制标记下标记新代码和所需的迁移。要部署到阶段或实时环境,您只需将所有内容更新到此标记并快速运行迁移脚本即可。(如果存在真正疯狂的模式更改,则可能需要安排短暂的停机时间。)


2

基本上就是Lot105所描述的。

每次迁移都需要有一个应用和回滚脚本,还有一种控制脚本,它会检查哪些迁移需要被应用并按适当的顺序应用它们。

然后每个开发人员都使用这个方案来保持他们的数据库同步,在应用到生产环境时,相关变更也会被应用。如果需要,可以使用回滚脚本撤销更改。

有些改变不能用像sqldiff这样的工具生成的简单ALTER脚本来完成;有些改变不需要模式更改而需要对现有数据进行编程更改。所以你无法进行概括,这就是为什么需要一个由人编辑的脚本。


2
Symfony有一个名为sfMigrationsLight的插件,用于处理基本迁移。CakePHP也有迁移工具。
由于某些原因,大多数PHP框架和ORM并没有将迁移支持作为重点考虑。

2
我使用 SQLyog 工具来复制结构,而且我总是,让我再强调一遍,总是先备份。

0
过去我使用过LiquiBase,这是一个基于Java的工具,您可以将迁移配置为XML文件,并使用它生成必要的SQL。
今天我会使用Doctrine 2库,它具有类似于Ruby的迁移功能Symfony 2框架也有一种很好的处理模式更改的方法 - 它的命令行工具可以分析现有的模式并生成SQL以匹配数据库到已更改的模式定义。

-1

我一直喜欢将我的开发站点指向与生产站点相同的数据库。这听起来可能有风险,但实际上它解决了许多问题。如果您在同一服务器上有两个站点指向相同的数据库,则可以实时准确地查看用户在上线时将看到什么。

您只需要一个数据库,只要您制定从不删除表中的列的策略,就知道您的新代码将与您正在使用的数据库匹配。

迁移时也会少很多麻烦。您只需要移动PHP脚本,它们已经使用相同的数据库进行了测试。

我还倾向于为任何目标用户上传的文件夹创建符号链接。这意味着没有混淆哪些用户文件已更新。

另一个副作用是将一小组“测试人员”移植到日常使用该网站的选项。这可能会导致大量反馈,您可以在公开发布之前实施。

这种方法可能并不适用于所有情况,但我已经开始将所有更新移动到这种模式。它使开发和发布更加顺畅。


你如何处理列的重命名?快速重命名可能会破坏你的生产环境,对吧? - Kevin Pang
@Kevin Pang - 这是真的,但我不经常重命名我的列。我发现更好的方法是保留列名不变。这使我仔细考虑列名,但我也发现这是很少发生的情况。如果必须重命名,两个系统中的查找替换并不比一个系统花费更多时间。它仍然比数据库迁移造成的停机时间和烦恼要少。我知道这很不寻常,但对我来说效果非常好 :) - Paulo
有点晚来到这个帖子,但我必须为将来的开发人员说一句话。不要这样做。这违背了几乎所有安全实践,我很惊讶你没有在这样做时炸毁你的实时数据库。永远不要对实时数据库运行开发、未经测试的代码。永远不要这样做。将数据复制到开发阶段是如此简单。当然,反向操作可能会有挑战(这也是我在这里研究更好方法的原因)。但是这绝对不应该被建议给其他人。为了不给将来的开发人员带来麻烦,我建议删除这个解决方案。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接